Linux进程通信-信号量

1.信号量:

 

信号量是解决资源同步与互斥的通用方法。信号量的值可以代表可用资源数。可以利用信号量来解决共享内存的资源同步与互斥问题。

内核通过全局数据结构struct ipc_ids sem_ids找到struct sem_array的第一个成员struct kern_ipc_perm,而这个结构体中有一个key,对应于具体的信号量集,这样,内核就知道系统中所有的信号量集合了。每一个信号量集合都有一个sem_array,用来描述信号量集合的相关信息,这个结构体存在于内核空间中,对应于用户空间的联合体union semun.

 

2.相关接口函数

 


semget(key_t key,int nsems,int semflg)//用来创建一个信号量集合,返回信号量标识符
//第一个参数是一个IPC对象标识符
//第二个参数是创建的信号量集合中信号量的个数
//第三个参数是一些标志
当semflg:
IPC_CREAT 如果内核中没有新创建的信号量集合,则创建它,否则返回信号量集合的标识符
IPC_EXCL 当与IPC_CREAT一起使用时,但信号量集合已经存在,则创建失败


信号量集合操作函数:
semop(int semid,struct sembuf*sops,unsigned nsops);
//第一个参数是信号量标识符
//sembuf是一个结构体,这个结构体定义了信号量的一些操作


struct sembuf{
unshort sem_num;//信号量在信号量集合中的索引值
short sem_op //信号量操作值
short sem_flg//操作标志

}

sem_op如果大于0,表示释放资源,资源的初始值val+semop
sem_op如果小于0,表示申请资源,如果val-sem_op小于0,该进程被挂起
sem_op为0,那么调用进程将睡眠到该信号量的值为0

第三个参数是操作的信号量的个数,sops指针所指向数组的个数,即可以操作多种资源

信号量集合上的控制操作
semctl(int semid,int semnum,int cmd,uniion semun arg)

//第一个参数是信号量标识符
//第二个参数是信号量集合上的信号量索引
//第三个参数表示在信号量集合上执行的命令
//semun是一个联合体,指示信号量的信息

union semun{

int val;//信号量的值,可以设置信号量的初始值
struct semid_ds *buf;
ushort *array;
struct seminfo* _buf;
void*_pad

};

cmd:
IPC_STAT 从信号量集合上检索semid_ds结构,并存到semun联合体的buf
IPC_SET 设置一个信号量集合上的semid_ds的ipc_perm
IPC_RMID 从内核中删除信号量集合
SETVAL 用联合体中的val成员的值设置信号量集合中单个信号量的值
可以通过 semget创建一个信号量集合,再用semctl来初始化信号量集合中各个信号量的值,然后进程用semop来操作资源

 这个函数要特别注意,可以传递3个参数也可以传递4个参数,当传递4个参数时,用来初始化信号量集合中的信号量,如果传递3个参数,如

semctl(semid,0,IPC_RMID);用来删除信号量集合。其中第2个参数信号量的个数这个参数被忽略了。

 

 

注意: sembuf这个结构体是用来操作信号量的,而semun这个联合体是用来初始化信号量的。

 

 

 

3. 经典实例-信号量与共享内存实现服务器与客户端

构成: 一个服务器进程与一个客户端进程

思路: 首先当没有请求时,阻塞服务器端,可以用信号量来阻塞,然后客户端在某个时间段向服务器发送数据,即将数据写入到共享内存中去,当服务器接收到数据后,解除阻塞,并根据客户端进程的pid,将数据发送给不同的客户端。

 

服务器端进程:

 

/**
本程序利用信号量与共享内存实现一个客户端服务器程序
两个客户端进程向服务器进程发送程序,当服务器进程收到数据时,再响应客户端请求
共享内存用到读写的互斥
**/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>

union semun{
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo* _buf;
};//用于信号量集合的初始化
int semid;
int shmid;

 

 

union semun seminit;
void myhandler(int signo,siginfo_t *si,void* vcontext);
typedef struct {
int pid;//存放的是信号量的索引
char da[256];//存放数据
}data;
data* p_map;
int main(){
key_t semkey;
key_t shmkey;
struct sembuf sem1,sem2,sem3;
struct sigaction act,oldact;
//创建一个IPC对象标识符
semkey=ftok("/home/C/semserver.c",0x28);
if(semkey==-1){
perror("semkey error");
exit(1);
}

semid=semget(semkey,2,IPC_CREAT|IPC_EXCL|0666);//返回信号量标识符,如果与semkey相关联的信号量集合不存在则创建一个信号量集合
if(shmid==-1){
perror("shmid error");
exit(1);
}
//信号量集合的初始化
seminit.val=0;
semctl(semid,0,SETVAL,seminit);
semctl(semid,1,SETVAL,seminit);

//对三个信号量实行的操作
sem1.sem_num=0;
sem1.sem_op=-1;//表示申请资源
sem1.sem_flg=SEM_UNDO;

sem2.sem_num=1;
sem2.sem_op=1;//释放资源
sem2.sem_flg=SEM_UNDO;

//建立共享内存
shmkey=ftok("/home/C/semserver.c",0x25);
if(shmkey==-1){
perror("shmkey error");
exit(1);
}

shmid=shmget(shmkey,4096,IPC_CREAT|IPC_EXCL|0666);//返回共享内存标识符

if(shmid==-1){
perror("shmid error");
exit(1);
}

p_map=(data*)shmat(shmid,NULL,0);
//信号机制,如果按下CTRL+C,删除信号量集合与共享内存
act.sa_sigaction=myhandler;
act.sa_flags=SA_SIGINFO;
sigaction(SIGINT,&act,&oldact);
sigaction(SIGTERM,&act,&oldact);
//服务器等待
while(1){
semop(semid,&sem1,1);
printf("%s/n",p_map->da);
sprintf(p_map->da,"%d,%s",p_map->pid,"server response");
semop(semid,&sem2,1);
}
}

