【Linux系统编程】进程同步与互斥:System V 信号量
信号量概述
信号量广泛用于进程或线程间的同步和互斥,信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问。
编程时可根据操作信号量值的结果判断是否对公共资源具有访问的权限,当信号量值大于 0 时,则可以访问,否则将阻塞。PV 原语是对信号量的操作,一次 P 操作使信号量减1,一次 V 操作使信号量加1。
在实际应用中两个进程间通信可能会使用多个信号量,因此 System V 的信号量以集合的概念来管理,具体操作和 Posix 信号量大同小异
信号量集合数据结构:struct semid_ds,此数据结构中定义了整个信号量集的基本属性。
/* Obsolete, used only for backwards compatibility and libc5 compiles */
struct semid_ds
{struct ipc_perm sem_perm; /* permissions .. see ipc.h */__kernel_time_t sem_otime; /* last semop time */__kernel_time_t sem_ctime; /* last change time */struct sem *sem_base; /* ptr to first semaphore in array */struct sem_queue *sem_pending; /* pending operations to be processed */struct sem_queue **sem_pending_last; /* last pending operation */struct sem_undo *undo; /* undo requests on this array */unsigned short sem_nsems; /* no. of semaphores in array */
};
信号量数据结构:struct sem,此数据结构中定义了信号量的基本属性。
/* One semaphore structure for each semaphore in the system. */
struct sem
{int semval; /* current value *信号量的值*/int sempid; /* pid of last operation *最后一个操作信号量的进程号*/struct list_head sem_pending; /* pending single-sop operations */
};
System V 信号量基本操作
使用 shell 命令操作信号量:
查看信号量:ipcs -s
删除信号量:ipcrm -s semid
以下函数所需头文件如下:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
1)创建信号量集合
int semget(key_t key, int nsems, int semflg);
功能:
创建或打开一个信号量集合,该集合中可以包含多个信号量。
参数:
key:进程间通信键值,通过调用 ftok() 函数得到的键值
nsems:创建的信号量的个数。如果只是访问而不创建则可以指定该参数为 0,一旦创建了该信号量,就不能更改其信号量个数,只要不删除该信号量,重新调用该函数创建该键值的信号量,该函数只是返回以前创建的值,不会重新创建。
semflg:标识函数的行为及信号量的权限,其取值如下:
IPC_CREAT:创建信号量。
IPC_EXCL:检测信号量是否存在。
位或权限位:信号量位或权限位后可以设置信号量的访问权限,格式和 open 函数的 mode_ t 一样(open() 的使用请点此链接),但可执行权限未使用。
返回值:
成功:信号量集标识符
失败:返回 -1
2)控制信号量集合、信号量
int semctl(int semid, int semnum, int cmd, ...);
功能:
对信号量集合以及集合中的信号量进行操作。
参数:
semid:信号量集标识符。
semnum:集合中信号量的序号,指定对哪个信号量操作, 只对几个特殊的 cmd 操作有意义。
cmd:信号量控制类型。semctl() 函数可能有3个参数,也可能有4个参数,参数的个数由 cmd 决定。当有4个参数时,第4个参数为联合体:
union semun{int val; /*信号量的值*/struct semid_ds *buf; /*信号量集合信息*/unsigned short *array;/*信号量值的数组*/struct seminfo *__buf;/*信号量限制信息*/ };
cmd 的取值如下:
GETVAL:获取信号量的值。此时函数有3个参数。semctl() 函数的返回值即为信号量的值。
SETVAL:设置信号量的值。此时函数有4个参数。第4个参数为联合体中的val,其值为信号量的值。
IPC_STAT:获取信号量集合的信息。此时函数有4个参数。第4个参数为联合体中的__buf。
IPC_SET:设置信号量集合的信息。此时函数有4个参数。第4个参数为联合体中的__buf。
IPC_RMID:删除信号量集。此时函数有3个参数,第2个参数semnum不起作用。
GETALL:获取所有信号量的值。此时函数有4个参数,第2个参数semnum不起作用。第4个参数为联合体中的array,其值为用来存放所有信号量值的数组的首地址。
SETALL:设置所有信号量的值 。参数说明同上。
IPC_INFO:获取信号量集合的限制信息。此时函数有4个参数,第2个参数semnum不起作用。第4个参数为联合体中的__buf。
GETPID:获取信号的进程号,即最后操作信号量的进程。此时函数有3个参数。semctl() 函数的返回值即为信号的进程号。
GETNCNT:获取等待信号的值递增的进程数。此时函数有3个参数。semctl() 函数的返回值即为进程数。
GETZCNT:获取等待信号的值递减的进程数。此时函数有3个参数。semctl() 函数的返回值即为进程数。
返回值:
成功:0
失败:-1
3)操作信号量
int semop(int semid, struct sembuf *sops, unsigned nsops);
功能:
操作信号量,主要进行信号量加减操作。
参数:
semid:信号量集标识符。
sops:操作信号量的结构体(struct sembuf)数组的首地址( 结构体定义在 sys/sem.h ),此结构体中的数据表明了对信号量进行的操作。struct sembuf{unsigned short sem_num; /*信号量的序号*/short sem_op; /*信号量的操作值*/short sem_flg; /*信号量的操作标识*/ };
结构体成员使用说明如下:
sem_num:信号量集中信号量的序号
sem_op 取值如下:
sem_op > 0:信号量的值在原来的基础上加上此值。
sem_op < 0:如果信号量的值小于 semop 的绝对值,则挂起操作进程。如果信号量的值大于等于 semop 的绝对值,则信号量的值在原来的基础上减去 semop 的绝对值。
sem_op = 0:对信号量的值进行是否为 0 测试。若为 0 则函数立即返回,若不为 0 则阻塞调用进程。
sem_flag 取值如下:
IPC_NOWAIT:在对信号量的操作不能执行的情况下使函数立即返回。
SEM_UNDO:当进程退出后,该进程对信号量进行的操作将被撤销。
nsops:操作信号量的结构体数组中元素的个数。
返回值:
成功:0
失败:-1
使用示例
示例一:
#include <sys/types.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <stdlib.h>
#include <stdio.h>int main(int argc, char *argv[])
{key_t key;//创建key值key = ftok(".", 'a');if(key == -1){perror("ftok");}//查看信号量system("ipcs -s");int semid;//1: 创建的信号量的个数semid = semget(key, 1, IPC_CREAT|0666); //创建信号量if(semid == -1){perror("semget");}system("ipcs -s"); //查看信号量//删去信号量// 0: 代表对第0个信号量进行操作// IPC_RMID:删除信号量集semctl(semid, 0, IPC_RMID);system("ipcs -s"); //查看信号量return 0;
}
运行结果如下:
示例二:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdlib.h>
#include <stdio.h>/*解决编译出错的问题*/
#define IPC_INFO 3int main(int argc, char *argv[])
{key_t key;//创建key值key = ftok(".", 'a');if(key == -1){perror("ftok");}system("ipcs -s"); //查看信号量int semid;//1: 创建的信号量的个数semid = semget(key, 1, IPC_CREAT|0666);//创建信号量if(semid == -1){perror("semget");}system("ipcs -s"); //查看信号量struct seminfo buf;/*//struct seminfo相关成员struct seminfo {int semmap;int semmni;int semmns;int semmnu;int semmsl;int semopm;int semume;int semusz;int semvmx;int semaem;};*///IPC_INFO:获取信号量集合的限制信息。//此时函数有4个参数,第2个参数semnum不起作用。semctl(semid, 0, IPC_INFO, &buf);printf("buf.semmni = %d\n", buf.semmni);printf("buf.semmns = %d\n", buf.semmns);printf("buf.semmnu = %d\n", buf.semmnu);printf("buf.semmsl = %d\n", buf.semmsl);printf("buf.semopm = %d\n", buf.semopm);printf("buf.semume = %d\n", buf.semume);printf("buf.semusz = %d\n", buf.semusz);printf("buf.semvmx = %d\n", buf.semvmx);printf("buf.semaem = %d\n", buf.semaem);//删去信号量// 0: 代表对第0个信号量进行操作// IPC_RMID:删除信号量集semctl(semid, 0, IPC_RMID);system("ipcs -s"); //查看信号量return 0;
}
运行结果如下:
示例三:
#include <sys/types.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <stdlib.h>
#include <stdio.h>int main(int argc, char *argv[])
{key_t key;//创建key值key = ftok(".", 'a');if(key == -1){perror("ftok");}//查看信号量system("ipcs -s");int semid;//1: 创建的信号量的个数semid = semget(key, 1, IPC_CREAT|0666); //创建信号量if(semid == -1){perror("semget");}system("ipcs -s"); //查看信号量int ret;/*//SETVAL: 设置信号量的值。此时函数有4个参数。第4个参数为联合体中的val,其值为信号量的值。 union semun{int val; //信号量的值struct semid_ds *buf; //信号量集合信息unsigned short *array; //信号量值的数组struct seminfo *__buf; //信号量限制信息};*/ret = semctl(semid, 0, SETVAL, 20);if(ret == -1){perror("semctl");}//GETVAL:获取信号量的值。函数返回值即为信号量的值。ret = semctl(semid, 0, GETVAL);if(ret == -1){perror("semctl");}printf("ret = %d\n", ret);// 0: 代表对第0个信号量进行操作// IPC_RMID:删除信号量集semctl(semid, 0, IPC_RMID);system("ipcs -s");return 0;
}
运行结果如下:
示例四:
#include <sys/types.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>int main(int argc, char *argv[])
{key_t key;//创建key值key = ftok(".", 'a');if(key == -1){perror("ftok");}//查看信号量system("ipcs -s");int semid;//2: 创建的信号量的个数semid = semget(key, 2, IPC_CREAT|0666); //创建信号量if(semid == -1){perror("semget");}system("ipcs -s"); //查看信号量int ret;unsigned short sem_arry[2] = {30,20};/*//SETALL: 设置所有信号量的值。此时函数有4个参数,第2个参数semnum不起作用。第4个参数为联合体中的array,其值为用来存放所有信号量值的数组的首地址。union semun{int val; //信号量的值struct semid_ds *buf; //信号量集合信息unsigned short *array; //信号量值的数组struct seminfo *__buf; //信号量限制信息};*/ret = semctl(semid, 0, SETALL, sem_arry);if(ret == -1){perror("semctl");}bzero(sem_arry, sizeof(sem_arry));//GETALL:获取所有信号量的值。此时函数有4个参数,第2个参数semnum不起作用。第4个参数为联合体中的array,其值为用来存放所有信号量值的数组的首地址。ret = semctl(semid, 0, GETALL, sem_arry);if(ret == -1){perror("semctl");}printf("sem_arry[0] = %d\n", sem_arry[0]);printf("sem_arry[1] = %d\n", sem_arry[1]);// IPC_RMID:删除信号量集semctl(semid, 0, IPC_RMID);system("ipcs -s");return 0;
}
运行结果如下:
示例五:
//此范例使用信号量来同步共享内存的操作
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <unistd.h>
#include <sys/wait.h>#define SHM_KEY 0x33
#define SEM_KEY 0x44 union semun
{ int val; struct semid_ds *buf; unsigned short *array;
}; int P(int semid)
{ struct sembuf sb;/*//操作信号量的结构体struct sembuf{unsigned short sem_num;//信号量的序号short sem_op; //信号量的操作值short sem_flg; //信号量的操作标识}; */sb.sem_num = 0; sb.sem_op = -1;//SEM_UNDO:当进程退出后,该进程对信号量进行的操作将被撤销。sb.sem_flg = SEM_UNDO; //操作1个信号量if(semop(semid, &sb, 1) == -1){ perror("semop"); return -1; } return 0;
} int V(int semid)
{ struct sembuf sb;/*//操作信号量的结构体struct sembuf{unsigned short sem_num;//信号量的序号short sem_op; //信号量的操作值short sem_flg; //信号量的操作标识}; */sb.sem_num = 0; sb.sem_op = 1;//SEM_UNDO:当进程退出后,该进程对信号量进行的操作将被撤销。sb.sem_flg = SEM_UNDO; //操作1个信号量if(semop(semid, &sb, 1) == -1){ perror("semop"); return -1; } return 0;
} int main(int argc, char **argv)
{pid_t pid; int i, shmid, semid; int *ptr = NULL; union semun semopts; /*union semun{int val; //信号量的值struct semid_ds *buf; //信号量集合信息unsigned short *array; //信号量值的数组struct seminfo *__buf; //信号量限制信息};*///创建一块共享内存, 存一个int变量if ((shmid = shmget(SHM_KEY, sizeof(int), IPC_CREAT | 0600)) == -1) { perror("msgget"); return -1;} //将共享内存映射到进程, fork后子进程可以继承映射ptr = (int *)shmat(shmid, NULL, 0);if (ptr == (int *)-1) { perror("shmat");return -1;}*ptr = 0; //赋值为0// 创建一个信号量用来同步共享内存的操作 if ((semid = semget(SEM_KEY, 1, IPC_CREAT | 0600)) == -1) { perror("semget"); return -1;} //初始化信号量 semopts.val = 1; if (semctl(semid, 0, SETVAL, semopts) < 0) { perror("semctl"); return -1;} if ((pid = fork()) < 0) { //创建进程perror("fork");_exit(0);}else if (pid == 0){ // Child// 子进程对共享内存加1 for (i = 0; i < 100000; i++) { P(semid); (*ptr)++; V(semid); printf("child: %d\n", *ptr); } } else { //Parent// 父进程对共享内存减1 for (i = 0; i < 100000; i++) { P(semid); (*ptr)--; V(semid); printf("parent: %d\n", *ptr); } //如果子进程结束,回收其资源wait(NULL);//如果同步成功, 共享内存的值为0 printf("finally: %d\n", *ptr); } return 0;
}
运行结果如下:
【Linux系统编程】进程同步与互斥:System V 信号量相关推荐
- 判断sem信号量为零_Linux系统编程——进程同步与互斥:System V 信号量
信号量概述 信号量广泛用于进程或线程间的同步和互斥,信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问. 编程时可根据操作信号量值的结果判断是否对公共资源具有访问的权限,当信号量值大于 ...
- 嵌入式Linux系统编程学习之二十五信号量
文章目录 前言 一.System V IPC 机制:信号量 1. semget 函数 2. semop 函数 3. semctl 函数 二.Posix 有名信号量 前言 信号量与信号量集的概念如下 ...
- 1.linux系统基础笔记(互斥量、信号量)
操作系统是很多人每天必须打交道的东西,因为在你打开电脑的一刹那,随着bios自检结束,你的windows系统已经开始运行了.如果问大家操作系统是什么?可能有的人会说操作系统就是windows,就是那些 ...
- Linux系统编程【文件IO、进程、进程间通信、信号、线程、互斥】
linux系统编程 个人通过学习,手打了一份48000字的Linux系统编程的笔记,包含了[文件IO.进程.进程间通信.信号.多线程.互斥]等知识点,并给出了大量的代码案例对每个重要的知识点进行了代码 ...
- linux进程间通信:system V 信号量 生产者和消费者模型编程案例
生产者和消费者模型: 有若干个缓冲区,生产者不断向里填数据,消费者不断从中取数据 两者不冲突的前提: 缓冲区有若干个,且是固定大小,生产者和消费者各有若干个 生产者向缓冲区中填数据前需要判断缓冲区是否 ...
- linux 内核信号量与用户态信号量(system v,信号量在Linux多线程机制中的应用
[摘 要]本文以信号量原理为基础,重点阐述信号量在Linux多线程同步机制中的实现特色. [关键词]信号量:Linux:多线程:同步 1 信号量 1965年E. W. Dijkstra首次提出信号量的 ...
- 【Linux】一步一步学Linux系统编程教程汇总(暂时暂停更新......)
00. 目录 文章目录 00. 目录 01. 概述和标准 02. 文件操作 03. 进程概念 04. 进程间通信 05. 多线程 06. 信号 07. 同步与互斥 08. 高级IO 09. 其它 10 ...
- Linux系统编程笔记
文章目录 1.Linux系统编程 2.文件IO 2.1 文件描述符 2.2 open 2.3 perror 2.4 close 2.5 write 2.6 read 2.7 remove 2.8 系统 ...
- linux系统发送信号的系统调用是,linux系统编程之信号:信号发送函数sigqueue和信号安装函数sigaction...
信号发送函数sigqueue和信号安装函数sigaction sigaction函数用于改变进程接收到特定信号后的行为. sigqueue()是比较新的发送信号系统调用,主要是针对实时信号提出的(当然 ...
最新文章
- 清华大学《大数据系统基础》项目征集说明(2021年秋季)
- 这三类问题是工控系统最大的威胁
- 位操作符:位与、|位或、^异或、~求反、左移位、带符号右移位、无符号右移位...
- python编程做什么工作-学python编程语言能找什么工作
- 2.6 datetime 模块
- 字节跳动---毕业旅行问题
- P3391 【模板】文艺平衡树 fhq-treap 模板
- [转载]sql server 分布式查询
- 免费SSL申请:(各大服务商资源综合)- 免费获取
- [转载] python类内部成员的访问及外部访问(入门)
- python生成的字符画怎么查看_通过python将图片生成字符画
- 微信小程序云开发教程-WXSS入门-常用样式
- WPF-创建超链接文本
- VB.NET 基础入门
- 20行代码教你用python给证件照换底色
- Cypress总结回顾
- AMS1117稳压芯片介绍
- HEKA.FitMaster.v2.15(用来分析和测试那些通过Patchmaster或Pulse得
- x11-forwarding disabled解决办法
- SCM:新一代存储介质技术
热门文章
- 水仙花python代码_「每日一练」巧用python打印出所有的水仙花数
- 文件传送到服务器的软件,远程服务器文件传输软件
- linux注册函数机制,Linux可信计算机制模块详细分析之函数实现机制(1)字符设备驱动...
- MATLAB函数gensurf,matlab模糊逻辑(二)
- mysql左连接去重查询_MYSQL-多表查询
- BusinessSkinForm使用
- Ex 2_5 求解递推式..._第三次作业
- JS时间戳格式化日期时间
- Oracle导入导出数据
- swoole+redis(websocket聊天室demo)