信号量的实现与应用
知识储备
在消费者与生产者实现时,需要进行文件读写,实验指导书中提到,可通过借助标准C库函数来实现,或者直接通过对应的系统调用。
这里列出关于文件读写的三个函数说明
fseek()
C 库函数 int fseek(FILE *stream, long int offset, int whence) 设置流 stream 的文件位置为给定的偏移 offset,参数 offset 意味着从给定的 whence 位置查找的字节数。
声明如下
1 | int fseek(FILE *stream, long int offset, int whence) |
参数:
1 | stream -- 这是指向 FILE 对象的指针,该 FILE 对象标识了流。 |
常量 | 描述 |
---|---|
SEEK_SET | 文件的开头 |
SEEK_CUR | 文件指针的当前位置 |
SEEK_END | 文件的末尾 |
返回值:如果成功,则该函数返回0,否则返回非零值。
fread()
C 库函数 size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream) 从给定流 stream 读取数据到 ptr 所指向的数组中。
声明如下
1 | size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream) |
参数
1 | ptr -- 这是指向带有最小尺寸 size*nmemb 字节的内存块的指针。 |
返回值:成功读取的元素总数会以 size_t 对象返回,size_t 对象是一个整型数据类型。如果总数与 nmemb 参数不同,则可能发生了一个错误或者到达了文件末尾。
fwrite()
C 库函数 size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) 把 ptr 所指向的数组中的数据写入到给定流 stream 中。
声明如下
1 | size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) |
参数
1 | ptr -- 这是指向要被写入的元素数组的指针。 |
返回值:如果成功,该函数返回一个 size_t 对象,表示元素的总数,该对象是一个整型数据类型。如果该数字与 nmemb 参数不同,则会显示一个错误。
步骤
实现信号量
新建sem头文件sem.h
在linux-0.11/include/linux目录下新建sem.h,定义信号量的数据结构。
sem.c
在linux-0.11/kernel目录下,新建实现信号量函数的源代码文件sem.c。
修改unistd.h
在其中增加对于信号量的系统调用编号,类似于之前的操作
修改system_call.s
因为增加了四个sem系统调用函数,在system_call.s文件中找到nr_system_calls并将其值更改为76
修改sys.h
修改Makefile
准备文件
在oslab根目录下执行
1 | sudo ./mount-hdc |
使用cp命令将unistd.h复制到usr/include下,将sem.h复制到usr/include/linux下
编写程序
新建pc.c
进行挂载,再,利用cp命令将其移动到usr/root目录下
编译
运行linux-0.11之后,首先编译pc.c,使用命令
1 | gcc -o pc pc.c |
随后运行pc,使用命令
1 | ./pc > sem_output |
最终在虚拟环境内输入
1 | sync |
把修改的数据写入磁盘。
查看sem_output
首先挂载hdc,然后进入usr/root目录并在终端内执行
1 | sudo less sem_output |
命令,可看到下图结果:
问题回答
如果去掉所有与信号量有关的代码,编译运行程序之后可以发现输出的数字顺序完全混乱。
信号量不存在的情况下,进程之间无法同步或者协作,造成此种情况的有如下原因:
1 | - 一种情况是缓冲区满了,生产者还在写入数据,会造覆盖掉部分数据。 |