Linux—进程间通信和同步(信号量)

信号量

信号量是一种计数器,用来控制对多个进程共享的资源所进行的访问。它们常常被用做一个锁机制在某个进程正在对特定资源进行操作时,信号量可以防止另一个进程去访问它。生产者和消费者的模型是信号量的典型使用。

1.  信号量数据结构

信号量数据结构是信号量程序设计中经常使用的数据结构,由于在之后的函数中经常用到,这里将结构的原型列出来,便于读者查找。

union semun            /*信号量操作的联合结构*/

{

       int               val;    /*整型变量*/

       struct semid_ds    *buf;        /*semid_ds结构指针*/

       unsigned short    *array;  /*数组类型*/

       struct seminfo    *_buf;   /*信号量内部结构*/

}

2.  新建信号量函数semget()

semget()函数用于创建一个新的信号量集合,或者访问现有的集合

其原型如下

参数keyftok生成的键值;

参数nsems可以指定在新的集合中应该创建的信号量的数目

参数semflg打开信号量的方式

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/sem.h>

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

semflg是打开信号量的方式:

IPC_CREAT如果内核中不存在这样的信号量集合,则把它创建出来

IPC_EXCL当与IPC_CREAT—起使用时,如果信号量集合早已存在,则操作将失败

如果单独使用IPC_CREAT,semget()或者返回新创建的信号量集合的信号量集合标识符;或者返回早已存在的具有同一个关键字值的集合的标识符。

如果同时使用IPC_EXCL和IPC_CREAT,那么将有两种可能的结果:如果集合不存在,则创建一个新的集合;如果集合早已存在,则调用失败,并返回-1。

IPC_EXCL 本身是没有什么用处的,但当与IPC_CREAT组合使用时,它可以用于防止为了访问而打开现有的信号量集合。

利用semget()函数包装建立信号量的代码如下:

CreateSem()函数按照用户的键值生成一个信号量,把信号量的初始值设为用户输入的value。

3. 信号量操作函数semop()

信号量的P、V操作是通过向己经建立好的信号量(使用semget()函数),发送命令来完成的。向信号量发送命令的函数是semop(),这个函数的原型如下:

#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/sem.h>

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

semop()函数中第2个参数(sops)是一个指针,指向将要在信号量集合上执行操作的一个数组,而第3个参数(nsops)则是该数组中操作的个数。sops参数指向的是类型为sembuf结构的一个数组。sembuf结构是在linux/sem.h中定义的,如下所示:

struct sembuf

{

       ushort  sem_num;        //信号量的编号

       short   sem_op;                   //信号量的操作

       short   sem_flg;                   //信号量的操作标志

}

□   sem_num:用户要处理的信号量的编号。

□   sem_op:将要执行的操作(正、负、或者零)。

□   sem_flg:信号量操作的标志。如果sem_op为负,则从信号量中减掉一个值。如果 sem_op为正,则从信号量中加上值。如果sem_op为0,则将进程设置为睡眠状态, 直到信号量的值为0为止。

例如“struct sembuf sem={0, +1,NOWAIT};”表示对信号量0,进行加1的操作。 用函数semop()可以构建基本的P、V操作,代码如下所示。Sem_P构建{0, +1,NOWAIT}的sembuf结构来进行增加1个信号量值的操作;Sem_V构建{0, -1, NOWAIT}的sembuf结构来进行减少1个信号量的操作,所对应的信号量由函数传入(semid)。

4. 控制信号量参数semctl()函数

与文件操作的ioctl()函数类似,信号量的其他操作是通过函数semctl()来完成的。函数semctl()的原型如下:

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/sem.h>

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

函数semctI()用于在信号量集合上执行控制操作这个调用类似于函数msgctl()msgctl()函数是用于消息队列上的操作

semctI()函数的第1个参数是关键字的值(在我们的例子中它是调用semget()函数所返回的值)。

第2个参数(semun)是将要执行操作的信号量的编号,它是信号量集合的一个索引值,对于集合中的第1个信号量(有可能只有这一个信号量)来说,它的索引值将是一个为0的值。

cmd参数代表将要在集合上执行的命令。其取值如下所述

IPC_STAT:获取某个集合的semid_ds结构,并把它存储在semun联合体的buf参数所指定的地址中。

IPC_SET:设置某个集合的semid_ds结构的ipc_perm成员的值。该命令所取的值是从semun联合体的buf参数中取到的。

IPC_RMID:从内核删除该集合。

GETALL:用于获取集合中所有信号量的值。整数值存放在无符号短整数的一个数组中,该数组由联合体的array成员所指定。

GETNCNT:返回当前正在等待资源的进程的数目。

GETPID:返回最后一次执行semop调用的进程的PID。

GETVAL:返回集合中某个信号量的值。

GETZCNT:返回正在等待资源利用率达到百分之百的进程的数目。

SETALL:把集合中所有信号量的值,设置为联合体的array成员所包含的对应值。

SETVAL:把集合中单个信号量的值设置为联合体的val成员的值。

参数arg代表类型semun的一个实例。这个特殊的联合体是在Linux/Sem.h中定义的, 如下所示

val:当执行SETVAL命令时将用到这个成员,它用于指定要把信号量设置成什么值。

buf:在命令IPC_STAT/IPC_SET中使用。它代表内核中所使用的内部信号量数据结构的一个复制。

array:用在GETALL/SETALL命令中的一个指针。它应当指向整数值的一个数组。在设置或获取集合中所有信号量的值的过程中,将会用到该数组。

剩下的参数_buf和_pad将在内核中的信号量代码的内部使用,对于应用程序开发人员来说,它们用处很少,或者说没有用处。这两个参数是Linux操作系统所特有的,在其他的UNIX实现中没有。

利用semctl()函数设置和获得信号量的值构建通用的函数:

SetvalueSem()函数设置信号量的值,它是通过SETVAL命令实现的,所设置的值通过联合变量sem的val域实现。GetvalueSem()函数用于获得信号量的值,semctl()函数的命令GETVAL会使其返回给定信号量的当前值。当然,销毁信号量同样可以使用semctl()函数实现。

命令IPC_RMID将给定的信号量销毁。

5. 一个信号量操作的例子

在之前的信号量函数的基础上,进行了单进程的信号量程序模拟。下面的代码先建立一个信号量,然后对这个信号量进行P、V操作,并将信号量的值打印出来,最后销毁信号量。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值