上节学习了进程间利用共享内存,进行通信。利用共享内存通信非常方便,高效,每个进程都可往共享内存里读写数据,但是并发的进程间的执行顺序是我们无法控制的,这样就可能出现诸如在读取信息之前覆盖内存空间等竞争条件。我们就需要一种机制来协调我们并发进程访问共享内存,以避免竞争状态的出现。
Linux提供一个用于进程同步的信号量(进程信号量或称为System V信号量)。
包含信号量操作函数声明以及一些宏定义的头文件:
- 与用于创建,销毁共享内存的shmget和shmctl类似,系统调用semget和semctl负责创建,释放信号量。
key参数、semflg参数与shmget里的第一个、第三个参数意思是一样的。
nsems参数是指定需要的信号量的数目。我们自己经常只是用到一个信号量,所以它的值几乎总是1。
当通过指定正确的键值来获取已存在的信号量的标识符是,可以把 semns 的值设为 0。
该函数调用成功返回 信号量 标识符,失败返回-1。
- semctl 信号量管理函数
semctl 可以用于对信号量的初始化设置,释放删除,获取信号信息、当前的值等其他管理功能。
semid 是由semget返回的信号量标识符。
sem_num 上面说过semget函数的第二个参数是指定创建的信号量的数目,也就是一组信号量,所以对信号量的操作分为对单个的操作和对整个信号量组进行操作,不同的操作sem_num参数所代表的意思是不一样的。
对单个信号量进行操作时,sem_num表示对第几个信号量进行操作,0表示信号量组里第一个信号量(与数组一样从0开始)。
一般我们只是用到一个信号量,信号量组里只有一个信号量,sem_num的值可以是0,即对信号量组第0个信号量管理,表示这是第一个也是唯一个信号量。
对信号量组进行操作时,sem_num表示这个组里信号量的数目。
我们只创建了一个信号量时,用semctl函数操作信号时,sem_num表示数目时,值就是1。
第四个参数是union semun的结构:(自己定义,不过sem.h里有这个联合体的定义)
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
};
cmd参数是指定对信号量进行什么操作。
IPC_RMID 删除信号量(对于删除信号量操作,一般可以不指定第四个参数了)。
SETVAL 通过设置union semun中的val成员来初始化,信号量的值。 这时sem_num参数的值表示的意思是第几个信号量。
GETVAL 获取第sem_num信号量的值,信号量的值是通过函数返回的(第四个参数可以不指定,即可以不写)。
SETALL 通过union semun中array所指的数组初始化有sem_num个信号量的信号量组。
IPC_STAT IPC_SET 通过union semum里buf……获得或设置信号量属性信息。
semctl函数,不同的操作返回值是不一样的,但像设置、删除调用成功返回0,失败返回-1。
- 信号量的 P V 操作
进程信号量的 PV操作时通过调用一个函数实现,具体是什么操作是由
结构体struct sembuf里的成员决定的。
struct sembuf
{
short sem_num;
short sem_op;
short sem_flg;
};
sem_num信号的编号,信号量组只有一个信号量时,它的总是0;
sem_op 1 表示 P 操作, -1 表示 V 操作。
sem_flg 通常被设置成SEM_UNDO……
参数num_sem_ops 是表示第二个参数sem_ops数组里元素的个数。
- 信号量通信简单例子: