李治军老师操作系统实验5---信号量的实现和应用-信号量可以是-种资源的数量-进程看信号量实现同步即走走停停-信号临界区保护:共享数据的更新过程不能中断-此时切换去执行其他进程,切后进程看的是错信号量
2022/1/6 7:03:39
本文主要是介绍李治军老师操作系统实验5---信号量的实现和应用-信号量可以是-种资源的数量-进程看信号量实现同步即走走停停-信号临界区保护:共享数据的更新过程不能中断-此时切换去执行其他进程,切后进程看的是错信号量,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
改自原文链接:https://blog.csdn.net/weixin_43166958/article/details/104163221
一、重要资料资料
满时不生产,空时不消耗:
生产者消费者问题(英语:Producer-consumer problem),也称有限缓冲问题(英语:Bounded-buffer problem),是一个多线程同步问题的经典案例。该问题描述了两个共享固定大小缓冲区的线程——即所谓的“生产者”和“消费者”——在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。
消费者-------消耗资源中数据-------等数据
生产者-------产生数据存入资源----等资源
1️⃣信号量等于0--------------资源中无数据,无消费者无生产者
2️⃣信号量大于0--------------资源中有数据,消费者与生产者可能在流水作业,也希望是流水作业,拥堵肯定是不好的。拥塞是有生产者睡着等资源存储数据
3️⃣信号量小于0--------------资源中无数据,有消费者在睡着等数据
二、本次实验的基本内容是:
在 Ubuntu 下编写程序,用信号量解决生产者——消费者问题;
在 0.11 中实现信号量,用生产者—消费者程序检验之。
1
用信号量解决生产者—消费者问题
实验要求:pc.c程序需打开一个文件buffer.txt作为共享缓冲区,缓冲区同时最多只能保存 10 个数;创建一个生产者进程和N个消费者进程,其中生产者进程向缓冲区写入连续的整数,0,1,2,……,M,M>=500;消费者进程从缓冲区依次读取数字,每次读一个,并将读出的数字从缓冲区删除,然后将本进程 ID 和数字输出到标准输出。
(1)
信号量相关函数-------根据这些函数编写生产者消费者模型代码 pc.c
sem_t *sem_open(const char *name, int oflag,mode_t mode, unsigned int value);
sem_open() 的功能是创建一个信号量,或打开一个已经存在的信号量。如失败,返回值是 NULL
name 是信号量的名字。不同的进程可以通过提供同样的 name 而共享同一个信号量。
value 是信号量的初值,仅当新建信号量时,此参数才有效,其余情况下它被忽略。
int sem_wait(sem_t *sem);
sem_wait() 就是信号量的 P (生产者)原子操作(硬件原子操作指令与进出栈,中断指令是一样的)。返回 0 表示成功,返回 -1 表示失败。
如果信号量的值比0大,那么进行减一的操作,函数立即返回,往下执行。如果信号量当前为0值,那么调用就会一直阻塞或者直到信号量变得可以进行减一操作,达到阻塞进程的目的.
int sem_post(sem_t *sem);
sem_post() 就是信号量的 V (消费者)原子操作。如果有等待 sem 的进程,它会唤醒其中的一个。返回 0 表示成功,返回 -1 表示失败。
sem_post函数的作用是给信号量的值加上一个“1”,即解锁操作; 它是一个原子操作。
int sem_unlink(const char *name);
sem_unlink() 的功能是删除名为 name 的信号量。返回 0 表示成功,返回 -1 表示失败。
off_t lseek(int filedes, off_t offset, int whence);
lseek() 函数可以改变文件的文件偏移量,所有打开的文件都有一个当前文件偏移量,用于表明文件开始处到文件当前位置的字节数。成功返回新的偏移量,失败返回-1。
SEEK_SET 将读写位置指向文件头后再增加offset个位移量。
SEEK_CUR 以目前的读写位置往后增加offset个位移量。
SEEK_END 将读写位置指向文件尾后再增加offset个位移量。
用lseek创建一个空洞文件
空洞文件就是这个文件有一段是空的;
普通文件中间不能有空,write文件时从前往后移动文件指针,依次写入;
用lseek往后跳过一段,就形成空洞文件;
空洞文件对多线程共同操作文件非常有用。需要创建一个很大文件时,从头开始依次创建时间很长,可以将文件分成多段,多个线程操作每个线程负责其中一段的写入。
ret = lseek(fd, 10, SEEK_SET);
(2)
信号量实现生产者消费者模型代码 pc.c:
#include <unistd.h> #include <sys/types.h> #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <sys/stat.h> #include <semaphore.h> #include <sys/types.h> #include <sys/wait.h> #define M 530 /*打出数字总数*/ #define N 5 /*消费者进程数*/ #define BUFSIZE 10 /*缓冲区大小*/ int main() { sem_t *empty, *full, *mutex;/*3个信号量*/ int fd; /*共享缓冲区文件描述符*/ int i,j,k,child; int data;/*写入的数据*/ pid_t pid; int buf_out = 0; /*从缓冲区读取位置*/ int buf_in = 0; /*写入缓冲区位置*/ /*打开信号量*/ empty = sem_open("empty", O_CREAT|O_EXCL, 0644, BUFSIZE); /*剩余资源,初始化为size*/ full = sem_open("full", O_CREAT|O_EXCL, 0644, 0); /*已使用资源,初始化为0*/ mutex = sem_open("mutex", O_CREAT|O_EXCL, 0644, 1); /*互斥量,初始化为1*/ fd = open("buffer.txt", O_CREAT|O_TRUNC|O_RDWR,0666); lseek(fd,BUFSIZE*sizeof(int),SEEK_SET);/*刷新了40个字节的缓冲区,存放10个数字*/ write(fd,(char *)&buf_out,sizeof(int));/*将待读取位置存入buffer后,以便子进程之间通信*/ /*生产者进程*/ if((pid=fork())==0) { printf("I'm producer. pid = %d\n", getpid()); /*生产多少个产品就循环几次*/ for( i = 0 ; i < M; i++) { /*empty大于0,才能生产*/ sem_wait(empty); sem_wait(mutex); /*写入一个字符*/ lseek(fd, buf_in*sizeof(int), SEEK_SET); write(fd,(char *)&i,sizeof(int)); /*更新写入缓冲区位置,保证在0-9之间*/ /*生产完一轮产品(文件缓冲区只能容纳BUFSIZE个产品编号)后*/ /*将缓冲文件的位置指针重新定位到文件首部。*/ buf_in = (buf_in + 1) % BUFSIZE; sem_post(mutex); sem_post(full);/*共享区中已使用资源++,唤醒消费者线程*/ } printf("producer end.\n"); fflush(stdout);/*确保将输出立刻输出到标准输出。*/ return 0; } else if(pid < 0) { perror("Fail to fork!\n"); return -1; } /*消费者进程*/ for( j = 0; j < N ; j++ ) { if((pid=fork())==0) { for( k = 0; k < M/N; k++ ) { sem_wait(full);/*共享区中已使用资源--,一开始为0会阻塞此处*/ sem_wait(mutex); /*获得读取位置*/ lseek(fd,BUFSIZE*sizeof(int),SEEK_SET); read(fd,(char *)&buf_out,sizeof(int)); /*读取数据*/ lseek(fd,buf_out*sizeof(int),SEEK_SET); read(fd,(char *)&data,sizeof(int)); /*写入读取位置*/ buf_out = (buf_out + 1) % BUFSIZE; lseek(fd,BUFSIZE*sizeof(int),SEEK_SET); write(fd,(char *)&buf_out,sizeof(int)); sem_post(mutex); sem_post(empty);/*共享区中剩余资源++,唤醒生产者进程*/ /*消费资源*/ printf("%d: %d\n",getpid(),data); fflush(stdout); } printf("child-%d: pid = %d end.\n", j, getpid()); return 0; } else if(pid<0) { perror("Fail to fork!\n"); return -1; } } /*回收线程资源*/ child = N + 1; while(child--) wait(NULL); /*释放信号量*/ sem_unlink("full"); sem_unlink("empty"); sem_unlink("mutex"); /*释放资源*/ close(fd); return 0; }
(3)
在ubuntu下执行,结果如下:
gcc pc.c -o pc -lpthread ./pc > 1.txt cat 1.txt | more
2
在 linux-0.11 中实现信号量
这篇关于李治军老师操作系统实验5---信号量的实现和应用-信号量可以是-种资源的数量-进程看信号量实现同步即走走停停-信号临界区保护:共享数据的更新过程不能中断-此时切换去执行其他进程,切后进程看的是错信号量的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-12如何创建可引导的 ESXi USB 安装介质 (macOS, Linux, Windows)
- 2024-11-08linux的 vi编辑器中搜索关键字有哪些常用的命令和技巧?-icode9专业技术文章分享
- 2024-11-08在 Linux 的 vi 或 vim 编辑器中什么命令可以直接跳到文件的结尾?-icode9专业技术文章分享
- 2024-10-22原生鸿蒙操作系统HarmonyOS NEXT(HarmonyOS 5)正式发布
- 2024-10-18操作系统入门教程:新手必看的基本操作指南
- 2024-10-18初学者必看:操作系统入门全攻略
- 2024-10-17操作系统入门教程:轻松掌握操作系统基础知识
- 2024-09-11Linux部署Scrapy学习:入门级指南
- 2024-09-11Linux部署Scrapy:入门级指南
- 2024-08-21【Linux】分区向左扩容的方法