XSI进程间通信---信号量

来源:互联网 发布:2015年各省地税数据 编辑:程序博客网 时间:2024/05/18 12:44

1. 基本特点
 1) 相当于计数器,用于限制多个进程对有限共享资源的访问。
 2) 多个进程获取有限共享资源的操作模式
   A. 测试控制该资源的信号量;
   B. 若信号量大于0,则进程可以使用该资源, 为了表示此进程已获得该资源,需将信号量减1;
   C. 若信号量等于0,则进程休眠等待该资源, 直到信号量大于0,进程被唤醒,执行步骤A;
   D. 当某进程不再使用该资源时,信号量增1,  正在休眠等待该资源的其它进程将被唤醒。
 3)内核维护一个semid_ds结构体

struct semid_ds {    struct ipc_perm sem_perm;  // 权限信息    time_t          sem_otime; // 上次执行 semop 的时间    time_t          sem_ctime; // 最后更新时间    unsigned short  sem_nsems; // 在信号量集合里的索引};
struct ipc_perm {    key_t          __key; // 键值    uid_t          uid;   // 有效属主ID    gid_t          gid;   // 有效属组ID    uid_t          cuid;  // 有效创建者ID    gid_t          cgid;  // 有效创建组ID    unsigned short mode;  // 权限字    unsigned short __seq; // 序列号};

2. 常用函数

1) 创建/获取信号量

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

A. 该函数以key参数为键值创建一个信号量集合  (nsems参数表示集合中的信号量数 ,或获取已有的信号量集合(nsems0)

B. semflg取值:
  0         - 获取,不存在即失败。
  IPC_CREAT - 创建,不存在即创建,已存在即获取,除非...
  IPC_EXCL  - 排斥,已存在即失败。

C. 成功返回信号量集合标识semid,失败返回-1。

2) 操作信号量:它的作用是改变信号量的值

int semop (int semid, struct sembuf* sops,unsigned nsops);
struct sembuf {    unsigned short sem_num; // 信号量下标    short          sem_op;  // 操作数    short          sem_flg; // 操作标记};

A. 该函数对semid参数所标识的信号量集合中, 由sops参数所指向的包含nsops个元素的, 结构体数组中的每个元素,依次执行如下操作:

   a) sem_op大于0 则将其加到第sem_num个信号量的计数值上,以表示对资源的释放;

   b) sem_op小于0则从第sem_num个信号量的计数值中减去其绝对值,以表示对资源的获取;

   c) 若第sem_num个信号量的计数值不够减(信号量不能为负) 则此函数会阻塞,直到该信号量够减为止,以表示对资源的等待;

   d) sem_flg包含IPC_NOWAIT位,则当第sem_num个信号量的计数值不够减时, 此函数不会阻塞,而是返回-1errnoEAGAIN以便在等待资源的同时还可做其它处理;

   e) sem_op等于0则直到第sem_num个信号量的计数值为0时才返回, 除非sem_flg包含IPC_NOWAIT位。

B. 成功返回0,失败返回-1


3) 销毁/控制信号量

int semctl (int semid, int semnum, int cmd);int semctl (int semid, int semnum, int cmd,union semun arg);

函数描述:semctl() 在 semid 标识的信号量集上,或者该集合的第 semnum 个信号量上执行 cmd 指定的控制命令。(信号量集合索引起始于零。)

A. senum :信号集的索引,用来存取信号集内的某个信号

union semun {          int val;               // SETVAL使用的值            struct semid_ds *buf;  // IPC_STAT、IPC_SET 使用缓存区         unsigned short *array; // GETALL,、SETALL 使用的数组          struct seminfo *__buf; // IPC_INFO(Linux特有) 使用缓存区 }; 

B. cmd取值:
   IPC_STAT - 获取信号量集合的属性,通过arg.buf输出。
   IPC_SET  - 设置信号量集合的属性,通过arg.buf输入,仅三个属性可设置
   IPC_RMID - 立即删除信号量集合。  此时所有阻塞在对该信号量集合的,semop函数调用,都会立即返回失败,errno为EIDRM。
   GETALL   - 获取信号量集合中每个信号量的计数值,  通过arg.array输出。
   SETALL   - 设置信号量集合中每个信号量的计数值,通过arg.array输入。
   GETVAL   - 获取信号量集合中, 第semnum个信号量的计数值, 通过返回值输出。
   SETVAL   - 设置信号量集合中,第semnum个信号量的计数值, 通过arg.val输入。

