进程间通信(3)---信号量

信号量引入

红绿灯,通过灯的红灯绿灯亮起通知横向纵向的汽车,这种形式就是信号量
程序中,有时存在一种特殊代码,最多只允许一个进程执行该部分代码。这部分区域,称为“临界区

然而在多进程并发执行时,当一个进程进入临界区,因某种原因被挂起时,其他进程就有可能也进入该区域(只能一个进程进), 所以这里期望它不要进来, 所以给他设置一个"红绿灯(信号量)"

什么是信号量

信号量,是一种特殊的变量。
只能对信号量执行P操作和V操作

P操作: 如果信号量的值 > 0, 则把该信号量减1, 如果信号量的值 ==0, 则挂起该进程。

V操作: 如果有进程因该信号量而被挂起,则恢复该进程运行, 如果没有进程因该信号量而挂起,则把该信号量加1
在这里插入图片描述
1.上图,信号量的值>0,这时1号进入
在这里插入图片描述
2.现在1号执行,p操作把信号量设为0,2号(其他进程)被阻塞在外面
在这里插入图片描述
3.线程执行完以后,这时执行v操作,会把信号量+1,恢复,让其他线程(唤醒)可以进来(黄灯只是帮助理解)
在这里插入图片描述
4.开始新一轮
注意P操作、V操作都是原子操作,即其在执行时,不会被中断。

注意:此指的“信号量”是指System V IPC的信号量,与线程所使用的信号量不同。该信号量,用于进程间通信

信号量的使用

semget()

原型:
 int semget(key_t key,   int nsems,   int semflg);
功能:

获取一个已存在的、或创建一个新的信号量量,返回该信号量的标识符

参数:

key:

键值,该键值对应一个唯一的信号量。类似于共享内存的键值。
不同的进程可通过该键值和semget获取唯一的信号量
特殊键值:
IPC_PRIVAT该信号量只允许创建者本身, 可用于父子进程间通信。

nsems

需要的信号量数目,一般取1

sem_flags:

 与共享内存的sem_flags类似。
 IPC_CREAT, 如果该信号量未存在,则创建该信号量如果该信号量已存在,也不发送错误。
 IPC_EXCL只有信号量不存在的时候,新的信号量才建立,否则就产生错误。
返回值:
  成功, 则返回一个正数
  失败, 返回-1

信号量的操作

semop()

原型:
int semop(int semid,   struct sembuf *sops,   unsigned nsops);
功能:

改变信号量的值,即对信号量执行P操作、或V操作。

参数:

semid

信号量标识符, 即semget的返回值

sops

  指向结构体数组的指针,元素类型为struct sembuf
struct sembuf 
{
short  sem_num;  //信号量组中的编号(即指定对哪个信号量操作)
//semget实际是获取一组信号量
//如果只获取了一个信号量,则该成员取0
short  sem_op;     //   -1,  表示P操作
//   1,  表示V操作
short  sem_flg;     // SEM_UNDO : 如果进程在终止时,没有释放信号量
// 如果不设置指定标志,应该设置为0 则,自动释放该信号量                     
}        

nsops

 表示第二个参数sops所表示的数组的大小,即表示有几个struct sembuf
返回值:
失败, 返回-1
成功, 返回 0

信号量的控制

semctl()

原型:
int  semctl(int semid,  int sem_num,  int cmd,  ...);
功能:

对信号量进行控制

参数:

semid

信号量标识符

sem_num

 信号量组中的编号,如果只有一个信号量,则取0

cmd

SETVAL   把信号量初始化为指定的值,具体的值由第4个参数确定

注意:只能对信号量初始化一次,如果在各进程中,分别对该信号量进行初始化,则可能导致错误!
IPC_RMID 删除信号量

参数4, 类型为

union  semun {
int     val;      // SETVAL命令要设置的值
struct  semid_ds  *buf;
unsigned short    *array;
}

注意:union semun类型要求自己定义有些Linux发行版在sys/sem.h中定义,有些发行版则没有定义。

可自定义如下:


#if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)						   
						#else
						    union semun {
						        int val;                             
						        struct semid_ds *buf;    
						        unsigned short int *array; 
						        struct seminfo *__buf;  
						    };
						#endif     

实例1:
不使用信号量,并发执行多个进程,观察对临界区的访问

#include <stdlib.h>
#include <stdio.h>

int main(void) 
{
	int i;
pid_t pd = fork();
	for (i=0; i<5; i++) {
		
		/* 模拟临界区----begin */
		printf("Process(%d) In\n", getpid());		
		sleep(1);
		printf("Process(%d) Out\n", getpid());
              /* 模拟临界区----end */ 
  
		sleep(1);
	}

	return 0;
}

此时临界区可以多个进程进行同时访问
实例2:
使用信号量,并发执行多个进程,观察对临界区的访问
对实例1改进

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdlib.h>
#include <stdio.h>

#if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)						   
#else
    union semun {
        int val;                             
        struct semid_ds *buf;    
        unsigned short int *array; 
        struct seminfo *__buf;  
    };
#endif     

//信号的初始化
static sem_initial(int semid) 
{
	int ret;
	
	union semun semun;
	semun.val = 1;
	ret = semctl(semid, 0, SETVAL, semun);
	if (ret == -1) {
		fprintf(stderr, "semctl failed!\n");
	}
	
	return ret;
}

//p操作
static int  sem_p(int semid)
{
	int ret;
	
	struct sembuf sembuf;
	sembuf.sem_op = -1;
	sembuf.sem_num = 0;
	sembuf.sem_flg = SEM_UNDO;
	ret = semop(semid, &sembuf, 1);	
	if (ret == -1) {
		fprintf(stderr, "sem_p failed!\n");
	}
	
	return ret;
}

//v操作
static int  sem_v(int semid)
{
	int ret;
	
	struct sembuf sembuf;
	sembuf.sem_op = 1;
	sembuf.sem_num = 0;
	sembuf.sem_flg = SEM_UNDO;
	ret = semop(semid, &sembuf, 1);	
	if (ret == -1) {
		fprintf(stderr, "sem_v failed!\n");
	}
	
	return ret;
}

int main(int argc, char* argv[]) 
{
	int i;
	int ret;
	int semid;

	/* 获取信号量 */
	semid = semget((key_t)1234, 1, 0666 | IPC_CREAT);
	if (semid == -1) {
		printf("semget failed!\n");
		exit(1);
	}

	/* 初始化信号量 */
	if (argc > 1) {
		ret = sem_initial(semid);
		if (ret == -1) {
			exit(1);
		}
	}

	for (i=0; i<5; i++) {

		if (sem_p(semid) == -1) {
			exit(1);
		}
		
		/* 模拟临界区----begin */
		printf("Process(%d) In\n", getpid());		
		sleep(1);
		printf("Process(%d) Out\n", getpid());
              /* 模拟临界区----end */ 

		if (sem_v(semid) == -1) {
			exit(1);
		}
			  
		sleep(1);
	}

    /* 删除信号量 */
	   
	return 0;
}

执行带参数轮流执行

./signal_demo init 

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值