进程间通信-----------------信号量(主要用于同步与互斥p、v操作)

信号量的原理和本质

信号量=计数器+等待队列

信号量本身不具有数据交换的功能,只是起到一个管理通信资源的作用,实际上是计数器和等待队列。在进程通信的过程中只负责数据操作的互斥和同步功能
信号量就是具有原子性的计数器,相当于一把锁,在每个进程要访问临界资源的时候,必须向信号量拿个锁,这样他才能够去访问“房间”(临界资源)。不让其他进程进来,此时信号量执行P()操作,在此过程中申请了一个锁那么对应锁的数目就减少了一个,所以计数器减1;当访问完成后,进程出来,就将锁还给信号量此时执行V()操作,计数器加1;这样就体现了是如何保证进程访问临界资源是互斥的。

涉及到名词的解释:

1、原子性:指的是做一件事情要么不做,如果做就要做完。
2、计数器:其实表示的就是临界资源的数目;信号量本身就可以理解成临界资源
3、临界资源:不同进程可以看到的那份公共的资源,但是一次只允许一个进程使用。也叫做互斥资源。
4、临界区:访问临界资源的代码叫做临界区
5、互斥:同一时刻只允许一个进程访问临界资源

为什么要使用信号量

为了防止出现由于多个程序同时访问一个临界资源而引发结果错乱等问题,就是通过信号量具有锁的机制,限定在同一时刻只能有一个进程访问这块临界资源,信号量起到协调进程对共享资源的访问的作用。

程序实例

创建和申请一个信号量集,这就形成了一份临界资源,同时让子进程和父进程向显示器中写入AA和BB,在没有信号量的限制下AB则不会成对出现,但是当使用了信号量就会发现可以有序的打印“BBAABB…”

实现代码涉及到的信号量集函数

1、semget函数:创建信号量集

int semget(key_t key,int nsems,int semflg);
key:信号量集的名字;nsems:信号量集中信号量的个数
成功返回信号集的标识码,失败返回-1

2、semctl函数:控制信号量集

int semctl(int semid,int semnum,int cmd,…);
semid:创建信号量集返回的信号量集标识码;semnum:信号集的序号(下标)cmd:将要采取的动作(三种)

命令 说明
SETVAL 设置信号量集中信号量的计数值
GETVAL 获取信号量集中信号量的计数值
IPC_STAT 将semid_ds结构中的数据设置为信号集的当天关联值
IPC_SET 在权限允许的情况下,将信号量的当前关联值设置为semid_ds数据结构中给出的值
IPC_RMID 删除信号集

3、semop函数:访问信号量集

int semop(int semid,struct sembuf* sops,unsigned nsops);
semid:信号量的标识码;sops:结构体指针;nsops:信号量的个数
成功返回0失败返回-1
struct sembuf{
short sem_num;
short sem_op;//PV操作时加减的数值(只有1和-1)
short sem_flg;
}

实例结果

这里写图片描述
这里写图片描述

实现代码:

1、comm.h文件:

#include<sys/ipc.h>
#include<sys/sem.h>

#define PATHNAME "."
#define PROJ_ID 0x6666


union semun{
int val;
struct semid_ds * buf;
unsigned short * array;
struct seminfo * _buf;
};

int CreateSemset(int nums);
int InitSem(int semid,int nums,int inival);
int GetSemset(int nums);
int P(int semid,int who);
int V(int semid,int who);
int DestroySemset(int semid);

2、comm.c文件:

#include"comm.h"

static int CommSemset(int nums,int flag){
key_t key=ftok(PATHNAME,PROJ_ID);
if(key<0){
perror("ftok\n");
return -1;
}
int semid=semget(key,nums,flag);
if(semid<0){
perror("semget\n");
return -1;
}
return semid;
}


int CreateSemset(int nums){
return CommSemset(nums,IPC_CREAT|IPC_EXCL|0666);
}
int GetSemset(int nums){
return CommSemset(nums,IPC_CREAT);
}


int InitSem(int semid,int nums,int inival){
union semun sem;
sem.val=inival;
if(semctl(semid,nums,SETVAL,sem)<0){
perror("semctl\n");
return -1;
}
return 0;
}


int commPV(int semid,int who,int op){
struct sembuf _sf;
_sf.sem_num=who;
_sf.sem_op=op;
_sf.sem_flg=0;
if(semop(semid,&_sf,1)<0){
perror("semop\n");
return -1;
}
return 0;
}


int P(int semid,int who){
 return commPV(semid,who,-1);
}


int V(int semid,int who){
return commPV(semid,who,1);
}


int DestroySemset(int semid){
  if(semctl(semid,0,IPC_RMID)<0){
perror("semctl\n");
return -1;
}
}

3、test_comm.c文件:


#include"comm.h"


int main(){
int semid=CreateSemset(1);
InitSem(semid,0,1);
pid_t id=fork();
if(id==0){//child
int semid=GetSemset(0);
while(1){
//P(semid,0);
printf("A");
fflush(stdout);
usleep(123456);
printf("A");
fflush(stdout);
usleep(323456);
//V(semid,0);
}
}
else if(id>0){//father
while(1){
//P(semid,0);
printf("B");
fflush(stdout);
usleep(223456);
printf("B");
fflush(stdout);
usleep(123456);
//V(semid,0);
}
wait(NULL);
}
DestroySemset(semid);
return 0;
}

猜你喜欢

转载自blog.csdn.net/cx2479750196/article/details/80734210