   注意:只有针对信号量集合中具体某个信号量的操作,才会使用semnum参数。针对整个信号量集合的操作,会忽略semnum参数。
C. 成功返回值因cmd而异,失败返回-1。


3. 编程模型


这里模拟一个简单的借书还书的案例:

#include <stdio.h>#include <errno.h>#include <sys/sem.h>int pleft (int semid) {int val = semctl (semid, 0, GETVAL);if (val == -1) {perror ("semctl");return -1;}printf ("还剩%d册。\n", val);return 0;}int main (void) {printf ("创建信号量...\n");key_t key = ftok (".", 100);if (key == -1) {perror ("ftok");return -1;}int semid = semget (key, 1, 0644 | IPC_CREAT | IPC_EXCL);if (semid == -1) {perror ("semget");return -1;}printf ("初始信号量...\n");if (semctl (semid, 0, SETVAL, 5) == -1) {perror ("semctl");return -1;}int quit = 0;while (! quit) {printf ("--------\n");printf ("三国演义\n");printf ("--------\n");printf ("[1] 借阅\n");printf ("[2] 归还\n");printf ("[0] 退出\n");printf ("--------\n");printf ("请选择:");int sel = -1;scanf ("%d", &sel);switch (sel) {case 0:quit = 1;break;case 1: {//printf ("请稍候...\n");struct sembuf sops = {0, -1, /*0*/IPC_NOWAIT};if (semop (semid, &sops, 1) == -1) {if (errno == EAGAIN) {printf ("暂时无书,下回再试。\n");break;}else {perror ("semop");return -1;}}printf ("恭喜恭喜,借阅成功。\n");pleft (semid);break;}case 2: {struct sembuf sops = {0, 1, 0};if (semop (semid, &sops, 1) == -1) {perror ("semop");return -1;}printf ("好借好还,再借不难。\n");pleft (semid);break;}default:printf ("无效选择!\n");scanf ("%*[^\n]");scanf ("%*c");break;}}printf ("销毁信号量...\n");if (semctl (semid, 0, IPC_RMID) == -1) {perror ("semctl");return -1;}printf ("大功告成!\n");return 0;}
#include <stdio.h>#include <errno.h>#include <sys/sem.h>int pleft (int semid) {int val = semctl (semid, 0, GETVAL);if (val == -1) {perror ("semctl");return -1;}printf ("还剩%d册。\n", val);return 0;}int main (void) {printf ("获取信号量...\n");key_t key = ftok (".", 100);if (key == -1) {perror ("ftok");return -1;}int semid = semget (key, 0, 0);if (semid == -1) {perror ("semget");return -1;}int quit = 0;while (! quit) {printf ("--------\n");printf ("三国演义\n");printf ("--------\n");printf ("[1] 借阅\n");printf ("[2] 归还\n");printf ("[0] 退出\n");printf ("--------\n");printf ("请选择:");int sel = -1;scanf ("%d", &sel);switch (sel) {case 0:quit = 1;break;case 1: {//printf ("请稍候...\n");struct sembuf sops = {0, -1, /*0*/IPC_NOWAIT};if (semop (semid, &sops, 1) == -1) {if (errno == EAGAIN) {printf ("暂时无书,下回再试。\n");break;}else {perror ("semop");return -1;}}printf ("恭喜恭喜,借阅成功。\n");pleft (semid);break;}case 2: {struct sembuf sops = {0, 1, 0};if (semop (semid, &sops, 1) == -1) {perror ("semop");return -1;}printf ("好借好还,再借不难。\n");pleft (semid);break;}default:printf ("无效选择!\n");scanf ("%*[^\n]");scanf ("%*c");break;}}printf ("大功告成!\n");return 0;}
               

也可以结合这篇博文来一起学习点击打开链接



0 0