(一)信号量
信号量是IPC的一种,可以看做是一个计数器,计数值为可用的共享资源的数量,信号量可用于多进程的同步,为多个进程提供对共享资源的访问。
linux下的信号量的接口函数如下:
int semget(key_t key, int semnum, int flag);
semnum为信号数量,如果是新创建信号量,则大于0;如果是打开已有的信号量,则semnum可为0;
例子:
int semid = semget((ket_t)29111, 2, 0060|IPC_CREAT);
/*(2)对信号量进行操作*/
int semctl(int semid, int semnm, int cmd [, union semun arg]);
参数semnm是信号量集合中某个信号量的下标,从0开始,在0与nsems-1之间;
参数中联合体结构如下(有些系统需要自定义):
union semun
{
int val; /*for SETVAL*/
struct semid_ds *buf; /*for IPC_STAT IPC_SET*/
unsigned short *array; /*for SETALL*/
};
这里union semun arg是可选参数,例如cmd=IPC_RMID就不需要该参数,cmd为SETVAL或者SETALL的时候需要该参数。
以cmd=SETALL时,此时信号量集合的值由arg.array数组指定。
例子:union semun arg;
unsigned short semvalArr[SEM_NUM] = {1, 0};
arg.array = semvalArr;
ret = semctl(semid, 0, SETALL, arg);
/*(3)自动执行信号量集合上的操作数组 semop是原子操作*/
int semctl(int semid, int semnm, int cmd [, union semun arg]);
其中机构体struct sembuf在(sys/sem.h)中定义如下:
struct sembuf
{
unsigned short sem_num; /*0~nsems-1*/
short sem_op; /*negative, 0, pasitive*/
short sem_flg; /*0 IPC_UNDO IPC_NOWAIT*/
};
输入域参数semoparray指向一个信号量操作结构体数组,参数nops是操作的信号量的个数。struct sembuf结构体参数含义如下:
sem_num: 信号量序号(0与nsems-1之间);
sem_flg: 值为0是正常操作;值为IPC_NOWAIT是非阻塞模式;IPC_UNDO会在信号量调整值的过程中,同步调整信号量调整值,以便某个进程异常退出的情况下,信号量能够恢复到原值,否则其他进程可能会一直阻塞而死锁;
sem_op: 取值为正时候,调用该函数信号量值自动加上sem_op(如果是IPC_UNDO模式,信号量调整值减去sem_op),其含义可理解为释放sem_op个共享资源;取值为负的时候,如果信号量的算术值(可能为负)大于或等于sem_op的绝对值,则信号量自动减去sem_op的绝对值(ICP_UNDO模式下信号量调整值加sem_op的绝对值),表示占有sem_op个资源;如果信号量的算术值(可能为负)小于sem_op的绝对值,表明剩余的资源(信号量值表示)少于申请的资源,此时如果指定了IPC_NOWAIT则报错,否则进程被挂起直到信号量的算术值(可能为负)大于或等于sem_op的绝对值被唤醒(挂起和唤醒是系统自动进行的);如果sem_op为0,则表明希望等到该信号量值变为0,如果不是可能会报错或者阻塞。
这里可以看用semop可以包装P、V操作,以便实现进程之间的同步,这里回顾一下PV操作。
定义一个信号量sem,则有:
P操作:令sem=sem-1,若sem>=0,则进程继续执行,否则挂起等待,加入等待队列。
V操作:令sem=sem+1,若sem<0,唤醒等待队列中的一个进程。(注意:sem<0才需要唤醒,因为此时有阻塞; s>=0说明没阻塞也不需要唤醒)。
定义两个信号量(不能是一个,否则生产者或者消费者可能会无限的访问共享资源,形成死循环)S1、S2,初始值为1,0。S1=1表示生产者进程可写共享资源,为1则不可;S2=1表示消费者进程可读共享资源,为0则不可。伪代码描述如下:
生产者进程:
whlie(true)
{
产生一个消息
P(S1)
消息写到共享资源
V(S2)
}
消费者进程:
whlie(true)
{
P(S2)
从共享资源读取消息
V(S1)
处理消息
}
这里需要注意的是以上必须生产者先启动,否则会死锁,如果需要消费者先启动,需要进行额外的控制。上述代码中的P、V操作可以用semop进行包装, 一个例子如下(信号量为二元信号量):
/*sem P*/
int sem_p(int semid, int semnum)