linux信号量对mysql_linux进程间通信之System V 信号量(semaphore)用法详解

信号量是一种不同进程或不同线程间的同步方法,有System V信号量和Posix信号量。

本文介绍System V 信号量,其在内核中维护,可用于进程间或线程间的同步,本文只介绍进程间同步。

信号量一般有两种,二值信号量(binary semaphore)和计数信号量(counting semaphore)。

二值信号量: 顾名思义,其值只有两种0或1,相当于互斥量,当值为1时资源可用;而当值为0时,资源被锁住,进程阻塞无法继续执行。

计数信号量: 其值是在0到某个限制值之间的信号量。

System V信号量是指的计数信号量集(set of counting semaphores),是一个或多个信号量的集合,其中每个都是计数信号量。(注:System V 信号量是计数信号量集,Posix 信号量是单个计数信号量。)

System V 信号量函数头文件及相关函数原型:

#include

int semget(key_t key, int nsems, int oflag);

功能:创建一个信号量集或访问一个已经存在的信号量集

返回值:成功返回非负的标识符,出错返回-1

参数:key是信号量的键值,多个进程可以通过这个键值访问同一个信号量;nsems参数指定信号量集合中的信号量数,一般设为1,如果不创建新的信号量集,只是访问一个已经存在的集合,可以把该参数设为0,一旦创建完一个信号量集,就不能改变其中的信号量数;oflag同open()权限位,IPC_CREAT标示创建新的信号量,如果或上IPC_EXCL,若信号量已存在则出错,如果没有或上IPC_EXCL,若信号量存在也不会出错。

int semctl(int semid, int semnum, int cmd, ... /*union semun arg */);

功能: 信号量控制操作。

返回值:若成功,根据cmd不同返回不同的值,IPC_STAT,IPC_SETVAL,IPC_RMID返回0,IPC_GETVAL返回信号量当前值;出错返回-1.

参数:semid标示操作的信号量集;semnum标示该信号量集内的某个成员(0,1等,直到nsems-1),semnum值仅仅用于GETVAL,SETVAL,GETNCNT,GETZCNT,GETPID,通常取值0,也就是第一个信号量;cmd:指定对单个信号量的各种操作,IPC_STAT,IPC_GETVAL,IPC_SETVAL,IPC_RMID;arg: 可选参数,取决了第三个参数cmd。

int semop(int semid, struct sembuf *opstr, size_t nops);

功能:操作信号量,P,V 操作

返回值:成功返回信号量标识符,出错返回-1

参数:semid:信号量集标识符;nops是opstr数组中元素数目,通常取值为1;opstr指向一个结构数组

struct sembuf{

short sem_num;

short sem_op;

short sem_flg;

}

一般编程步骤:

1. 创建信号量或获得在系统中已存在的信号量

1). 调用semget().

2). 不同进程使用同一个信号量键值来获得同个信号量

2. 初始化信号量

1).使用semctl()函数的SETVAL操作

2).当使用二维信号量时,通常将信号量初始化为1

3.进行信号量PV操作

1). 调用semop()函数

2). 实现进程之间的同步和互斥

4.如果不需要该信号量,从系统中删除

1).使用semctl()函数的IPC_RMID操作

代码举例,父子进程间的同步sem_test.c

#include

#include

#include

#include

#include

#include

#include

#define USE_SYSTEMV_SEM 1

#define DELAY_TIME 2

union semun {

int val;

struct semid_ds *buf;

unsigned short *array;

};

// 将信号量sem_id设置为init_value

int init_sem(int sem_id,int init_value) {

union semun sem_union;

sem_union.val=init_value;

if (semctl(sem_id,0,SETVAL,sem_union)==-1) {

perror("Sem init");

exit(1);

}

return 0;

}

// 删除sem_id信号量

int del_sem(int sem_id) {

union semun sem_union;

if (semctl(sem_id,0,IPC_RMID,sem_union)==-1) {

perror("Sem delete");

exit(1);

}

return 0;

}

// 对sem_id执行p操作

int sem_p(int sem_id) {

struct sembuf sem_buf;

sem_buf.sem_num=0;//信号量编号

sem_buf.sem_op=-1;//P操作

sem_buf.sem_flg=SEM_UNDO;//系统退出前未释放信号量,系统自动释放

if (semop(sem_id,&sem_buf,1)==-1) {

perror("Sem P operation");

exit(1);

}

return 0;

}

// 对sem_id执行V操作

int sem_v(int sem_id) {

struct sembuf sem_buf;

sem_buf.sem_num=0;

sem_buf.sem_op=1;//V操作

sem_buf.sem_flg=SEM_UNDO;

if (semop(sem_id,&sem_buf,1)==-1) {

perror("Sem V operation");

exit(1);

}

return 0;

}

int main() {

pid_t pid;

#if USE_SYSTEMV_SEM

int sem_id;

key_t sem_key;

sem_key=ftok(".",'A');

printf("sem_key=%x\n",sem_key);

//以0666且create mode创建一个信号量,返回给sem_id

sem_id=semget(sem_key,1,0666|IPC_CREAT);

printf("sem_id=%x\n",sem_id);

//将sem_id设为1

init_sem(sem_id,1);

#endif

if ((pid=fork())<0) {

perror("Fork error!\n");

exit(1);

} else if (pid==0) {

#if USE_SYSTEMV_SEM

sem_p(sem_id); //    P操作

#endif

printf("Child running...\n");

sleep(DELAY_TIME);

printf("Child %d,returned value:%d.\n",getpid(),pid);

#if USE_SYSTEMV_SEM

sem_v(sem_id); //    V操作

#endif

exit(0);

} else {

#if USE_SYSTEMV_SEM

sem_p(sem_id); //    P操作

#endif

printf("Parent running!\n");

sleep(DELAY_TIME);

printf("Parent %d,returned value:%d.\n",getpid(),pid);

#if USE_SYSTEMV_SEM

sem_v(sem_id); //    V操作

waitpid(pid,0,0);

del_sem(sem_id);

#endif

exit(0);

}

}

复制代码

运行结果:

$ ./a.out

sem_key=41015bb5

sem_id=70004

Parent running!

Parent 17759,returned value:17760.

Child running...

Child 17760,returned value:0.

可以看到是父进程执行完后才执行子进程。

如果取消System V 信号量(把条件编译宏设为0 “#define USE_SYSTEMV_SEM 0”),运行结果为

$ ./a.out

Parent running!

Child running...

Parent 17772,returned value:17773.

Child 17773,returned value:0.

可以看到父子进程之间存在竞争

注意:sem_key=ftok(".",'A');语句中ftok的第一个参数必须是存在的文件名,如果不存在的文件名,例如sem_key=ftok("xxxxx",'A'); 则运行结果出错:

$ ./a.out

sem_key=ffffffff

sem_id=ffffffff

Sem init: Invalid argument

关于PV操作

p操作和v操作是不可中断的程序段,称为原语。P,V原语中P是荷兰语的Passeren,相当于英文的pass, 意为通过,V是荷兰语的Verhoog,相当于英文中的incremnet,意为释放。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值