IPC对象之信号量
1. 信号量
1)信号量(semaphore),也叫信号灯。它是不同进程间或一个给定进程内部不同线程间同步的机制。
2)二值信号量:值为0或1。与互斥锁类似,资源可用时值为1,不可用时值为0。
3)计数信号量:值在0到n之间。同来统计资源,其值代表可用资源数。
4)等待操作是等待信号量的值变为大于0,然后将其减一;而释放操作则相反,用来唤醒等待资源的进程或者线程。
2. 信号量编程函数
在Linux系统中,使用信号量通常需要创建信号量、初始化信号量、信号量PV操作以及信号量删除四种操作。
1)创建信号量
函数semget()
所需头文件:#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
函数原型:int semget(key_t key, int nsems, intsemflg)
函数参数:
key 信号量的键值,其他进程通过该值访问该信号量,其中有个特殊值IPC_PRIVATE,表示创建当前进程的私有信号量
nsems 需要创建的信号量数目,通常为1。若创建多个信号量则称为信号量集
semflg 同open()函数的第三个参数,为信号量设定权限,通常使用八进制表示。
函数返回值:
成功:信号量的标识符(非负整数)
失败:-1
2)初始化信号量,函数semctl()用于对信号量进行相应的控制,包括设置初始化值、获取信息、设置属性、删除信号量(集)等操作
函数semctl()
头文件:#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
函数原型:int semctl(int semid, int semnum, intcmd, union semun arg)
函数参数:
semid 信号量标识符(即semget()函数的返回值)
semnum 信号量编号,通常存在多个信号量时才会使用。通常取值为0,即第一个信号量。
cmd 需要对信号量采取的操作。可取值有很多,常用的有:
IPC_STAT 读取消息队列的数据结构semid_ds并将其存储在第四个参数arg结构变量的buf指定的地址中
IPC_SETVAL 将信号量值设定为arg中的val值
IPC_GETVAL 获取当前信号量的值
IPC_RMID 从内核中删除信号量(集)
arg 是一个union semun结构的共用体,常用类型如下:
union semun
{
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT,IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO
(Linux-specific) */
};
注意:某些系统内未给出union semun的定义,需要程序员自己定义该共用体。
其中buf参数是一个semid_ds类型的结构体指针,使用时必须使用地址传递的方式。结构体成员很多,常用的有:
struct semid_ds
{
uid_t sem_perm.uid; /* Effective UID of owner*/
gid_t sem_perm.gid; /* Effective GID of owner*/
……
};
函数返回值:成功:
IPC_STAT、IPC_SETVAL或IPC_RMID操作:0
IPC_GETVAL操作:返回当前信号量的值
失败:-1
3)信号量PV操作
函数semop()
头文件:#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
函数原型:int semop(int semid, struct sembuf*sops, size_t nsops)
函数参数:
semid 信号量标识符(即semget()函数的返回值)
sops 是一个sembuf类型的结构体指针,使用时必须使用地址传递的方式。结构体类型如下:
struct sembuf
{
unsigned short sem_num;//信号量编号,若是单个信号量则取值0
short sem_op;//取值-1为P操作,取值1为V操作
short sem_flg;//通常取值SEM_UNDO,表示进程结束后系统自动释放该进程中未释放的信号量
}
nsops 需要操作的信号量数目,通常取值1(一个操作)
函数返回值:
成功:信号量的标识符
失败:-1
示例:有两个进程,进程A和进程B,这两个进程通过共享内存通信,进程A负责写内存,进程B负责读内存。要求进程A写一次,然后B读一次。这里用两个信号量进行读写的同步。(在博文:IPC对象之共享内存中使用无名管道进行都写得同步)。
方法一程序结构图如下:
示例代码:
/*************************************************************************
@Author: wanghao
@Created Time : Wed 23 May 2018 07:37:06 PMPDT
@File Name: sem_demo.c
@Description:
************************************************************************/
#include <sys/shm.h>
#include <sys/sem.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
/*The structure that stores the data*/
typedef struct{
int val;
int semid;
}Storage;
void init(Storage *s)
{
assert(s!= NULL);
/*Create semaphore, return semaphore value*/
if((s->semid= semget(IPC_PRIVATE,2,IPC_CREAT|IPC_EXCL|0777)) <0){
perror("semgeterror");
exit(1);
}
/*Define union semun struct*/
union semun{
int val;
struct semid_ds *ds;
unsigned short *array;
};
union semun un;
/*Each value in the semaphore*/
unsigned short array[2] = {0,1};
un.array= array;
/*Set all values in the semaphore*/
if(semctl(s->semid,0,SETALL,un)<0){
perror("semctrlerror");
exit(1);
}
}
void destroy(Storage *s)
{
assert(s!= NULL);
/*Delete semaphore*/
if(semctl(s->semid,0,IPC_RMID,NULL)<0){
perror("semctlerror");
exit(1);
}
}
void write(Storage *s, int val)
{
/*Set semaphore 0 to V Operation*/
struct sembuf ops_v[1] = {{0,1,SEM_UNDO}};
/*Set semaphore 1 to P Operation*/
struct sembuf ops_p[1] = {{1,-1,SEM_UNDO}};
/*Set semaphore 1 to P Operation*/
if(semop(s->semid,ops_p,1)<0){
perror("semoperror");
}
s->val= val;
printf("%d write %d\n",getpid(),val);
/*Set semaphore 1 to V Operation*/
if(semop(s->semid,ops_v,1)<0){
perror("semoperror");
}
}
void read(Storage *s)
{
assert(s!= NULL);
/*Setsemaphore 0 to P Operation*/
struct sembuf ops_p[1] = {{0,-1,SEM_UNDO}};
/*Set semaphore 0 to V Operation*/
struct sembuf ops_v[1] = {{1,1,SEM_UNDO}};
/*Set semaphore 0 to P Operation*/
if(semop(s->semid,ops_p,1)<0){
perror("semoperror");
}
printf("%d read %d\n",getpid(),s->val);
/*Setsemaphore 0 to V Operation*/
if(semop(s->semid,ops_v,1)<0 ){
perror("semoperror");
}
}
int main(void)
{
intshmid;
/*Create Shared Memory, size is sizeof Storage*/
if((shmid= shmget(IPC_PRIVATE,sizeof(Storage),
IPC_CREAT|IPC_EXCL|0777))< 0){
perror("shmgeterror");
exit(1);
}
/*Map shared memory*/
Storage *s = (Storage*) shmat(shmid,0,0);
if(s== (Storage*) -1){
perror("shmaterror");
exit(1);
}
init(s);
pid_tpid;
pid= fork();
if(pid<0){
perror("forkerror");
exit(1);
}else if(pid > 0){
inti = 1;
for(;i<=100; i++){
write(s,i);
}
wait(NULL);
/*Deletesemaphore*/
destroy(s);
/*Unlocka shared memory map*/
shmdt(s);
/*Deletememory*/
shmctl(shmid,IPC_RMID,NULL);
}else{
int i = 1;
for(;i<=100;i++){
read(s);
}
/*Unlocka shared memory map*/
shmdt(s);
}
exit(0);
}
方法二程序结构图如下:
示例代码如下,与方法1的不同之处,用红笔标出。
/*************************************************************************
@Author: wanghao
@Created Time : Wed 23 May 2018 07:42:18 PMPDT
@File Name: sem_demo1.c
@Description:
************************************************************************/
#include <sys/shm.h>
#include <sys/sem.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
/*The structure that stores the data*/
typedef struct{
intval;
intsemid;
}Storage;
void init(Storage *s)
{
assert(s!= NULL);
/*Createsemaphore, return semaphore value*/
if((s->semid= semget(IPC_PRIVATE,2,IPC_CREAT|IPC_EXCL|0777)) <0){
perror("semgeterror");
exit(1);
}
/*Defineunion semun struct*/
unionsemun{
intval;
structsemid_ds *ds;
unsignedshort *array;
};
union semun un;
/*Eachvalue in the semaphore*/
unsigned short array[2] = {0,0};
un.array= array;
/*Setall values in the semaphore*/
if(semctl(s->semid,0,SETALL,un)<0){
perror("semctrlerror");
exit(1);
}
}
void destroy(Storage *s)
{
assert(s!= NULL);
/*Deletesemaphore*/
if(semctl(s->semid,0,IPC_RMID,NULL)<0){
perror("semctlerror");
exit(1);
}
}
void write(Storage *s, int val)
{
/*Set semaphore 0 to V Operation*/
struct sembuf ops_v[1] = {{0,1,SEM_UNDO}};
/*Set semaphore 1 to P Operation*/
struct sembuf ops_p[1] = {{1,-1,SEM_UNDO}};
s->val = val;
printf("%d write %d\n",getpid(),val);
/*Set semaphore 1 to V Operation*/
if(semop(s->semid,ops_v,1) <0){
perror("semop error");
}
/*Set semaphore 1 to P Operation*/
if(semop(s->semid,ops_p,1)<0){
perror("semop error");
}
}
void read(Storage *s)
{
assert(s!= NULL);
/*Setsemaphore 0 to P Operation*/
structsembuf ops_p[1] = {{0,-1,SEM_UNDO}};
/*Setsemaphore 0 to V Operation*/
structsembuf ops_v[1] = {{1,1,SEM_UNDO}};
/*Setsemaphore 0 to P Operation*/
if(semop(s->semid,ops_p,1)<0){
perror("semoperror");
}
printf("%dread %d\n",getpid(),s->val);
/*Setsemaphore 0 to V Operation*/
if(semop(s->semid,ops_v,1)<0 ){
perror("semoperror");
}
}
int main(void)
{
intshmid;
/*CreateShared Memory, size is sizeof Storage*/
if((shmid= shmget(IPC_PRIVATE,sizeof(Storage),
IPC_CREAT|IPC_EXCL|0777))< 0){
perror("shmgeterror");
exit(1);
}
/*Mapshared memory*/
Storage*s = (Storage*) shmat(shmid,0,0);
if(s== (Storage*) -1){
perror("shmaterror");
exit(1);
}
init(s);
pid_tpid;
pid= fork();
if(pid<0){
perror("forkerror");
exit(1);
}elseif(pid > 0){
inti = 1;
for(;i<=100; i++){
write(s,i);
}
wait(NULL);
/*Deletesemaphore*/
destroy(s);
/*Unlocka shared memory map*/
shmdt(s);
/*Deletememory*/
shmctl(shmid,IPC_RMID,NULL);
}else{
inti = 1;
for(;i<=100;i++){
read(s);
}
/*Unlocka shared memory map*/
shmdt(s);
}
exit(0);
}