生产者和消费者模型:

  • 有若干个缓冲区,生产者不断向里填数据,消费者不断从中取数据
  • 两者不冲突的前提:
    • 缓冲区有若干个,且是固定大小,生产者和消费者各有若干个
    • 生产者向缓冲区中填数据前需要判断缓冲区是否满,满了则等待,直到有空间
    • 消费者从缓冲区中取数据前需要判断缓冲区是否为空,空了则等待,直到缓冲区有数据
    • 在某一时刻,缓冲区中只允许有一个操作者进行读或者写操作

通过信号量,则可以将以上提出的不冲突的前提一一解决。当信号量的value值为0时,则对当前信号量进行p操作(-1)时当前进程会挂起,直到信号量的value值为1。

当前生产者消费者模型并不友好,即生产者和消费者都需要受灾人口缓冲区前等待,判断是否能够写入或者取出,极大得浪费了系统空间。后期的优化应该为,当生产者写满之后向消费者发送一个可以来取信息的信号之后就离开,去做自己的事情。而消费者取完之后发送一个信号告诉生产者取完了,可以继续写入,消费者即可回去做自己的事情。
后期的优化待了解了进程信号处理方式之后合入当前生产者消费者模型之中

编程实例

  • 2个生产者每隔1秒向缓冲区中写入一次数据
  • 3个消费者每3秒,3秒,5秒从缓冲区中读出数据
  • 2个生产者分别对写信号量做P操作,对读信号量做V操作
  • 3个消费者分别对写信号量做V操作,对读信号量做P操作

代码如下:
productor.c生产者

#include<stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>//向获取到达共享内存的地址中写入值为data 的数据
//使用index表示当前数组下标同时表示当前缓冲区有多少个元素
//随着数据的不断写入,下标不断增加
void mem_write(int *addr, int data) {int index;index = addr[0];index ++;addr[index] = data;addr[0] = index;
}union semnum {int val;struct semid_ds *buf;unsigned short int *array;struct seminfo *__buf;
};
int sem_id;//初始化信号量的编号以及信号量的value值
void sem_init(int semid, int nsignum, int sem_value) {union semnum sem_union;sem_union.val = sem_value;if (semctl(semid,nsignum,SETVAL,sem_union) == -1) {printf("semctl failed\n");_exit(-1);}
}//信号量-1操作,即编号为nsignum的信号量的value进行-1操作
void sem_p(int semid, int nsignum) {struct sembuf sops;sops.sem_num = nsignum;sops.sem_op = -1;sops.sem_flg = SEM_UNDO;if (semop(sem_id, &sops,1) == -1) {printf("semop P failed \n");_exit(-1);}}//信号量+1 操作,即编号为nsignum的信号量的value进行+1操作
void sem_v(int sem_id, int nsignum) {struct sembuf sops;sops.sem_num = nsignum;sops.sem_op = 1;sops.sem_flg = SEM_UNDO;if (semop(sem_id, &sops,1) == -1) {printf("semop V failed \n");_exit(-1);}
}//打印信号量,通过semctl获取信号量的数据结构,暂时没有用到
void sem_print(int sem_id, int nsignum) {int sem_value;sem_value = semctl(sem_id, nsignum, GETVAL);printf("sem[%d] = %d \n",nsignum, sem_value);
}int main() {//生成共享内存和信号量的keyint shm_id;key_t shm_key = ftok("./",111);key_t sem_key = ftok("./",112);//shmget获取共享内存的IPC标识,并进行内存的映射shmat得到共享内存的地址shm_id = shmget(shm_key, 1028, IPC_CREAT|0666);char *shm_addr = shmat(shm_id,NULL,0);//memset (shm_adr,0,sizeof(shm_addr));memset (shm_addr,0,128);//通过key生成信号量的IPC标识sem_id = semget(sem_key, 2,IPC_CREAT | 0666);if (sem_id == -1) {printf("semget failed\n");_exit(-1);} else {sem_init(sem_id,0,0); //read sem//写信号量初始化为5,表示可以写5个缓冲区,即我们上图中描述的支持写入5个bufsem_init(sem_id,1,5); //write sem}int ret;if( (ret = fork()) == -1 ) {printf("fork failed\n");_exit(-1);}//创建子进程,每隔一秒,写入数据else if (ret == 0) {int child_data = 1;while (1) {sleep(1);//写入之前将写信号量进行p操作(-1),表示写入一个缓冲区sem_p(sem_id,1);printf("child data  %d\n",child_data);mem_write((int *)shm_addr,child_data);child_data += 2;//写之后将读信号量进行v操作(+1),表示支持读一个缓冲区sem_v(sem_id,0);}}//父进程每隔一秒写入数据else {int parent_data = 2;while(1) {sleep(1);sem_p(sem_id ,1);printf("parent_data %d\n",parent_data);mem_write((int *)shm_addr, parent_data);parent_data = parent_data + 2;sem_v(sem_id,0);}}
}

