分类: LINUX
2013-06-05 14:46:55
原文地址:(6)进程间通信之System V信号量 作者:g_programming
一、什么是信号灯
信号灯(也叫信号量)是一种用于提供不同进程间或一个给定进程的不同线程间同步手段的原语。信号灯是进程/线程同步的一种方式,有时候我们需要保护一段代码,使它每次只能被一个执行进程/线程运行,这种工作就需要一个二进制开关;有时候需要限制一段代码可以被多少个进程/线程执行,这就需要用到关于计数信号灯。信号灯开关是二进制信号灯的一种逻辑扩展,两者实际调用的函数都是一样的。
信号灯分为以下三种。
一、System V信号灯,在内核中维护,可用于进程或线程间的同步,常用于进程的同步。
二、Posix有名信号灯,一种来源于POSIX技术规范的实时扩展方案(POSIX Realtime Extension),可用于进程或线程间的同步,但常用于线程。
三、Posix基于内存的信号灯,存放在共享内存区中,可用于进程或线程间的同步。
这里只介绍Sysem V信号灯。
为了获得共享资源进程需要执行下列操作:
(1) 测试控制该资源的信号灯。
(2) 若此信号灯的值为正,则进程可以使用该资源。进程信号灯值减1,表示它使用了一个资源单位。此进程使用完共享资源后对应的信号灯会加1。以便其他进程使用。
(3) 若此信号灯的值为0,则进程进入休息状态,直至信号灯值大于0。进程被唤醒后,它返回至第(1)步。
为了正确地实现信号灯,信号灯值的测试值的测试及减1操作应当是原子操作。为此信号灯通常是在内核中实现的。
System V信号灯
System V每一个信号灯函数都能对成组的通用信号量进行操作,这在一个进程需要锁定多个资源的时候是很容易办到的。
与消息队列相似,信号灯也有一个结构semid_ds用于设置信号灯的信息。
struct semid_ds{
struct ipc_perm sem_perm; /*设置权限和所有者*/
struct sem *sem_base; /*描述一个信号灯集中的每个信号灯的结构*/
unsigned short sem_nsems; /*信号灯集中信号灯的数量*/
time_t sem_otime;
time_t sem_ctime;
…….
};
sem_perm结构的uid和cuid成员被置为调用进程的有效用户ID,gid和cgid成员被置为调用进程的有效组ID。
sem_otime被置为0,sem_ctime则被置为当前时间。
sem_nsems被置为nsems参数的值。
sem结构是内核用于维护某个给定信号灯的一组值的内部数据结构。一个信号灯集的每个成员由下面的结构描述:
struct sem{
unsigned short_t semval; /*信号灯的值*/
short sempid; /*对信号灯最后调用semop函数的进程ID*/
unsigned short_t semncnt; /*等待其值增长的进程数*/
unsigned short_t semzcnt; /*等待其值变为0的进程数*/
};
内核除维护一个信号灯集中每个信号灯的实际值之外,内核还给该集合中每个信号灯维护另外三个信息:对其值执行最后一次操作的进程的进程ID、等待其值增长的进程数计数以及等待其值变为0的进程数计数。
二、信号灯函数
8.
名称:: |
semget |
功能: |
获得一个信号灯id |
头文件: |
#include #include #inlcude |
函数原形: |
int semget(key_t key,int nsems,int flag); |
参数: |
key 键 nsems 信号灯数 flag 选项 |
返回值: |
若成功信号灯id,若出错返回-1。 |
key是一个建我们可以用ftok函数来获得。key是一个整数值,不相关的进程将通过这个值去访问同一信号量。程序对任何信号量的访问都必须间接地进行,先由程序提供一个键字,再由系统生成一个相应的信号量标识码。
semget用于获得一个信号灯ID,同msg相似此函数也需要提供一个唯一的外部键,作为信号灯的外部标识。
nsems参数是需要使用的信号量个数。如果是创建新集合,则必须制定nsems。如果引用一个现存的集合,则将nsems指定为0。一旦创建完毕一个信号灯集,我们就不能改变其中的信号灯数。
oflag参数是一组标志,其作用与open函数的各种标志很相似。它低端的九个位是该信号量的权限,其作用相当于文件的访问权限。但它们可以与键值IPC_CREAT做按位的或操作以创建一个新的信号量。即使在设置了IPC_CREAT标志后给出的是一个现有的信号量的键字,也并不是一个错误。我们也可以通过IPC_CREAT和IPC_EXCL标志的联合使用确保自己将创建出一个新的独一无二的信号量来,如果该信号量已经存在,就会返回一个错误。
当实际操作为创建一个信号灯时,相应的semid_ds结构的以下成员将被初始化:
与该集合中每个信号灯关联的各个sem结构并不初始化。这些结构是在以SET_VAL或SETALL命令调用semctl时初始化的。
下面程序创建一个信号灯集。
/*semcreat.c*/ #include #include #include #include #include #include
int main(int argc,char **argv) { int semid;
if(argc!=2) { printf(“please input a file name!”); exit(1); } if((semid=semget(ftok(argv[1],0),1,0644|IPC_CREAT))==-1) perror(semget); exit(0); } |
#gcc –o semcreat semcreat.c
#./semcreat test
此程序运行后不显示任何内容
下面的程序是设置信号灯的值并显示设置后的值。
/*semrmid.c*/ #include #include #include #include #include #include
union semun{ int val; struct semid_ds *buf; unsigned short int *array; };
int main(int argc,char **argv) { int semid; union semun arg; int value;
if(argc!=2) { printf(“please input a file name!”); exit(1); } if((semid=semget(ftok(argv[1],0),1,0644|IPC_CREAT))==-1) perror(semget); if((value=semctl(semid,0,IPC_RMID)==-1) perror(“semctl”); printf(“value:%d\n”,value); if((semctl(semid,0,IPC_RMID))==-1) perror(“semctl”); exit(0); } |
#gcc –o semvalue semvalue.c
#./semvalue test
value:0