赞
踩
目录
信号量(semaphore) 与已经介绍过的 PC 结构不同,它是一个计数器。信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据。
1.信号量用于进程间同步,若要在进程间传递数据需要结合共享内存
2.信号量基于操作系统的 PV 操作,程序对信号量的操作都是原子操作
PV操作 | 一种实现进程互斥与同步的有效方法。PV操作与信号量的处理相关,P表示通过的意思,V表示释放的意思 |
原子操作 | 指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch(切换到另一个线程) |
3.每次对信号量的 PV 操作不仅限于对信号量值加 1 或减 1,而且可以加减任意正整数
4.支持信号量组
最简单的信号量是只能取 0和 1的变量,这也是信号量最常见的一种形式,叫做二值信号量 Binary Semaphore) 。而可以取多个正整数的信号量被称为通用信号量。
Linux 下的信号量函数都是在通用的信号量数组上进行操作,而不是在一个单一的二值信号量上进行操作。
创建或获取一个信号量组:若成功返回信号量标识符ID,失败返回-1。
int semget(key_t key, int nsems, int semflg);
key | 所创建或打开信号量集的键值 |
nsems | 信号量的个数,通常为1;如果是引用一个现有的集合,则将nsems指定为 0,该参数只在创建信号量集时有效 |
semflg | 调用函数的操作类型,也可用于设置信号量集的访问权限,两者通过 | 表示 |
控制信号量的相关信息。
int semctl(int semid, int semnum, int cmd, ...);
semid | semget函数返回的信号量标识符ID |
semnum | 操作信号在信号集中的编号,这里是以数组为单位的,所以第一个单位是数组中的第一个,即为0 |
cmd | 对信号量进行相关操作 |
在semctl函数中的cmd有多种,这里就说两个常用的:
SETVAL | 用于初始化信号量为一个已知的值。所需要的值作为联合体semun的val成员来传递,在信号量第一次使用之前需要设置信号量 |
IPC_RMID | 删除一个信号量集合。如果不删除信号量,它将继续在系统中存在,即使程序已经退出,它可能在你下次运行此程序时引发问题,而且信号量是一种有限的资源 |
如果有第四个参数,它通常是联合体union semun,定义格式如下:
- union semun
- {
- int val; /* Value for SETVAL */
- struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
- unsigned short *array; /* Array for GETALL, SETALL */
- struct seminfo *__buf; /* Buffer for IPC_INFO
- (Linux-specific) */
- };
对信号量组进行操作,改变信号量的值:成功返回0,失败返回-1。
int semop(int semid, struct sembuf *sops, unsigned nsops);
semid | semget函数返回的信号量标识符ID |
*sops | 通常是一个结构体数组,但信号量理论上一次只能执行一个,所以在定义该结构体时,不需要以数组格式定义 |
nsops | 信号操作结构的数量,恒大于或等于1 |
*sops结构体定义格式如下:
- struct sembuf
- {
- short sem_num; // 信号量的编号,默认为0
- short sem_op; // 信号量在一次操作中需要改变的数据,通常是两个数,一个是-1,即P(等待)操作,
- // 一个是+1,即V(发送信号)操作。
- short sem_flg; // 通常为SEM_UNDO,使操作系统跟踪信号,当进程结束之前,取消对信号量的任何操作
- };
- #include <stdio.h>
- #include <sys/types.h>
- #include <sys/ipc.h>
- #include <sys/sem.h>
- //模拟用钥匙开锁场景
-
- union semun//定义一个联合体
- {
- int val; /* Value for SETVAL */
- struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
- unsigned short *array; /* Array for GETALL, SETALL */
- struct seminfo *__buf; /* Buffer for IPC_INFO*/
- };
-
- void pGetKey(int semid)
- {
- struct sembuf set;//定义一个结构体
- set.sem_num = 0;//第一个信号量
- set.sem_op = -1;//拿走钥匙 数量-1
- set.sem_flg = SEM_UNDO;//拿不到钥匙就等待
- semop(semid,&set,1);//第二个参数为指针,这里需要以地址形式写入
- printf("get the key\n");
- }
-
- void vPutBackKey(int semid)
- {
- struct sembuf put;
- put.sem_num = 0;
- put.sem_op = +1;//放回钥匙 数量+1
- put.sem_flg = SEM_UNDO;
- semop(semid,&put,1);
- printf("put back the key\n");
- }
-
- int main()
- {
- int semid;
- int pid;
-
- key_t key;
- key = ftok(".",1);
-
- semid = semget(key,1,IPC_CREAT|0666);//以可读可写权限创建信号量
-
- union semun initsem;//调用联合体:初识化钥匙数量
- initsem.val = 0;//一开始钥匙数量为0
-
- semctl(semid,0,SETVAL,initsem);
-
- pid = fork();
- if(pid > 0)
- {
- pGetKey(semid);//拿钥匙 但钥匙数量为0 需等待钥匙放回 即等待子进程放回钥匙
- printf("this is father\n");
- vPutBackKey(semid);//拿到钥匙后使用完毕放回钥匙
- }
- else if(pid == 0)
- {
- printf("this is child\n");
- vPutBackKey(semid);//将钥匙放回,钥匙数量+1,父进程检测到有钥匙就拿钥匙,即先进入子进程再进入父进程
- }
- else
- printf("error!\n");
-
- return 0;
- }
可见,一开始没有钥匙,父进程中没有任何操作,当子进程中将钥匙放回时,父进程检测到后拿到钥匙使用完毕后并放回。实现信号量的检测。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。