一、概念
system v的信号量实质是一个计数器,用于多进程对共享数据对象的访问。
二、工作流程
为了获得共享资源,进程需要执行下列操作:
1.测试控制该资源的信号量
2.若此信号的值为正,则进程可以使用该资源,并对该信号量的值减1,表示消耗了一个资源单位。至第三步。
若此信号量的值为0,则进入休眠状态,直至信号量大于0。进程被唤醒后,返回至第一步。
3.当进程使用完该资源以后,需要释放一个信号量,即对这个信号量的值加1操作。如果有进程正在休眠等待此信号量,则唤醒他们。
备注:为了正确的实现信号量,信号量值的测试、加1、减1操作是原子操作。所以,信号量通常是在内核实现的。
三、相关函数
1.semget
获得一个信号量集ID
#include <sys/sem.h>
int semget(key_t key, int nsems, int flag);
key是个什么东西?
nsems表示这个信号量集里面包含的信号量数
flag表示属性,第一次创建用IPC_CREAT
成功返回信号量ID,错误返回-1
2.semctl
设置初值或者删除等操作
int semctl(int semid, int semnum, int cmd, ...);
semid是信号量ID,就是semget的返回值;
semnum表示信号量集中的第几个信号量(0 <= semnum <= nsems )
cmd是控制指令:IPC_STAT、IPC_SET(设置初值)、IPC_RMID(删除信号量集)、GETVAL、SETVAL、GETALL……
...表示一个枚举,semun
返回值:除GETALL以外的GET命令的返回相应的值,其他成功0,失败-1
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
};
3.semop
加1或减1操作
int semop(int semid, struct sembuf semoparray[], size_t nops);
semid为信号量ID
semoparray[]为struct sembuf 型结构体指针
nops是要操作的信号量个数
返回值:成功0,失败-1
struct sembuf
{
unsigned short sem_num;//要操作的第几个信号量
short sem_op;//1 or -1
short sem_flg;//SEM_UNDO
}
四、例程(单个信号量)
#include <stdio.h>添加了一个自定义的头文件semun.h:
#include <stdlib.h>
#include <unistd.h>
#include <sys/sem.h>
#include "semun.h"
static int set_semvalue(void);
static void del_semvalue(void);
static int semaphore_p(void);
static int semaphore_v(void);
static int sem_id;
int main(int argc, char *argv[])
{
int i;
int pause_time;
char op_char = 'O';
srand((unsigned int)getpid ());
sem_id = semget((key_t)1234,1,0666|IPC_CREAT);
if(argc >1 )
{
if(!set_semvalue ())
{
fprintf (stderr,"Failed to initialize semaphore\n");
exit(EXIT_FAILURE);
}
op_char ='X';
sleep(2);
}
for(i =0 ; i < 10; i++)
{
if(!semaphore_p ())exit(1);
printf("%c",op_char);fflush(stdout);
pause_time = rand() % 3;
sleep(pause_time);
printf("%c",op_char);fflush(stdout);
if(!semaphore_v ())exit (1);
pause_time = rand() % 2;
sleep(pause_time);
}
printf("\n%d - finished\n",getpid());
if(argc >1)
{
sleep(10);
del_semvalue ();
}
exit(0);
}
static int set_semvalue (void)
{
union semun sem_union;
sem_union.val = 1;<span style="white-space:pre"></span>//初始值设为1个资源单位
if(semctl (sem_id, 0, SETVAL, sem_union)==-1)return (0);
return (1);
}
static void del_semvalue (void)
{
union semun sem_union;
if(semctl(sem_id, 0, IPC_RMID, sem_union)==-1)
printf(stderr, "Failed to del semaphore.\n");
}
static int 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)
{
fprintf(stderr, "semaphore_p failed.\n");
return 0;
}
return 1;
}
static int 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)
{
fprintf(stderr, "semaphore_v failed.\n");
return 0;
}
return 1;
}
#if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)
/* union semun is defined by including <sys/sem.h> */
#else
/* according to X/OPEN we have to define it ourselves */
union semun {
int val; /* value for SETVAL */
struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */
unsigned short int *array; /* array for GETALL, SETALL */
struct seminfo *__buf; /* buffer for IPC_INFO */
};
#endif
运行程序:
#./sem 1 &
#./sem &
结果:XXXXOOXXXXXXOOOOOOXXOOXXOOXXXXXXOOO
解释:第一次执行sem程序,申请了一个信号量集,第二次运行的sem程序,直接访问该信号量集。二者交替打印,因为信号量的存在,而初始值设为1个资源单位,所以‘X’或‘O’必定是成对出现的。