博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
进程间通信方式--信号量semaphore
阅读量:3957 次
发布时间:2019-05-24

本文共 6073 字,大约阅读时间需要 20 分钟。

信号量semaphore


定义


信号量主要是用来保护共享资源,使得资源在一个时刻只有一个进程(线程)所拥有。

进程可以根据它判定是否能够访问某些共享资源,同时,进程也可以修改该标志。除了用于访问控制外,还可用于进程同步。
信号量有以下两种类型:
二值信号量: 最简单的信号量形式,信号量的值只能取0或1,类似于互斥锁。 注:二值信号量能够实现互斥锁的功能,但两者的关注内容不同。信号量强调共享资源,只要共享资源可用,其他进程同样可以修改信号量的值;互斥锁更强调进程,占用资源的进程使用完资源后,必须由进程本身来解锁。
计算信号量:信号量的值可以取任意非负值(当然受内核本身的约束)。
和消息队列一样,通过struct kern_ipc_perm的指针可以找到相应的信号量,在
System V IPC
信号量中,我们的每一个条目为一个信号量,定义在/include/linux/sem.h:

struct sem_array {
struct kern_ipc_perm ____cacheline_aligned_in_smp sem_perm; /* permissions .. see ipc.h */ time_t sem_otime; /* last semop time */ time_t sem_ctime; /* last change time */ struct sem *sem_base; /* ptr to first semaphore in array */ struct list_head sem_pending; /* pending operations to be processed */ struct list_head list_id; /* undo requests on this array */ int sem_nsems; /* no. of semaphores in array */ int complex_count; /* pending complex operations */};

其中struct sem *sem_base为信号量列表的头指针。struct sem是一个简单的数据结构:

struct sem {
int semval; /* current value */ int sempid; /* pid of last operation */ struct list_head sem_pending; /* pending single-sop operations */};

它维持一个当前值,最后操作的进程ID以及一个阻塞队列。在用户空间可以通过struct sembufsem中的信号量的值进行改变**(SETVAL操作)** 或者通过通过联合体union semun 对整个信号量进行改变 (IPC_STAT SETVAL等操作) ,两个结构分别如下:

/*semop system calls takes an array of these. */struct sembuf {
unsigned short sem_num; /* semaphore index in array */ short sem_op; /* semaphore operation */ short sem_flg; /* operation flags */};/* arg for semctl system calls. */union semun {
int val; /* value for SETVAL */ struct semid_ds __user *buf; /* buffer for IPC_STAT & IPC_SET */ unsigned short __user *array; /* array for GETALL & SETALL */ struct seminfo __user *__buf; /* buffer for IPC_INFO */ void __user *__pad;};

include/linux/ipc.h文件中定义相应的操作:

操作 含义
#define SEMOP 1 改变信号量的值
#define SEMGET 2 打开或者创建一个信号量
#define SEMCTL 3 消息量控制
#define SEMTIMEDOP 4 好像是内部使用吧,没有仔细去看

信号量操作函数


信号量主要是实现进程间共享资源访问的控制。

对信号量的操作主要有P()V(),假设sv是一个信号量变量:

  • P(sv):如果sv的值大于0,就减1,如果sv的值等于0,则挂起进程的执行。
  • V(sv):如果有其他进程因为等待 sv而挂起,就让它恢复运行;如果没有因sv等待而挂起的进程,就对该信号量进行加1操作。
    对信号量的操作有:
操作 含义
SEMOP 改变信号量的值
SEMGET 打开或者创建一个信号量
SEMCTL 信号量控制

对信号量的操作流程是:

SEMGET —> SEMOP(-1) —> (操作临界区) —> SEMOP(+1)(SEMCTL删除或者修改信号量参数等)

创建或打开信号量


#include 
#include
#include
int semget(key_t key, int nsems, int semflg);

参数1

key是一个键值,唯一标识一个信号灯集,用法与msgget()中相同。

参数2

nsems指定打开或者新创建的信号灯集中将包含信号量的数目,一般情况下,都是取值为1。

参数3

semflg是一些标志位。参数keysemflg的取值,以及何时打开已有信号灯集或者创建一个新的信号灯集与msgget()中的对应部分相同,不再祥述。该调用返回与健值key相对应的信号灯集描述字。

返回:成功返回信号灯集描述字semid,否则返回-1。

对信号量进行PV操作

int semop(int semid, struct sembuf *sops, unsigned nsops);

参数1

semidsemget()返回的信号量描述符

参数2

如果nsems参数不为1,此时semid指向的是一个信号量集,而不是单独的一个信号量。因此每次对该信号集进行操作时候必须指定需要操作的信号量数目,即nsops大小。struct sembuf *sops指向的是一个struct sembuf结构体数组,数组大小即为nsops

如果我们的信号量集只有一个信号量,此时,nsops=1,我们的sops就直接指向一个struct sembuf类型的指针。
struct sembuf数据结构:

struct sembuf {
unsigned short sem_num; /* semaphore index in array */ short sem_op; /* semaphore operation */ short sem_flg; /* operation flags */};

semnum: 当前需要操作的信号量在信号集中编号,从0开始

sem_flg: IPC_NOWAITSEM_UNDO,如果设置了SEM_UNDO标志,那么在进程结束时,相应的操作将被取消,这是比较重要的一个标志位。

sem_op: PV操作,其值为正,加到现有的信号内含值。通常用于释放所控资源的使用权;值为负,而其绝对值又大于信号的现值,操作将会阻塞,直到信号值大于或等于sem_op的绝对值。通常用于获取资源的使用权;如果sem_op的值为0,则操作将暂时阻塞,直到信号的值变为0。