consumer.c消费者

#include<stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>//这里读时和mem_write相反,此时从共享内存中读出一个数据
//同时将index--;表示读出了一个数据,已经支持向内存写入
int mem_read(int *addr) {int index, data;index = addr[0];data = addr[index];index --;addr[0] = index;return data;
}union semnum {int val;struct semid_ds *buf;unsigned short int *array;struct seminfo *__buf;
};
int sem_id;void sem_init(int semid, int nsignum, int sem_value) {union semnum sem_union;sem_union.val = sem_value;if (semctl(semid,nsignum,SETVAL,sem_union) == -1) {printf("semctl failed\n");_exit(-1);}
}void sem_p(int semid, int nsignum) {struct sembuf sops;sops.sem_num = nsignum;sops.sem_op = -1;sops.sem_flg = SEM_UNDO;if (semop(sem_id, &sops,1) == -1) {//printf("semop P failed \n");//_exit(-1);}}void sem_v(int sem_id, int nsignum) {struct sembuf sops;sops.sem_num = nsignum;sops.sem_op = 1;sops.sem_flg = SEM_UNDO;if (semop(sem_id, &sops,1) == -1) {//printf("semop v failed \n");// _exit(-1);}
}void sem_print(int sem_id, int nsignum) {int sem_value;sem_value = semctl(sem_id, nsignum, GETVAL);printf("sem[%d] = %d \n",nsignum, sem_value);
}int main() {//共享内存和信号量都支持key形式的对象生成int shm_id,sem_id;key_t shm_key = ftok("./",111);key_t sem_key = ftok("./",112);//获取共享内存的IPC标识,并进行内存的映射得到共享内存的地址shm_id = shmget(shm_key, 1028, IPC_CREAT|0666);char *shm_addr = shmat(shm_id,NULL,0);memset (shm_addr,0,128);//通过key生成信号量的IPC标识sem_id = semget(sem_key, 2,IPC_CREAT | 0666);if (sem_id == -1) {printf("semget failed\n");_exit(-1);} else {sem_init(sem_id,0,0); //read semsem_init(sem_id,1,5); //write sem}//创建两个子进程读,每隔三秒读一个缓冲区//读之前将读信号量p(-1)操作,表示读一个信号量//读之后将写信号量v(+1)操作,表示支持写一个信号量for (int i = 0;i < 2; ++i) {int ret;if ((ret = fork()) == -1) {printf("fork failed\n");_exit(-1);} else if (ret == 0) {while (1) {sleep(3);sem_p (sem_id, 0);printf("pid %d data :%d\n",getpid(), mem_read((int *)shm_addr));sem_v (sem_id,1);}}}//父进程同样的方式也进行读while(1) {sleep(5);sem_p(sem_id , 0);printf("pid %d data :%d\n",getpid(), mem_read((int *)shm_addr));sem_v(sem_id, 1);}return 0;
}

运行结果如下:

system V 信号量的通信特点

  • 信号量是通过标识而不是常用的文件描述符来引用的
  • 使用键而不是文件名来表示信号量
  • 创建,初始化,操作信号量需要单独的系统调用,分别为semget,semctl,semop;关于system V 信号量的详细使用可以参考文章linux进程间通信:system V 信号量
  • 信号量的操作同样会有阻塞现象。当信号量的value值为0时,则对当前信号量进行p操作(-1)时当前进程会挂起,直到信号量的value值为1。

linux进程间通信:system V 信号量 生产者和消费者模型编程案例相关推荐

  1. 【Linux下】 线程同步 生产者与消费者模型

    文章目录 [Linux下] 线程同步 生产者与消费者模型 线程同步 同步概念与竞态条件 条件变量 条件变量本质 操作条件变量 初始化和销毁条件变量 等待 唤醒 通过条件变量实现的简单线程同步例子 为什 ...

  2. Linux进程间通信三 System V 信号量简介与示例

    1. System V信号量简介 SystemV信号量主要用于解决生产者和消费者问题,一个信号量能够控制多个资源,说它是信号量集也不为过. 2. API接口介绍 2.1 创建或打开信号量集 #incl ...

  3. linux 内核信号量与用户态信号量(system v,信号量在Linux多线程机制中的应用

    [摘 要]本文以信号量原理为基础,重点阐述信号量在Linux多线程同步机制中的实现特色. [关键词]信号量:Linux:多线程:同步 1 信号量 1965年E. W. Dijkstra首次提出信号量的 ...

  4. 【Linux系统编程】进程同步与互斥:System V 信号量

    信号量概述 信号量广泛用于进程或线程间的同步和互斥,信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问. 编程时可根据操作信号量值的结果判断是否对公共资源具有访问的权限,当信号量值大于 ...

  5. Linux系统编程:使用semaphore信号量和mutex互斥量实现多个生产者和消费者模型

    代码实现 如题,使用semaphore信号量和mutex互斥量实现多个生产者和消费者模型.本来是想只用信号量实现生产者消费者模型的,但是发现 只能在一个生产者和一个消费者之间,要在多个生产者和消费者模 ...

  6. Linux IPC实践(11) --System V信号量(1)

    信号量API #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> int semget ...

  7. 【Linux】生产者与消费者模型、信号量、死锁

    目录 死锁 死锁的产生场景 死锁的gdb分析 1.通过调试可执行程序来分析 2.通过调试正在运行的程序 死锁的必要条件 死锁的预防 生产者与消费者模型 123规则 应用场景及特点 代码实现: 信号量 ...

  8. 【Linux】system V 消息队列 | system V 信号量(简单赘述)

    文章目录 1 . system V 消息队列(了解) 接口 查看消息队列 2.system V 信号量 (了解) 1.进程互斥等概念的理解 2.认识信号量 3. 接口 这两部分主要是了解即可,为后面学 ...

  9. Linux系统编程---17(条件变量及其函数,生产者消费者条件变量模型,生产者与消费者模型(线程安全队列),条件变量优点,信号量及其主要函数,信号量与条件变量的区别,)

    条件变量 条件变量本身不是锁!但它也可以造成线程阻塞.通常与互斥锁配合使用.给多线程提供一个会合的场所. 主要应用函数: pthread_cond_init 函数 pthread_cond_destr ...

最新文章

  1. Exchange Server 2010 全新部署篇八:CASHUB中集线器配置部分
  2. 程序人生:什么是“对用户友好”
  3. Linux脚本利器sed
  4. python语言发明者 google_看看9种编程语言的发明者是怎么说的
  5. MEF(可扩展框架)使用总结
  6. dbscan论文_重点关注!我校论文被CCF A类人工智能国际顶级学术会议IJCAI录用
  7. Atitit 搜索蓝牙设备 powershell的实现 java noede.js python 先用脚本语言python nodejs,不好实现。。Java 也不好实现。。 Netcore可以,
  8. 基于DL的目标检测技术:R-CNN、Fast R-CNN、Faster R-CNN
  9. 4g网卡MF831移植到smart210
  10. excel 的条件格式(一)
  11. Verifying dml pool data
  12. Longest Continuous Increasing Subsequence(最长递增连续子序列)
  13. Matlab龚珀兹曲线模型预测,指数曲线模型的讲解=.pptx
  14. 微博html5版打不开,PC端网页版微博就是打不开是什么问题啊!缓 – 手机爱问
  15. JMeter BeanShell 应用
  16. 推荐十四款常见的Web前端开发框架
  17. 盘点国内MCU级RISC-V内核IP厂商
  18. 网络信息安全之基于时间的安全模型(PDR和PPDR模型)
  19. 学习——Regression Tree 回归树
  20. 为什么聪明人未能拯救世界?|《流浪地球》冷思考

热门文章

  1. 使用Netsil监控Kubernetes上的微服务
  2. GPS部标平台的架构设计(十)-基于Asp.NET MVC构建GPS部标平台
  3. wp 删除独立存储空间文件(多级非空文件夹删除)
  4. POJ1088(滑雪)
  5. 使用SharpPCap在C#下进行网络抓包
  6. #时间预测算法_改进的智慧交通系统出行时间预测算法
  7. 三角测量计算三维坐标的代码_浅谈三维扫描仪的由来
  8. java 变量的线程可见性_Java多线程——变量可见性
  9. php jwt token过期时间,php – 动态设置laravel jwt的到期时间
  10. java map collection_java 集合----Map、Collection