信号量概述
信号量广泛用于进程或线程间的同步和互斥,信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问。
编程时可根据操作信号量值的结果判断是否对公共资源具有访问的权限,当信号量值大于 0 时,则可以访问,否则将阻塞。 PV 原语 是对信号量的操作,一次 P 操作使信号量减1,一次 V 操作使信号量加1。
在实际应用中两个进程间通信可能会使用多个信号量,因此 System V 的信号量以集合的概念来管理,具体操作和 Posix 信号量 大同小异,详情请点此链接:http://blog.csdn.net/tennysonsky/article/details/46496201 。
信号量集合数据结构 :struct semid_ds,此数据结构中定义了整个信号量集的基本属性。
struct semid_ds { struct ipc_perm sem_perm; __kernel_time_t sem_otime; __kernel_time_t sem_ctime; struct sem *sem_base; struct sem_queue *sem_pending; struct sem_queue **sem_pending_last; struct sem_undo *undo; unsigned short sem_nsems; };
信号量数据结构:struct sem,此数据结构中定义了信号量的基本属性。
struct sem { int semval; int sempid; struct list_head sem_pending; };
System V 信号量基本操作
使用 shell 命令操作信号量:
查看信号量:ipcs -s
删除信号量:ipcrm -s semid
以下函数所需头文件如下:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
1)创建信号量集合
int semget(key_t key, int nsems, int semflg);
功能:
创建或打开一个信号量集合,该集合中可以包含多个信号量。
参数:
key :进程间通信键值,通过调用 ftok() 函数得到的键值,详情请点此链接:http://blog.csdn.net/tennysonsky/article/details/46331643 。
nsems :创建的信号量的个数。如果只是访问而不创建则可以指定该参数为 0,一旦创建了该信号量,就不能更改其信号量个数,只要不删除该信号量,重新调用该函数创建该键值的信号量,该函数只是返回以前创建的值,不会重新创建。
semflg :标识函数的行为及信号量的权限,其取值如下:
IPC_CREAT :创建信号量。 IPC_EXCL :检测信号量是否存在。 位或权限位 :信号量位或权限位后可以设置信号量的访问权限,格式和 open 函数的 mode_ t 一样(open() 的使用请点此链接 ),但可执行权限未使用。
返回值:
成功:信号量集标识符
失败:返回 -1
2)控制信号量集合、信号量
int semctl(int semid, int semnum, int cmd, ...);
功能:
对信号量集合以及集合中的信号量进行操作。
参数:
semid :信号量集标识符。
semnum :集合中信号量的序号,指定对哪个信号量操作, 只对几个特殊的 cmd 操作有意义。
cmd :信号量控制类型。semctl() 函数可能有3个参数,也可能有4个参数,参数的个数由 cmd 决定。当有4个参数时,第4个参数为联合体:
union semun{ int val; struct semid_ds *buf; unsigned short *array; struct seminfo *__buf; };
cmd 的取值如下:
GETVAL :获取信号量的值。此时函数有3个参数。semctl() 函数的返回值即为信号量的值。
SETVAL :设置信号量的值。此时函数有4个参数。第4个参数为联合体中的val,其值为信号量的值。
IPC_STAT :获取信号量集合的信息。此时函数有4个参数。第4个参数为联合体中的__buf。
IPC_SET :设置信号量集合的信息。此时函数有4个参数。第4个参数为联合体中的__buf。
IPC_RMID :删除信号量集。此时函数有3个参数,第2个参数semnum不起作用。
GETALL :获取所有信号量的值。此时函数有4个参数,第2个参数semnum不起作用。第4个参数为联合体中的array,其值为用来存放所有信号量值的数组的首地址。
SETALL :设置所有信号量的值 。参数说明同上。
IPC_INFO :获取信号量集合的限制信息。此时函数有4个参数,第2个参数semnum不起作用。第4个参数为联合体中的__buf。
GETPID :获取信号的进程号,即最后操作信号量的进程。此时函数有3个参数。semctl() 函数的返回值即为信号的进程号。
GETNCNT :获取等待信号的值递增的进程数。此时函数有3个参数。semctl() 函数的返回值即为进程数。
GETZCNT :获取等待信号的值递减的进程数。此时函数有3个参数。semctl() 函数的返回值即为进程数。
返回值:
成功:0
失败:-1
3)操作信号量
int semop(int semid, struct sembuf *sops, unsigned nsops);
功能:
操作信号量,主要进行信号量加减操作。
参数:
semid :信号量集标识符。 sops :操作信号量的结构体(struct sembuf)数组的首地址( 结构体定义在 sys/sem.h ),此结构体中的数据表明了对信号量进行的操作。
struct sembuf{ unsigned short sem_num; short sem_op; short sem_flg; };
结构体成员使用说明如下:
sem_num :信号量集中信号量的序号
sem_op 取值如下:
sem_op > 0 :信号量的值在原来的基础上加上此值。
sem_op < 0 :如果信号量的值小于 semop 的绝对值,则挂起操作进程。如果信号量的值大于等于 semop 的绝对值,则信号量的值在原来的基础上减去 semop 的绝对值。
sem_op = 0 :对信号量的值进行是否为 0 测试 。若为 0 则函数立即返回,若不为 0 则阻塞调用进程。
sem_flag 取值如下:
IPC_NOWAIT :在对信号量的操作不能执行的情况下使函数立即返回。
SEM_UNDO :当进程退出后,该进程对信号量进行的操作将被撤销。
nsops :操作信号量的结构体数组中元素的个数。
返回值:
成功:0
失败:-1
使用示例
示例一:
#include <sys/types.h> #include <sys/sem.h> #include <sys/ipc.h> #include <stdlib.h> #include <stdio.h> int main( int argc, char *argv[]) { key_t key; key = ftok("." , 'a' ); if (key == -1) { perror("ftok" ); } system("ipcs -s" ); int semid; semid = semget(key, 1, IPC_CREAT|0666); if (semid == -1) { perror("semget" ); } system("ipcs -s" ); semctl(semid, 0, IPC_RMID); system("ipcs -s" ); return 0; }
运行结果如下:
示例二:
#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> #include <stdlib.h> #include <stdio.h> #define IPC_INFO 3 int main( int argc, char *argv[]) { key_t key; key = ftok("." , 'a' ); if (key == -1) { perror("ftok" ); } system("ipcs -s" ); int semid; semid = semget(key, 1, IPC_CREAT|0666); if (semid == -1) { perror("semget" ); } system("ipcs -s" ); struct seminfo buf; semctl(semid, 0, IPC_INFO, &buf); printf("buf.semmni = %d\n" , buf.semmni); printf("buf.semmns = %d\n" , buf.semmns); printf("buf.semmnu = %d\n" , buf.semmnu); printf("buf.semmsl = %d\n" , buf.semmsl); printf("buf.semopm = %d\n" , buf.semopm); printf("buf.semume = %d\n" , buf.semume); printf("buf.semusz = %d\n" , buf.semusz); printf("buf.semvmx = %d\n" , buf.semvmx); printf("buf.semaem = %d\n" , buf.semaem); semctl(semid, 0, IPC_RMID); system("ipcs -s" ); return 0; }
运行结果如下:
示例三:
#include <sys/types.h> #include <sys/sem.h> #include <sys/ipc.h> #include <stdlib.h> #include <stdio.h> int main( int argc, char *argv[]) { key_t key; key = ftok("." , 'a' ); if (key == -1) { perror("ftok" ); } system("ipcs -s" ); int semid; semid = semget(key, 1, IPC_CREAT|0666); if (semid == -1) { perror("semget" ); } system("ipcs -s" ); int ret; ret = semctl(semid, 0, SETVAL, 20); if (ret == -1) { perror("semctl" ); } ret = semctl(semid, 0, GETVAL); if (ret == -1) { perror("semctl" ); } printf("ret = %d\n" , ret); semctl(semid, 0, IPC_RMID); system("ipcs -s" ); return 0; }
运行结果如下:
示例四:
#include <sys/types.h> #include <sys/sem.h> #include <sys/ipc.h> #include <stdlib.h> #include <stdio.h> #include <string.h> int main( int argc, char *argv[]) { key_t key; key = ftok("." , 'a' ); if (key == -1) { perror("ftok" ); } system("ipcs -s" ); int semid; semid = semget(key, 2, IPC_CREAT|0666); if (semid == -1) { perror("semget" ); } system("ipcs -s" ); int ret; unsigned short sem_arry[2] = {30,20}; ret = semctl(semid, 0, SETALL, sem_arry); if (ret == -1) { perror("semctl" ); } bzero(sem_arry, sizeof (sem_arry)); ret = semctl(semid, 0, GETALL, sem_arry); if (ret == -1) { perror("semctl" ); } printf("sem_arry[0] = %d\n" , sem_arry[0]); printf("sem_arry[1] = %d\n" , sem_arry[1]); semctl(semid, 0, IPC_RMID); system("ipcs -s" ); return 0; }
运行结果如下:
示例五:
#include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> #include <sys/shm.h> #include <unistd.h> #include <sys/wait.h> #define SHM_KEY 0x33 #define SEM_KEY 0x44 union semun { int val; struct semid_ds *buf; unsigned short *array; }; int P( int semid) { struct sembuf sb; sb.sem_num = 0; sb.sem_op = -1; sb.sem_flg = SEM_UNDO; if (semop(semid, &sb, 1) == -1){ perror("semop" ); return -1; } return 0; } int V( int semid) { struct sembuf sb; sb.sem_num = 0; sb.sem_op = 1; sb.sem_flg = SEM_UNDO; if (semop(semid, &sb, 1) == -1){ perror("semop" ); return -1; } return 0; } int main( int argc, char **argv) { pid_t pid; int i, shmid, semid; int *ptr = NULL; union semun semopts; if ((shmid = shmget(SHM_KEY, sizeof ( int ), IPC_CREAT | 0600)) == -1) { perror("msgget" ); return -1; } ptr = (int *)shmat(shmid, NULL, 0); if (ptr == ( int *)-1) { perror("shmat" ); return -1; } *ptr = 0; if ((semid = semget(SEM_KEY, 1, IPC_CREAT | 0600)) == -1) { perror("semget" ); return -1; } semopts.val = 1; if (semctl(semid, 0, SETVAL, semopts) < 0) { perror("semctl" ); return -1; } if ((pid = fork()) < 0) { perror("fork" ); _exit(0); }else if (pid == 0){ for (i = 0; i < 100000; i++) { P(semid); (*ptr)++; V(semid); printf("child: %d\n" , *ptr); } } else { for (i = 0; i < 100000; i++) { P(semid); (*ptr)--; V(semid); printf("parent: %d\n" , *ptr); } wait(NULL); printf("finally: %d\n" , *ptr); } return 0; }
运行结果如下:
本教程示例代码下载请点此链接:http://download.csdn.net/detail/tennysonsky/9029479