信号量控制函数


int semctl(int semid, int semnum, int cmd, ...);

semnum为需要控制的信号量在信号集中的编号,如果信号集只有一个元素,该值为0。cmd为控制类型,对于有些操作,需要第四个参数,即为一个union semun联合体,根据cmd不同,使用联合体中不同的字段:

union semun {
int val; /* value for SETVAL */ struct semid_ds __user *buf; /* buffer for IPC_STAT & IPC_SET */ unsigned short __user *array; /* array for GETALL & SETALL */ struct seminfo __user *__buf; /* buffer for IPC_INFO */ void __user *__pad;};
cmd 描述
SETVAL 用于把信号量初始化为一个已知的值,用于第一次使用该信号量时,完成信号量值的初始化。此时使用的是union semun 中val字段。
IPC_RMID 用于删除已经不再继续使用的信号量标识符,该操作会解除所有在该信号量上的挂起进程。
IPC_STAT/IPC_SET 和消息队列相似。

信号量实例


实现字符成对打印

#include 
#include
#include
#include
//包含信号量定义的头文件 //联合类型semun定义union semun{
int val; struct semid_ds *buf; unsigned short *array;}; //函数声明//函数:设置信号量的值static int set_semvalue(void);//函数:删除信号量static void del_semvalue(void);//函数:信号量P操作static int semaphore_p(void);//函数:信号量V操作static int semaphore_v(void); static int sem_id;//信号量ID int main(int argc,char *argv[]){
int i; int pause_time; char op_char = 'O'; srand((unsigned int)getpid()); //创建一个新的信号量或者是取得一个已有信号量的键 sem_id = semget((key_t)1234,1,0666 | IPC_CREAT); //如果参数数量大于1,则这个程序负责创建信号和删除信号量 if(argc > 1) {
if(!set_semvalue()) {
fprintf(stderr,"failed to initialize semaphore\n"); exit(EXIT_FAILURE); } op_char = 'X';//对进程进行标记 sleep(5); } //循环:访问临界区 for(i = 0;i < 10;++i) {
//P操作,尝试进入缓冲区 if(!semaphore_p()) exit(EXIT_FAILURE); printf("%c",op_char); fflush(stdout);//刷新标准输出缓冲区,把输出缓冲区里的东西打印到标准输出设备上 pause_time = rand() % 3; sleep(pause_time); printf("%c",op_char); fflush(stdout); //V操作,尝试离开缓冲区 if(!semaphore_v()) exit(EXIT_FAILURE); pause_time = rand() % 2; sleep(pause_time); } printf("\n %d - finished \n",getpid()); if(argc > 1) {
sleep(10); del_semvalue();//删除信号量 }} //函数:设置信号量的值static int set_semvalue(void){
union semun sem_union; sem_union.val = 1; if(semctl(sem_id,0,SETVAL,sem_union)) return 0; return 1;} //函数:删除信号量static void del_semvalue(void){
union semun sem_union; if(semctl(sem_id,0,IPC_RMID,sem_union)) fprintf(stderr,"Failed to delete semaphore\n");} //函数:信号量P操作:对信号量进行减一操作static int semaphore_p(void){
struct sembuf sem_b; sem_b.sem_num = 0;//信号量编号 sem_b.sem_op = -1;//P操作 sem_b.sem_flg = SEM_UNDO; if(semop(sem_id,&sem_b,1) == -1) {
fprintf(stderr,"semaphore_p failed\n"); return 0; } return 1;} //函数:信号量V操作:对信号量进行加一操作static int semaphore_v(void){
struct sembuf sem_b; sem_b.sem_num = 0;//信号量编号 sem_b.sem_op = 1;//V操作 sem_b.sem_flg = SEM_UNDO; if(semop(sem_id,&sem_b,1) == -1) {
fprintf(stderr,"semaphore_v failed\n"); return 0; } return 1; }

运行结果

在这里插入图片描述

转载地址:http://wnvzi.baihongyu.com/

你可能感兴趣的文章
2017新生儿爆款名字出炉!90后的父母们最受欢迎的居然是.....
查看>>
全景图解高铁数据,谁是最有潜力的高铁城市?
查看>>
张小龙现场“约战”跳一跳,发布2018微信全新计划(内附演讲全文)
查看>>
爬取电影天堂的最新电影
查看>>
运维总监不会告诉你这些有趣但鲜为人知的 Linux 命令
查看>>
2017新浪微整形年度大数据报告
查看>>
实战 | 用 Python 选股票,据说可以多挣个20%
查看>>
重磅 | 数据挖掘之父韩家炜:文本语料库的数据挖掘(附视频+PPT下载)
查看>>
白话AI:看懂深度学习真的那么难吗?初中数学,就用10分钟
查看>>
超全的 Linux 机器的渗透测试命令备忘表,共16表128条命令
查看>>
教你用Java来玩答题(百万英雄/冲刺大会等)
查看>>
用Python做跳一跳外挂太浪费了,这种技能让你目瞪口呆
查看>>
如何在Python中快速进行语料库搜索:近似最近邻算法
查看>>
快收藏! 30 分钟包你学会 AWK
查看>>
各平台的推荐算法,太贴切了!
查看>>
一张图学会Python3
查看>>
500款各领域机器学习数据集,总有一个是你要找的
查看>>
2017年终奖调查出炉 程序员年终奖多少你绝对猜不到
查看>>
使用 Charles 抓取 app 数据包
查看>>
未来,改变世界的将是这些......
查看>>