五十一、进程间通信——System V IPC 之进程信号量
51.1 进程信号量
51.1.1 信号量
- 本质上就是共享资源的数目,用来控制对共享资源的访问
- 用于进程间的互斥和同步
- 每种共享资源对应一个信号量,为了便于大量共享资源的操作引入了信号量集,可对所有信号量一次性操作。对信号量集中所有操作可以要求全部成功,也可以部分成功
- 二元信号量(信号灯)值为 0 和 1
- 对信号量做 PV 操作2
51.1.2 信号量集属性
51.1.3 创建信号量集
- 函数参数:
- key:用户指定的信号量集键值
- nsems:信号量集中信号量个数
- semflg:IPC_CREAT,IPC_EXCL 等权限组合
- 返回值:成功,返回信号量集 ID,出错,返回 -1
51.1.4 信号量集控制
- 函数参数:
- semid:信号量集 ID
- semnum:0 表示对所有信号量操作,信号量编号从 0 开始
- cmd:控制命令,通过 cmd 参数设定对信号量集要执行的操作
- IPC_STAT:获取信号量集的属性 ---> buf
- IPC_SET:设置信号量集的属性 ---> buf
- IPC_RMID:删除信号量集 ---> buf
- GETVAL:返回信号量的值 ---> val
- SETVAL:设置 semnum 信号量的值 ---> val
- GETALL:获取所有信号量的值 ---> arryr
- SETALL:设置所有信号量的初始值 ---> array
- arg:即 ... ,semun 联合体变量
- val:放置获取或设置信号量集中某个信号量的值
- buf:信号量集属性指针
- array:放置获取或设置信号量集中所有信号量的值
51.1.5 信号量集操作
- 函数参数:
- semid:信号集 ID
- sops:sembuf 结构体数组指针
- sem_num:信号集中信号量的编号
- sem_op:正数为 V 操作,负数为 P 操作,0 可用于对共享资源是否已用完的测试
- sem_flg:SEM_UNDO 标识,表示在进程结束时,相应的操作将被取消。如果设置了该标志,那么在进程没有释放共享资源就退出时,内核将代为释放
- nsops:第二个参数中结构体数组的长度
- 返回值:成功返回 0;出错返回 -1
- 其他说明:
- 用于信号量集中信号量的加和减操作(PV 操作)
- 可用于进程间的互斥或同步
51.2 信号量例子
51.2.1 PV 操作
(1)PV模块
sem_pv.h
- #ifndef INCLUDE_SEM_PV_H_
- #define INCLUDE_SEM_PV_H_
- #include <sys/sem.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <assert.h>
- #include <malloc.h>
- union semun {
- int val;
- struct semid_ds *buf;
- unsigned short *array;
- };
- /** 初始化 semnums 个信号灯/信号量值(value) */
- extern int sem_I(int semnums, int value);
- /** 对信号量集(semid)中的信号灯(semnum)作 P() */
- extern void sem_P(int semid, int semnum, int value);
- /** 对信号集(semid) 中的信号灯(semnum)作V(value)操作 */
- extern void sem_V(int semid, int semnum, int value);
- /** 销毁信号量集(semid) */
- extern void sem_D(int semid);
- #endif /* INCLUDE_SEM_PV_H_ */
sem_pv.c
- #include "sem_pv.h"
- /** 初始化 semnums 个信号灯/信号量值(value) */
- int sem_I(int semnums, int value)
- {
- /** 创建信号量集 */
- int semid;
- /** 创建信号量集 */
- semid = semget(IPC_PRIVATE, semnums, IPC_CREAT | IPC_EXCL | );
- if(semid < ){
- return -;
- }
- union semun un;
- unsigned short *array = (unsigned short *)calloc(semnums, sizeof(unsigned short));
- int i;
- for(i = ; i < semnums; i++){
- array[i] = value;
- }
- un.array = array;
- /**
- * 初始化信号量集中所有信号灯的初值
- * 0: 表示要初始化所有的信号灯
- */
- if(semctl(semid, , SETALL, un) < ){
- perror("semctl error");
- return -;
- }
- free(array);
- return semid;
- }
- /** 对信号量集(semid)中的信号灯(semnum)作 P() */
- void sem_P(int semid, int semnum, int value)
- {
- assert(value >= );
- /** 定义 sembuf 类型的结构体数组,放置若干个结构体变量,对应要操作的信号量、P或V操作 */
- struct sembuf ops[] = {{semnum, -value, SEM_UNDO}};
- if(semop(semid, ops, sizeof(ops)/sizeof(struct sembuf)) < ){
- perror("semop error");
- }
- }
- /** 对信号集(semid) 中的信号灯(semnum)作V(value)操作 */
- void sem_V(int semid, int semnum, int value)
- {
- assert(value >= );
- /** 定义 sembuf 类型的结构体数组,放置若干个结构体变量,对应要操作的信号量、P或V操作 */
- struct sembuf ops[] = {{semnum, value, SEM_UNDO}};
- if(semop(semid, ops, sizeof(ops)/sizeof(struct sembuf)) < ){
- perror("semop error");
- }
- }
- /** 销毁信号量集(semid) */
- void sem_D(int semid)
- {
- if(semctl(semid, , IPC_RMID, NULL) < ){
- perror("semctl error");
- }
- }
编译:
gcc -o obj/sem_pv.o -Iinclude -c src/sem_pv.c
(2)互斥操作
atm_account.h
- #ifndef INCLUDE_ATM_ACCOUNT_H_
- #define INCLUDE_ATM_ACCOUNT_H_
- #include <malloc.h>
- #include <assert.h>
- #include <string.h>
- typedef struct {
- int code;
- double balance;
- int semid; ///< 在共享资源上绑定一个信号量集
- }atm_account;
- /** 取款 */
- extern double atm_account_withdraw(atm_account *a, double amt);
- /** 存款 */
- extern double atm_account_deposit(atm_account *a, double amt);
- /** 查看账户余额度 */
- extern double amt_account_balanceGet(atm_account *a);
- #endif /* INCLUDE_ATM_ACCOUNT_H_ */
atm_account.c
- #include "sem_pv.h"
- #include "atm_account.h"
- /** 取款 */
- double atm_account_withdraw(atm_account *a, double amt)
- {
- assert(a != NULL);
- /** 对信号量集 semid 中的0号信号量/信号灯作 P(1) 操作 */
- sem_P(a->semid, , );
- if(amt < || amt > a->balance){
- /** 对信号量集 semid 中的0号信号量/信号灯作 V(1) 操作 */
- sem_V(a->semid, , );
- return 0.0;
- }
- double balance = a->balance;
- sleep();
- balance -= amt;
- a->balance = balance;
- /** 对信号量集 semid 中的0号信号量/信号灯作 V(1) 操作 */
- sem_V(a->semid, , );
- return amt;
- }
- /** 存款 */
- double atm_account_deposit(atm_account *a, double amt)
- {
- assert(a != NULL);
- /** 对信号量集 semid 中的0号信号量/信号灯作 P(1) 操作 */
- sem_P(a->semid, , );
- if(amt < ){
- /** 对信号量集 semid 中的0号信号量/信号灯作 V(1) 操作 */
- sem_V(a->semid, , );
- return 0.0;
- }
- double balance = a->balance;
- sleep();
- balance += amt;
- a->balance = balance;
- /** 对信号量集 semid 中的0号信号量/信号灯作 V(1) 操作 */
- sem_V(a->semid, , );
- return amt;
- }
- /** 查看账户余额度 */
- double amt_account_balanceGet(atm_account *a)
- {
- assert(a != NULL);
- /** 对信号量集 semid 中的0号信号量/信号灯作 P(1) 操作 */
- sem_P(a->semid, , );
- double balance = a->balance;
- /** 对信号量集 semid 中的0号信号量/信号灯作 V(1) 操作 */
- sem_V(a->semid, , );
- return balance;
- }
测试代码:atm_account_test.c
- #include "atm_account.h"
- #include "sem_pv.h"
- #include <unistd.h>
- #include <sys/shm.h>
- #include <sys/wait.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- int main(void)
- {
- /** 在共享内存中创建银行账户 */
- int shmid;
- if((shmid = shmget(IPC_PRIVATE, sizeof(atm_account), IPC_CREAT | IPC_EXCL | )) < ){
- perror("shmget error");
- return ;
- }
- /** 进行共享内存映射(a 为映射的地址) */
- atm_account *a = (atm_account *)shmat(shmid, , );
- if(a == (atm_account *)-){
- perror("shmat error");
- return ;
- }
- a->code = ;
- a->balance = ;
- /** 创建信号量集并初始化(1 个信号量/信号灯,初值为 1) */
- a->semid = sem_I(, );
- if(a->semid < ){
- perror("sem_I(1, 1) error");
- return ;
- }
- printf("balance: %f\n", a->balance);
- pid_t pid;
- if((pid = fork()) < ){
- perror("fork error");
- return ;
- }
- else if(pid > ){
- /** 父进程执行取款操作 */
- double amt = atm_account_withdraw(a, );
- printf("pid %d withdraw %f form code %d\n", getpid(), amt, a->code);
- wait();
- /** 对共享内存的操作要在解除映射之前 */
- printf("balance: %f\n", a->balance);
- sem_D(a->semid); ///< 销毁信号量集
- shmdt(a); ///< 解除共享内存的映射
- shmctl(shmid, IPC_RMID, NULL);///< 释放共享内存
- }
- else {
- /** 子进程进行取款操作 */
- double amt = atm_account_withdraw(a, );
- printf("pid %d withdraw %f form code %d\n", getpid(), amt, a->code);
- shmdt(a); ///< 解除共享内存的映射
- }
- return ;
- }
编译运行如下:
51.2.2 PV操作--读者写者案例
目的:利用进程信号量的 PV操作实现进程间的同步问题
共享内存中读写数据(读者和写者问题)
- #include <sys/shm.h>
- #include <sys/sem.h>
- #include <sys/wait.h>
- #include <unistd.h>
- #include <string.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <assert.h>
- /** 读者和写者的共享资源 */
- typedef struct {
- int val;
- int semid;
- }Storage;
- void init(Storage *s)
- {
- assert(s != NULL);
- /** 创建信号量集(包含 2 个信号量) */
- if((s->semid = semget(IPC_PRIVATE, , IPC_CREAT | IPC_EXCL | )) < ){
- perror("semget error");
- exit();
- }
- /** 对信号量集中的所有信号量初始化 */
- union semun{
- int val;
- struct semid_ds *ds;
- unsigned short *array;
- };
- union semun un;
- /** 2 个信号量的初值设置为 0 */
- unsigned short array[] = {, };
- un.array = array;
- if(semctl(s->semid, , SETALL, un) < ){
- perror("semctl error");
- exit();
- }
- }
- void destroy(Storage *s)
- {
- assert(s != NULL);
- if(semctl(s->semid, , IPC_RMID, NULL) < ){
- perror("semctl error");
- exit();
- }
- }
- void writer(Storage *s, int val)
- {
- /** 写入数据到 Storage */
- s->val = val;
- printf("%d write %d\n", getpid(), val);
- /** 设置信号量 0 号作 V(1) 操作 */
- struct sembuf ops_v[] = {{, , SEM_UNDO}};
- /** 设置信号量 1 号作 P(1) 操作 */
- struct sembuf ops_p[] = {{, -, SEM_UNDO}};
- /** V(s1) */
- if(semop(s->semid, ops_v, ) < ){
- perror("semop error");
- }
- /** P(s2) */
- if(semop(s->semid, ops_p, ) < ){
- perror("semop error");
- }
- }
- void reader(Storage *s)
- {
- assert(s != NULL);
- /** 设置信号量 0 号作 P(1) 操作 */
- struct sembuf ops_p[] = {{, -, SEM_UNDO}};
- /** 设置信号量 1 号作 V(1) 操作 */
- struct sembuf ops_v[] = {{, , SEM_UNDO}};
- /** P(s1) */
- if(semop(s->semid, ops_p, ) < ){
- perror("semop error");
- }
- /** 从 Storage 中读取数据 */
- printf("%d read %d\n", getpid(), s->val);
- /** V(s2) */
- if(semop(s->semid, ops_v, ) < ){
- perror("semop error");
- }
- }
- int main(void)
- {
- /** 将共享资源 Storage 创建在共享内存中 */
- int shmid;
- if((shmid = shmget(IPC_PRIVATE, sizeof(Storage), IPC_CREAT | IPC_EXCL | )) < ){
- perror("shmget error");
- exit();
- }
- /** 父进程进行共享内存映射 */
- Storage *s = (Storage *)shmat(shmid, , );
- if(s == (Storage *)-){
- perror("shmat error");
- exit();
- }
- /** 创建信号量并初始化 */
- init(s);
- pid_t pid;
- pid = fork();
- if(pid < ){
- perror("fork error");
- exit();
- }
- else if(pid > ){
- int i = ;
- for(;i <= ; i++){
- writer(s, i);
- }
- wait();
- destroy(s);
- shmdt(s);
- shmctl(shmid, IPC_RMID, NULL);
- }
- else{
- int i = ;
- for(;i <= ; i++){
- reader(s);
- }
- shmdt(s);
- }
- }
五十一、进程间通信——System V IPC 之进程信号量的更多相关文章
- 四十九、进程间通信——System V IPC 之消息队列
49.1 System V IPC 介绍 49.1.1 System V IPC 概述 UNIX 系统存在信号.管道和命名管道等基本进程间通讯机制 System V 引入了三种高级进程间通信机制 消息 ...
- 五十、进程间通信——System V IPC 之共享内存
50.1 共享内存 50.1.1 共享内存的概念 共享内存区域是被多个进程共享的一部分物理内存 多个进程都可把该共享内存映射到自己的虚拟内存空间.所有用户空间的进程若要操作共享内存,都要将其映射到自己 ...
- 从并发处理谈PHP进程间通信(二)System V IPC
.container { margin-right: auto; margin-left: auto; padding-left: 15px; padding-right: 15px } .conta ...
- UNIX 进程间通讯(IPC)概念(Posix,System V IPC)
IPC(Inter-Process Communication,进程间通讯)可以有三种信息共享方式(随文件系统,随内核,随共享内存).(当然这里虽然说是进程间通讯,其实也是可以和线程相通的). 相对 ...
- System V IPC 之共享内存
IPC 是进程间通信(Interprocess Communication)的缩写,通常指允许用户态进程执行系列操作的一组机制: 通过信号量与其他进程进行同步 向其他进程发送消息或者从其他进程接收消息 ...
- System V IPC 之信号量
本文继<System V IPC 之共享内存>之后接着介绍 System V IPC 的信号量编程.在开始正式的内容前让我们先概要的了解一下 Linux 中信号量的分类. 信号量的分类 在 ...
- System V IPC 之消息队列
消息队列和共享内存.信号量一样,同属 System V IPC 通信机制.消息队列是一系列连续排列的消息,保存在内核中,通过消息队列的引用标识符来访问.使用消息队列的好处是对每个消息指定了特定消息类型 ...
- pause、jobs、setitimer(2)、system v ipc(day12)
一.pause()的使用 #include <unistd.h> int pause(void); 功能:等待信号的到来 返回值: - 错误 errno被设置 只有在信号处理函数执行完毕的 ...
- Linux 系统编程 学习:04-进程间通信2:System V IPC(1)
Linux 系统编程 学习:04-进程间通信2:System V IPC(1) 背景 上一讲 进程间通信:Unix IPC-信号中,我们介绍了Unix IPC中有关信号的概念,以及如何使用. IPC的 ...
随机推荐
- 通过shell命令往android中写入配置
C:\Users>adb shell setprop "persist.sys.btylevel" 100 C:\Users>adb shell getprop &qu ...
- 数据可视化的开源方案: Superset vs Redash vs Metabase (二)
在上篇结尾处我提到“如果现在让我重新选择,我会使用哪个可视化工具?”我的答案是 Redash,原因主要不是功能层面,而是技术层面.本篇就从项目关注度与活跃度,项目的技术架构,源代码的规模与质量,这三个 ...
- web框架开发-模板层
你可能已经注意到我们在例子视图中返回文本的方式有点特别. 也就是说,HTML被直接硬编码在 Python代码之中. def current_datetime(request): now = datet ...
- [LeetCode]2. 两数相加
题目链接:https://leetcode-cn.com/problems/add-two-numbers/ 题目描述: 给出两个 非空 的链表用来表示两个非负的整数.其中,它们各自的位数是按照 逆序 ...
- STM32F40G-EVAL_UC/OS III
micrum官网下载uc/os程序包: 包含文件cotex_M4.h:
- ES ik分词器使用技巧
match查询会将查询词分词,然后对分词的结果进行term查询. 然后默认是将每个分词term查询之后的结果求交集,所以只要分词的结果能够命中,某条数据就可以被查询出来,而分词是在新建索引时指定的,只 ...
- centos7内网源站建设
centos7内网源站建设 1.部署环境: 系统:Centos7 x86_64 应用服务:nginx.createrepo.reposync 镜像源:https://mirrors.aliyun.co ...
- 解决hash冲突的三个方法
通过构造性能良好的哈希函数,可以减少冲突,但一般不可能完全避免冲突,因此解决冲突是哈希法的另一个关键问题.创建哈希表和查找哈希表都会遇到冲突,两种情况下解决冲突的方法应该一致.下面以创建哈希表为例,说 ...
- 小小知识点(四)——MATLAB如何画等高线图和线性规划约束方程
MATLAB程序: figure contourf(x,y,data) % 画等高线 hold on plot(x,y(x)) %画线性规划约束方程1 hold on plot(y,x(y)) %画线 ...
- CodeForces Round #552 Div.3
A. Restoring Three Numbers 代码: #include <bits/stdc++.h> using namespace std; ]; int a, b, c; i ...