linux 进程通信之 信号量

信号量又名信号灯,与其他进程间通信方式大不相同,主要用途是用来保护临界资源。进程可以根据它判断是否能访问某些共享资源。除了用于访问控制外,还可以用于进程同步。

分类:

二值信号灯:信号灯的值只能取0或1,类似与互斥锁。但两者有不同:信号灯强调共享资源,只要共享资源可用,其他进程同样可以修改信号灯的值;互斥锁更强调进程,占用资源的进程使用完资源后,必须有进程本身来解锁。(我们常说的PV操作)

计数信号灯:信号灯的值可以取任意非负数。(多用于生产者消费者模型)

不管哪种信号灯,当信号灯的值为0时,会产生阻塞。

信号量使用相关API

int semget(key_t key, int nsems, int semflg);函数作用:创建一个新信号量或取得一个已有信号量。key:值为 IPC_PRIVATE 意味这即将创建新的信号量。,官方的说法是用ftok产生一个key(个人比较认同的做法是自己定义一个)使用。nsems:指定打开或者新创建的信号量集中将包含信号量的数目。nsems > 0时,创建一个新的信号量集,指定给集合中信号量的数量。nsems == 0时,访问一个已存在的集合。msgflg:是一组标志。IPC_CREAT:如果信号量不存在,则创建一个共享内存。IPC_EXCL:只有在信号量不存在的时候,新的信号量才建立,否则就产生错误。对于这个参数一般是这样操作#define PERM S_IRUSR | S_IWUSR | IPC_CREAT然后把 PERM 当作semflg。成功返回信号量集的ID,不成功返回-1,并设置errno。注:semget函数执行成功后,就产生了一个由内核维持的类型为semid_ds结构体的信号量集,返回值就是指向该信号量集的引索。结构体原型如下:struct semid_ds {struct ipc_perm sem_perm; /* 信号量集的操作许可权限 */struct sem *sem_base; /* 某个信号量sem结构数组的指针,当前信号量集中的每个信号量对应其中一个数组元素 */ushort sem_nsems; /* sem_base 数组的个数 */time_t sem_otime; /* 最后一次成功修改信号量数组的时间 */time_t sem_ctime; /* 成功创建时间 */};struct ipc_perm{ __kernel_key_t key;// IPC 的键值 __kernel_uid_t uid;// 所有者的用户 id __kernel_gid_t gid;// 所有者的组 id __kernel_uid_t cuid;// 创建者的用户 id __kernel_gid_t cgid;// 创建者的组 id __kernel_mode_t mode;// 访问模式 unsigned short seq;// 序列号};struct sem {ushort semval; /* 信号量的当前值 */short sempid; /* 最后一次返回该信号量的进程ID 号 */ushort semncnt; /* 等待semval大于当前值的进程个数 */ushort semzcnt; /* 等待semval变成0的进程个数 */};int semop(int semid, struct sembuf *sops, unsigned nsops);函数作用:设置信号量的值。semid:信号量的标识符(semget 函数的返回值)sops:指向信号量操作结构数组。结构体原型如下:struct sembuf {unsigned short sem_num; // 要操作的信号量在信号量集里的编号,short sem_op; // 信号量操作short sem_flg; // 操作表示符};若sem_op 是正数,其值就加到semval上,即释放信号量控制的资源若sem_op 是0,那么调用者希望等到semval变为0,如果semval是0就返回;若sem_op 是负数,那么调用者希望等待semval变为大于或等于sem_op的绝对值注:semval是指semid_ds中的信号量集中的某个信号量的值sem_flg:可以为以下值:SEM_UNDO 由进程自动释放信号量IPC_NOWAIT 不阻塞nsops:信号量操作结构数组的个数。int semctl(int semid, int semnum, int cmd, …);函数功能:控制信号量信息。semid:信号量的标识符(semget 函数的返回值)semnum:是信号在集合中的序号cmd:控制命令。可取值如下:SETVAL:指定信号量的当前值。此时 semval = val (val 的描述看下面可能出现的第四个参数)GETVAL:获取信号量的当前值。(semctl 函数的返回值)SETALL:指定所有信号量的值。此时 使用 array来将信号量集的所有值都赋值(array 的描述看下面可能出现的第四个参数)GETALL:获取所有信号量的值。将信号量集的所有值返回到 array 指定的数组中。(array 的描述看下面可能出现的第四个参数)IPC_STAT:得到信号量的状态。IPC_SET:改变信号量的状态。IPC_RMID:删除信号量标识符第四个参数是一个必须由用户自定义的联合结构体,该结构体原型如下:union semun{int val; // cmd == SETVALstruct semid_ds *buf // cmd == IPC_SET或者 cmd == IPC_STATushort *array; // cmd == SETALL,或 cmd = GETALL};注:第四个参数只在需要的时候才会用到。

