本次实验的基本内容是:
10: 0
10: 1
10: 2
10: 3
10: 4
11: 5
11: 6
12: 7
10: 8
12: 9
12: 10
12: 11
12: 12
……
11: 498
11: 499
其中 ID 的顺序会有较大变化,但冒号后的数字一定是从 0 开始递增加一的。
pc.c 中将会用到 sem_open() 、 sem_close() 、 sem_wait() 和 sem_post() 等信号量相关的系统调用,请查阅相关文档。
《UNIX环境高级编程》是一本关于 Unix/Linux 系统级编程的相当经典的教程。 电子版可在网站上下载,后续实验也用得到。如果你对 POSIX 编程感兴趣,建议买一本常备手边。
Linux 在 0.11 版还没有实现信号量,Linus 把这件富有挑战的工作留给了你。 如果能实现一套山寨版的完全符合 POSIX 规范的信号量,无疑是很有成就感的。但时间暂时不允许我们这么做,所以先弄一套缩水版的类 POSIX 信号量,它的函数原型和标准并不完全相同,而且只包含如下系统调用:
sem_t *sem_open(const char *name, unsigned int value);
int sem_wait(sem_t *sem);
int sem_post(sem_t *sem);
int sem_unlink(const char *name);
sem_t
是信号量类型,根据实现的需要自定义。
sem_open()
的功能是创建一个信号量,或打开一个已经存在的信号量。
name
是信号量的名字。不同的进程可以通过提供同样的name
而共享同一个信号量。如果该信号量不存在,就创建新的名为name
的信号量;如果存在,就打开已经存在的名为name
的信号量。value
是信号量的初值,仅当新建信号量时,此参数才有效,其余情况下它被忽略。当成功时,返回值是该信号量的唯一标识(比如,在内核的地址、ID
等),由另两个系统调用使用。如失败,返回值是NULL
。sem_wait()
就是信号量的P
原子操作。如果继续运行的条件不满足,则令调用进程等待在信号量sem
上。返回0
表示成功,返回-1
表示失败。
sem_post()
就是信号量的V
原子操作。如果有等待sem
的进程,它会唤醒其中的一个。返回0
表示成功,返回-1
表示失败。
sem_unlink()
的功能是删除名为name
的信号量。返回0
表示成功,返回-1
表示失败。
在kernel
目录下新建sem.c文件实现如上功能。然后将pc.c从Ubuntu移植到0.11下,测试自己实现的信号量。
Producer()
{
P(Mutex); //互斥信号量
// 生产一个产品item;
P(Empty); //空闲缓存资源
// 将item放到空闲缓存中;
V(Full); //产品资源
V(Mutex);
}
Consumer()
{
P(Mutex);
P(Full);
// 从缓存区取出一个赋值给item;
V(Empty);
// 消费产品item;
V(Mutex);
}
在实验过程中,我们实现的多个函数都要以系统调用的形式在Linux0.11中进行使用,因此,需要根据实验2的内容对makefile、unistd.h、system_call.s等文件进行修改,修改的过程不在此赘述,可以参考另一篇讲实验2的博客。
sem_t *sys_sem_open(const char *name,unsigned int value) //sem_t的定义会在后边给出,name是信号量的名称,value信号量对应的初值
{
char kernelname[100];
int isExist = 0;
int i = 0;
int name_cnt = 0;
while(get_fs_byte(name + name_cnt) != ‘\0‘)//进行信号量名称长度的读取
{
name_cnt++;
}
if(name_cnt > SEM_NAME_LEN)//信号量名称需小于最大长度
{
return NULL;
}
for(i = 0;i < name_cnt;i++)//将信号量的名称读入
{
kernelname[i] = get_fs_byte(name + i);
}
int name_len = strlen(kernelname);
int sem_name_len =0;
sem_t *p = NULL;
for(i = 0;i < cnt;i++)//判断是否当前信号已经存在
{
sem_name_len = strlen(semtable[i].name);
if(sem_name_len == name_len)
{
if( !strcmp(kernelname,semtable[i].name) )
{
isExist = 1;
break;
}
}
}
if(isExist == 1)//如果已经存在,那么返回已经存在的信号
{
p = (sem_t*)(&semtable[i]);
}
else//否则新建一个信号
{
i = 0;
for(i = 0;i < name_len;i++)
{
semtable[cnt].name[i] = kernelname[i];
}
semtable[cnt].value = value;
p = (sem_t*)(&semtable[cnt]);
cnt++;//并且将这个信号放入信号表中
}
return p;
}
这个函数实现的是打开信号量的操作。
int sys_sem_wait(sem_t *sem)
{
cli();//关中断
while(sem->value <= 0) //进程等待直到信号量的值大于0
{
sleep_on(&(sem->queue));
}
sem->value--;
sti();//开启中断
return 0;
}
这个函数实现了进程的等待过程。
int sys_sem_post(sem_t *sem)
{
cli();
sem->value++;
if((sem->value) <= 1)//唤醒在信号量上等待的进程
{
wake_up(&(sem->queue));
}
sti();
return 0;
}
这个函数用于唤醒对应的进程。
int sys_sem_unlink(const char *name)
{
char kernelname[100];
int isExist = 0;
int i = 0;
int name_cnt = 0;
while( get_fs_byte(name + name_cnt) != ‘\0‘)
{
name_cnt++;
}
if(name_cnt > SEM_NAME_LEN)
{
return NULL;
}
for(i=0;i<name_cnt;i++)
{
kernelname[i] = get_fs_byte(name + i);
}
int name_len = strlen(name);
int sem_name_len =0;
for(i = 0;i < cnt;i++)
{
sem_name_len = strlen(semtable[i].name);
if(sem_name_len == name_len)
{
if( !strcmp(kernelname,semtable[i].name))
{
isExist = 1;
break;
}
}
}
if(isExist == 1)
{
int tmp = 0;
for(tmp = i;tmp <= cnt;tmp++)
{
semtable[tmp] = semtable[tmp + 1];
}
cnt = cnt - 1;
return 0;
}
else
return -1;
}
这个函数用于删除名为name的信号量。
这个文件定义了sem_t这个数据类型。
#ifndef _SEM_H
#define _SEM_H
#include <linux/sched.h>
#define SEM_NAME_LEN 50
#define SEMTABLE_LEN 20
typedef struct sem_t{
char name[SEM_NAME_LEN];//信号量名称
unsigned int value;//初值
struct task_struct *queue;//等待信号量的进程指针
}sem_t;
extern sem_t semtable[SEMTABLE_LEN];
sem_t *sem_open(const char name, unsigned int value);
int sem_wait(sem_t *sem);
int sem_post(sem_t *sem);
int sem_unlink(const char *name);
#endif
#define __LIBRARY__
#include <unistd.h>
#include <stdio.h>
#include <linux/sem.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
_syscall2(sem_t*, sem_open, const char*, name, unsigned int, value)
_syscall1(int, sem_wait, sem_t *, sem)
_syscall1(int, sem_post, sem_t *, sem)
_syscall1(int, sem_unlink, const char *, name)
#define NUMBUFFER 550 /*打出数字的总数*/
#define NUMPRO 5 /*消费者进程个数*/
#define MAXSIZE 10 /*缓冲区大小*/
sem_t *empty, *full, *mutex;
int main()
{
int i, j, k;
FILE *fp = NULL;
int buf_out = 0;/*从缓冲区读取的位置*/
int buf_in = 0;/*写入缓冲区的位置*/
empty = (sem_t *)sem_open("EMPTY", MAXSIZE);/*打开对应的信号量*/
full = (sem_t *)sem_open("FULL", 0);
mutex = (sem_t *)sem_open("MUTEX", 1);
fp = fopen("/var/buffer.dat", "wb+"); /*存在文件中。*/
fseek(fp, MAXSIZE * sizeof(int), SEEK_SET);
fwrite(&buf_out, 1, sizeof(buf_out), fp);
fflush(fp);
if(!fork())
{
for(i = 0;i < NUMBUFFER;i ++)
{
sem_wait(empty);
sem_wait(mutex);
fseek(fp, buf_in * sizeof(int), SEEK_SET);
fwrite(&i, 1, sizeof(i), fp);
fflush(fp);
buf_in = (buf_in + 1) % MAXSIZE;
sem_post(mutex);
sem_post(full);
}
return 0;
}
for(i = 0;i < NUMPRO; i++)
{
if(!fork())
{
for(j = 0;j < NUMBUFFER / NUMPRO; j++)
{
int cost;
sem_wait(full);
sem_wait(mutex);
fflush(stdout);
fseek(fp, MAXSIZE * sizeof(int), SEEK_SET);
fread(&buf_out, sizeof(int), 1, fp);
fseek(fp, buf_out * sizeof(int), SEEK_SET);
fread(&cost, sizeof(int), 1, fp);
printf("%d:\t%d\n", getpid(), cost);
fflush(stdout);
buf_out = (buf_out + 1) % MAXSIZE;
fseek(fp, MAXSIZE * sizeof(int), SEEK_SET);
fwrite(&buf_out, 1, sizeof(buf_out),fp);
fflush(fp);
sem_post(mutex);
sem_post(empty);
}
return 0;
}
}
wait(NULL);
sem_unlink("EMPTY");
sem_unlink("FULL");
sem_unlink("MUTEX");
fclose(fp);
return 0;
}
....
17: 543
17: 544
17: 545
17: 546
17: 547
17: 548
17: 549
最终可以输出550个数字。
原文:https://www.cnblogs.com/winston8086/p/13258591.html