void myhandler(int signo,siginfo_t *si,void *vcontext){
//删除信号量集合与共享内存
if(shmdt(p_map)==-1){
perror("shmdt error");
}
semctl(semid,0,IPC_RMID);
printf("shmid=%d",shmid);
shmctl(shmid,IPC_RMID,NULL);
exit(1);
}

 客户端进程:


#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <sys/sem.h>
int main(){
typedef struct {
int pid;
char da[256];

}data;
data* p_map;
int semid;
int shmid;
key_t shmkey;
key_t semkey;
//首先创建信号量标训符
semkey=ftok("/home/C/semserver.c",0x28);
if(semkey==-1){
perror("semkey error");
exit(1);
}

semid=semget(semkey,0,IPC_CREAT);//如果信号量集合已经存在,则返回信号量标识符
if(semid==-1){
perror("semid error");
exit(1);
}

shmkey=ftok("/home/C/semserver.c",0x25);
if(shmkey==-1){
perror("shmkey error");
exit(1);
}

shmid=shmget(shmkey,4096,IPC_CREAT);//返回共享内存标识符
if(shmid==-1){
perror("shmid error");
exit(1);
}

p_map=(data*)shmat(shmid,NULL,0);

//定义信号量操作
struct sembuf sem1,sem2;
sem1.sem_num=0;
sem1.sem_op=1;//释放资源
sem1.sem_flg=SEM_UNDO;

sem2.sem_num=1;
sem2.sem_op=-1;
sem2.sem_flg=SEM_UNDO;


while(1){
sleep(2);
p_map->pid=getpid();
sprintf(p_map->da,"%d%s",p_map->pid,"client request");
semop(semid,&sem1,1);
semop(semid,&sem2,1);
if(p_map->pid==getpid()){
printf("%s/n",p_map->da);
}
}
}

 

运行结果:

 

服务器端


[root@localhost C]# ./semserver
3444client request
3444client request
3444client request
3444client request
3444client request
3444client request
3444client request
3444client request
3444client request
3444client request
3444client request
3444client request
3444client request
3444client request
3444client request
3444client request
3444client request
3472client request
3444client request
3472client request
3444client request
3472client request
3444client request
3472client request
3444client request
3472client request
3444client request
3472client request
3444client request
3472client request
3444client request
3472client request
3444client request
3472client request
3444client request
3472client request
3444client request
3472client request
3444client request
3472client request
3444client request
3472client request
3444client request
3472client request
3444client request
3472client request
3444client request
3472client request
3444client request
3472client request
3444client request
3472client request
3444client request
3472client request
3444client request
3472client request
3444client request
3501client request
3472client request
3444client request
3501client request
3472client request
3444client request
3501client request
3472client request
3444client request
3501client request
3472client request
3444client request
3444client request

客户端1


[root@localhost C]# ./semclient1
3444,server response
3444,server response
3444,server response
3444,server response
3444,server response
3444,server response
3444,server response
3444,server response
3444,server response
3444,server response
3444,server response
3444,server response
3444,server response
3444,server response
3444,server response
3444,server response
3444,server response
3444,server response
3444,server response
3444,server response
3444,server response
3444,server response
3444,server response
3444,server response
3444,server response
3444,server response
3444,server response
3444,server response
3444,server response
3444,server response
3444,server response
3444,server response
3444,server response
3444,server response
3444,server response
3444,server response
3444,server response
3444,server response
3444,server response
3444,server response
3444,server response

客户端2:


[root@localhost C]# ./semclient1
3472,server response
3472,server response
3472,server response
3472,server response
3472,server response
3472,server response
3472,server response
3472,server response
3472,server response
3472,server response
3472,server response
3472,server response
3472,server response
3472,server response
3472,server response
3472,server response
3472,server response
3472,server response
3472,server response
3472,server response
3472,server response
3472,server response

 

 

总之,可以利用信号量与共享内存来实现进程间的通信,如果采用管道,那么就是一个服务器管道,多个客户端管道,而管道根据不同的pid来建立。

如果想对进程间通信了解的更透彻,可以参考 http://blog.csdn.net/floweronwarmbed/archive/2010/02/27/5328345.aspx

 

阅读更多
个人分类: LINUX
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