进程间通信——信号量

来源:互联网 发布:张靓颖是冯轲的m知乎 编辑:程序博客网 时间:2024/06/03 13:47

在说信号量之前我们再来复习一下几个概念:

1、把两个或多个执行流所能访问的同一块内存资源叫做临界资源

2、把访问临界资源的代码叫做临界区

3、在任意时刻只有一个执行流可以访问临界区,这种现象叫做互斥

4、一件事,要么做了,要么没有做,这叫做原子性

现在我们来看下什么事信号量

信号量:信号量的本质是一种数据操作锁(计数器)、用来负责数据操作过程中的互斥、同步等功能。

那么为什么要使用信号量呢

使用信号量为了防止多个进程在访问临界资源为引发的问题。我们需要在任一时刻只能有一个执行线程访问代码的临界区域。临界区域是指执行数据更新的代码需要独占式地执行。信号量就可以提供这样的一种访问机制,让一个临界区同一时间只有一个线程在访问它, 也就是说信号量是用来调协进程对共享资源的访问的。

创建信号量用的函数是:

int semget(key_t key, int num_sems, int sem_flags); 

第一个参数key是整数值(唯一非零),不相关的进程可以通过它访问一个信号量,它代表程序可能要使用的某个资源,程序对所有信号量的访问都是间接的,程序先通过调用semget函数并提供一个键,再由系统生成一个相应的信号标识符(semget函数的返回值),只有semget函数才直接使用信号量键,所有其他的信号量函数使用由semget函数返回的信号量标识符。如果多个程序使用相同的key值,key将负责协调工作。

第二个参数num_sems指定需要的信号量数目,它的值几乎总是1。

第三个参数sem_flags是一组标志,当想要当信号量不存在时创建一个新的信号量,可以和值IPC_CREAT做按位或操作。设置了IPC_CREAT标志后,即使给出的键是一个已有信号量的键,也不会产生错误。而IPC_CREAT | IPC_EXCL则可以创建一个新的,唯一的信号量,如果信号量已存在,返回一个错误。

除了这个函数,还有一些其它的函数

//改变信号量的值int semop(int sem_id, struct sembuf *sem_opa, size_t num_sem_ops);  

其中:

struct sembuf{      short sem_num;//除非使用一组信号量,否则它为0      short sem_op;//信号量在一次操作中需要改变的数据,通常是两个数,                 //一个是-1,即P(等待)操作,一个是+1,即V(发送信号)操作。      short sem_flg;//通常为SEM_UNDO,使操作系统跟踪信号,                    //并在进程没有释放该信号量而终止时,操作系统释放信号量  };
//控制信号量信息int semctl(int sem_id, int sem_num, int command, ...);

如果它有第四个参数,那么是:

union semun{      int val;      struct semid_ds *buf;      unsigned short *arry;  }; 

SEM_UNDO

当操作信号量semop函数时,sem_flg可以设置SEM_UNDO标识;SEM_UNDO用于将修改的信号量值在进程正常退出(调用exit退出或main执行完)或异常退出(如段异常、除0异常、收到KILL信号等)时归还给信号量。

下面我们就来用代码实现信号量

这里写图片描述

  //comm.c  1 #include"comm.h"  2   3 int CommSemSet(int flags,int nums)  4 {  5     key_t _key=ftok(PATHNAME,PROJ_ID);  6     if(_key<0)  7     {  8         perror("ftok");  9         sleep(3); 10         return -1; 11     } 12  13     int semid=semget(_key,nums,flags); 14     if(semid<0) 15     { 16         perror("semget"); 17         sleep(3); 18         return -2; 19     } 20     return semid; 21 } 22  23 int CreateSemSet(int nums) 24 { 25     return CommSemSet(IPC_CREAT | IPC_EXCL | 0666, nums); 26 } 27  28 int GetSemSet() 29 { 30     return CommSemSet(0,0); 31 } 32  33 int InitSemSet(int semid, int nums) 34 { 35     union SemNo _sn; 36     _sn.val=1; 37     if(semctl(semid,nums,SETVAL,_sn)<0) 38     { 39         perror("semctl"); 40         sleep(3); 41         return -1; 42     } 43     return 0; 44 } 45  46 int CommPV(int semid,int nums,int flags) 47 { 48     struct sembuf _s[1]; 49     _s[0].sem_op=flags; 50     _s[0].sem_num=nums; 51     if(semop(semid,_s,1)<0) 52     { 53         perror("semop"); 54         sleep(3); 55         return -1; 56     } 57     return 0; 58 } 59  60 int P(int semid) 61 { 62     return CommPV(semid,0,-1); 63 } 64 int V(int semid) 65 { 66     return CommPV(semid,0,1); 67 } 68 int DestorySemSet(int semid) 69 { 70     if(semctl(semid,0,IPC_RMID)<0) 71     { 72         perror("semctl"); 73         sleep(3); 74         return -1; 75     } 76     return 0; 77 }

这里写图片描述

结果:

这里写图片描述

查看和删除信号量的命令:

ipcs -s              //查看信号量ipcrm -s             //删除信号量

最后在强调一点:信号量虽然是进程间通信的一种,但信号量不以通信为主要目的,它以保护临界资源为主要目的