示例程序(简单的PV操作示例)

//sem_h.h#ifndef SEM_H_H_INCLUDED#define SEM_H_H_INCLUDED#include <unistd.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <stdlib.h>#include <stdio.h>#include <string.h>#include <sys/sem.h>#include <errno.h>// 新建或打开信号的操作标志#define PERMS_IRUSR | S_IWUSR | IPC_CREAT// 联合类型 semnu , semctl函数的第四个参数union semnu {int val;struct semid_ds *buf;ushort *array;};//显示出错信息void print_error ( int f_line );//打开或者创建信号量void open_sem ( void );//初始化信号量void init_sem ( void );//信号量 p 操作void semaphore_p ( void );//信号量 v 操作void semaphore_v ( void );//删除信号量void del_sem ( void );#endif // SEM_H_H_INCLUDED// sem_c.c#include "sem_h.h"// 信号量 IDint sem_id = 0;void print_error ( int f_line ){fprintf ( stderr, "%s %s %d\n", strerror ( errno ), __FILE__, f_line );}void open_sem ( void ){if ( ( sem_id = semget ( ( key_t ) 1234, 1, PERM ) ) == -1 ) {print_error ( __LINE__ );}}void init_sem ( void ){union semnu semnu;semnu.val = 1;if ( semctl ( sem_id, 0, SETVAL, semnu ) == -1 ) {print_error ( __LINE__ );}}void semaphore_p ( void ){struct sembuf sem_b;sem_b.sem_num = 0;sem_b.sem_op = -1;sem_b.sem_flg = SEM_UNDO;if ( semop ( sem_id, &sem_b, 1 ) == -1 ) {print_error ( __LINE__ );}}void semaphore_v ( void ){struct sembuf sem_b;sem_b.sem_num = 0;sem_b.sem_op = 1;sem_b.sem_flg = SEM_UNDO;if ( semop ( sem_id, &sem_b, 1 ) == -1 ) {print_error ( __LINE__ );}}void del_sem ( void ){if ( semctl ( sem_id, 0, IPC_RMID ) == -1 ) {print_error ( __LINE__ );}}sem_1.c#include "sem_h.h"int main ( void ){    open_sem();    init_sem();    while ( 1 ) {        // p 操作,尝试进入缓冲区        semaphore_p();        printf ( "%d: hello \n", getpid() );        sleep(1);        //  v 操作, 离开缓冲区        semaphore_v();    }}// sem_2.c#include "sem_h.h"#include <signal.h>int runnig = 1;void Handlesignal(int signo){        printf("dasd\n");       runnig =0;      del_sem();}int main ( void ){      if(signal(SIGINT,Handlesignal)==SIG_ERR){        print_error(__LINE__);    }    open_sem();    while ( runnig ) {        semaphore_p();        printf ( "%d: hello \n", getpid() );        sleep(1);        semaphore_v();    }}

测试示例:

首先在命令行执行 sem_2 这时,程序是没有输出的,因为信号量的值是为0的,,阻塞了,然后运行 sem_1 ,就会发现,程序开始输出,并且是交替输出的。

如图:

学习不是人生的全部,但学习都征服不了,那我还能做什么?

linux 进程通信之 信号量

相关文章:

你感兴趣的文章:

标签云: