SYSTEM V IPC——信号量笔记

时间:2021-09-22 20:42:08

一、概念

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>
#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;
}
添加了一个自定义的头文件semun.h:

#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’必定是成对出现的。