当前位置: 首页 > 编程日记 > 正文

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

生产者和消费者模型:

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

通过信号量,则可以将以上提出的不冲突的前提一一解决。当信号量的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。

相关文章:

mysql类exadata功能_几类关系型数据库的数据解决方案

今天聊下几类关系型数据库的数据解决方案,算是抛砖引玉&#xff0c;近期也要对技术方向上做一些扩展&#xff0c;也算是前期的小结吧。13Oracle目前市面上的主流版本应该还是11gR2&#xff0c;记得很多年前有个网站做过一次调查&#xff0c;10g&#xff0c;11g的版本比例差不多…

ZOJ 2723 Semi-Prime ||ZOJ 2060 Fibonacci Again 水水水!

两题水题&#xff1a; 1.如果一个数能被分解为两个素数的乘积&#xff0c;则称为Semi-Prime&#xff0c;给你一个数&#xff0c;让你判断是不是Semi-Prime数。 2.定义F(0) 7, F(1) 11, F(n) F(n-1) F(n-2) (n>2) 让你判断第n项是否能被3整除。 1.ZOJ 2723 Semi-Prime ht…

Lua中的基本函数库

基本函数库为Lua内置的函数库&#xff0c;不需要额外装载assert (v [, message])功能&#xff1a;相当于C的断言&#xff0c;参数&#xff1a;v&#xff1a;当表达式v为nil或false将触发错误,message&#xff1a;发生错误时返回的信息&#xff0c;默认为"assertion failed…

Oracle中的substr()函数 详解及应用

1&#xff09;substr函数格式 (俗称&#xff1a;字符截取函数) 格式1&#xff1a; substr(string string, int a, int b); 格式2&#xff1a;substr(string string, int a) ; 解释&#xff1a; 格式1&#xff1a; 1、string 需要截取的字符串 2、a 截取字符串…

linux进程间通信:POSIX 消息队列

文章目录基本介绍相关编程接口编程实例消息队列通信实例消息队列属性设置实例基本介绍 关于消息队列的基本介绍&#xff0c;前面在学习system V的消息队列时已经有过了解&#xff0c;linux进程间通信&#xff1a;system V消息队列 支持不同进程之间以消息&#xff08;message…

opencv 修改图像数值_opencv 修改图像数值_Python中使用OpenCV读取像素

如何在Python中遍历像素呢&#xff1f;请看代码&#xff1a;import numpy as npimport cv2import matplotlib.pyplot as pltsrc cv2.imread("src.png")gray cv2.cvtColor(src, cv2.COLOR_RGB2GRAY)rows, cols gray.shape[:2]row gray[200]plt.plot(row)# plt.sho…

SQL中返回一个字符串在另一个中存在的次数

