标签:
信号量又名信号灯,与其他进程间通信方式大不相同,主要用途是用来保护临界资源。进程可以根据它判断是否能访问某些共享资源。除了用于访问控制外,还可以用于进程同步。
分类:
二值信号灯:信号灯的值只能取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 == SETVAL
struct semid_ds *buf // cmd == IPC_SET或者 cmd == IPC_STAT
ushort *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 PERM S_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" // 信号量 ID int 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 ,就会发现,程序开始输出,并且是交替输出的。
如图:
版权声明:本文为博主原创文章,未经博主允许不得转载。
标签:
原文地址:http://blog.csdn.net/u011641885/article/details/47682421