ALTER FUNCTION [reg].[f_GetSameStringCntNoSort] ( str1 VARCHAR(100),--源字符串&#xff0c;取该串中的strLen长度的字符是否在目的字符串 str2 VARCHAR(100) --目的字符串 ) RETURNS INT AS BEGIN -- 返回的长度 DECLARE count INT DECLARE returnValue …

自由软件的自由

我相信我的订阅者中有很多是IT从业人员&#xff0c;我们在工作中会使用各种软件&#xff0c;除了微软、苹果等软件巨头的商业软件&#xff0c;还有更多的自由软件。我们使用这些软件构建自己的工作环境&#xff0c;为公司开发运营系统&#xff0c;为客户实现业务流程&#xff0…

c/c++ 拷贝控制 构造函数的问题

拷贝控制 构造函数的问题 问题1&#xff1a;下面①处的代码注释掉后&#xff0c;就编译不过&#xff0c;为什么&#xff1f;&#xff1f;&#xff1f; 问题2&#xff1a;但是把②处的也注释掉后&#xff0c;编译就过了&#xff0c;为什么&#xff1f;&#xff1f;&#xff1f; …

linux进程间通信:POSIX 消息队列 ----异步通信

在上一篇中linux进程间通信&#xff1a;POSIX 消息队列我们知道消息队列中在消息个数达到了队列所能承载的上限&#xff0c;就会发生消息的写阻塞。 阻塞式的通信影响系统效率&#xff0c;进程之间在通信收到阻塞时并不能去做其他事情&#xff0c;而是一直处于阻塞状态。 为了…

【转】Android设计中的.9.png

来源&#xff1a;http://isux.tencent.com/android-ui-9-png.html Android设计中的.9.png 注意&#xff1a;当使用9.png做TextView背景时&#xff0c;一定要设置内容区域&#xff08;底部和右侧&#xff09;&#xff0c;否则&#xff0c;文字显示不出来。 偶米伽 2013.08.20在A…

python创建scrapy_Python爬虫教程-31-创建 Scrapy 爬虫框架项目

首先说一下&#xff0c;本篇是在 Anaconda 环境下&#xff0c;所以如果没有安装 Anaconda 请先到官网下载安装Scrapy 爬虫框架项目的创建0.打开【cmd】1.进入你要使用的 Anaconda 环境1.环境名可以在【Pycharm】的【Settings】下【Project&#xff1a;】下找到2.使用命令&#…

KOAProgressBar

2019独角兽企业重金招聘Python工程师标准>>> KOAProgressBar 是 iOS 上的一个进度条控件&#xff0c;要求 iOS 5 支持 转载:http://www.adobex.com/ios/source/details/00000866.htm 转载于:https://my.oschina.net/u/868244/blog/106310

linux进程间通信:POSIX信号量

文章目录概念描述编程接口注意事项编程案例信号量基本接口使用案例信号量父子进程间通信信号量实现 两进程之间通信概念描述 英文&#xff1a;semaphore 简称SEM&#xff0c;主要用来进行进程间同步本质&#xff1a;内核维护的一个正整数&#xff0c;可对其进行各种/-操作分类…

指针小白:修改*p与p会对相应的地址的变量产生什么影响?各个变量指针的长度为多少?...

这两天敲代码碰到了一个这样的问题 代码如下&#xff1a; 1 #include <stdio.h>2 #include <stdlib.h>3 int main()4 {5 int num1 10;6 7 int* p&num1;8 *p 20;9 10 printf("%p\n", &num1); 11 printf("%d\…

MVC自带表单效验

废话不说&#xff0c;直接上代码&#xff0c;一看就明白 源地址&#xff1a;http://www.cnblogs.com/sixiangqimeng/p/3561313.html [StringLength(5,ErrorMessage "*长度小于5")][Required(ErrorMessage "*必填啊&#xff01;")][Required(ErrorMessage…

mysql求每个订单的平均价_MySQL – 选择所有客户和每个客户的总订单和总价值

SELECT a.ID,a.Name,COUNT(b.Customer) totalOrders,SUM(b.value) total_valueFROM Customers aLEFT JOIN Orders bON a.ID b.CustomerGROUP BY a.ID,a.Name要么SELECT a.ID,a.Name,COUNT(b.Customer) totalOrders,COALESCE(SUM(b.value), 0) total_valueFROM Customers aLEFT…

windows server 2008设置远程桌面连接最大数量

windows server 2008设置远程桌面连接最大数量 系统默认远程桌面连接的数量为1 打开控制面板---管理工具---远程桌面服务---远程桌面会话主机设置---把"限制每个用户只能进行一个会话"勾选去掉&#xff1b; 然后双击连接中的RDP-Tcp---网络适配器--最大连接数 修改为…

BZOJ1002 [FJOI2007]轮状病毒(最小生成树计数)

Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 7125 Solved: 3878[Submit][Status][Discuss]Description 轮状病毒有很多变种&#xff0c;所有轮状病毒的变种都是从一个轮状基产生的。一个N轮状基由圆环上N个不同的基原子和圆心处一个核原子构成的&#xff0c;2个原子之间的…

linux进程间通信:POSIX 共享内存

文章目录思维导图通信原理优势POSIX 共享内存 编程接口编程案例思维导图 之前学习过sysemV 的共享内存的实现及使用原理&#xff0c;参考linux进程间通信&#xff1a;system V 共享内存 POSIX 同样提供共享内存的接口&#xff0c;基本原理和system V的共享内存是一样的。 通信…

求二进制中1的个数(编程之美2.1)

行文脉络 解法一——除法解法二——移位解法三——高效移位解法四——查表扩展问题——异或后转化为该问题对于一个字节&#xff08;8bit&#xff09;的变量&#xff0c;求其二进制“1”的个数。例如6&#xff08;二进制0000 0110&#xff09;“1”的个数为2&#xff0c;要求算…

mysql管理用户数据库_MySQL 数据库管理(一)(用户与受权)

前言在企业信息化的过程当中&#xff0c;数据库中库和表都会大量存在&#xff0c;须要分配给管理者核实的权限进行操做合理地分配权限&#xff0c;可使数据库管理井井有理&#xff0c;各个管理者只须要关注本身负责的内容&#xff0c;也可避免误操做对系统形成损失1、用户与受权…

Smart-linkmonitor-link配置注意事项

一、应用场合 Smart Link用于双上行组网&#xff0c;Monitor Link一般用于与Smart Link的联动&#xff0c;配置在Smart Link的上游设备。 二、注意事项 在配置过程中&#xff0c;请注意以下几点&#xff1a; ? 1、指定实例之前请先配置MSTP实例。MSTP的实例和VLAN映射关系发生…

封装OpenCL类

以上一篇《OpenCL入门测试》为基础&#xff0c;将函数封装到类中&#xff0c;方便调用。 #include <cstdlib> #include <iostream> #include <iomanip> #include <cstring> #include <cassert> #include <windows.h> #define CL_USE_DEPRE…

linux系统调用 ftruncate设置文件大小

系统调用ftruncate可以将一个文件裁剪为指定的大小&#xff0c;函数描述如下&#xff1a; 头文件&#xff1a;<unistd.h> <sys/types.h>函数使用&#xff1a; int truncate(const char *path, off_t length); int ftruncate(int fd, off_t length);函数参数&…

剑指 offer set 22 数组中的逆序数

总结 1. 题目为归并排序的变形, 不过我完全没想到 2. 在归并排序进行字符组 merge 时, 统计逆序数. merge 后, 两个子数组是有序的了, 下次再 merge 的时候就能以 o(n) 的时间内找到某一个逆序对第二个元素的个数 转载于:https://www.cnblogs.com/xinsheng/p/3564026.html

qt信号发送间隔短而槽耗时多_Qt信号槽问题汇总 - osc_9q1dp3jk的个人空间 - OSCHINA - 中文开源技术交流社区...

1. 发送一次信号&#xff0c;调用多次槽函数问题在同一个类中&#xff0c;多次链接QObject::connect(sender, SIGNAL(signalSender(QString, int)), receiver, SLOT(onSignalSender(QString, int))); 会导致发送一次信号signalSender(QString, int) 多次调用槽函数(onSignalSen…

一劳永逸关闭Windwos默认共享

对于IPC$&#xff0c;找到注册表HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\LSA下的RestrictAnonymous项&#xff0c;并将键值改为1。 对于其它默认共享&#xff0c;在注册表HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters下&#…

springboot 集成mybatis时日志输出

application.properties(yml)中配置的两种方式&#xff1a; 这两种方式的效果是一样的&#xff0c;但是下面一种可以指定某个包下的SQL打印出来&#xff0c;上面这个会全部的都会打印出来。 转载于:https://www.cnblogs.com/z0909y/p/10077565.html

linux文件IO与内存映射:用户空间的IO缓冲区

文章目录用户空间IO缓冲区产生IO缓冲区 描述IO缓冲区的写模式自定义IO缓冲区用户空间IO缓冲区产生 系统调用过程中会产生的开销如下&#xff1a; 切换CPU到内核态进行数据内容的拷贝&#xff0c;从用户态到内核态或者从内核态到用户态切换CPU到用户态 以上为普通到系统调用过…