linux中的信号量

问题引入

当我们想让我们的程序同时只能被一个进程运行时,我们需要使用到信号量来实现.

何为信号量

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

P操作, 如果信号量的值 > 0,    则把该信号量减1
           如果信号量的值  ==0,  则挂起该进程。
           
V操作:  如果有进程因该信号量而被挂起,则恢复该进程运行
           如果没有进程因该信号量而挂起,则把该信号量加1
           
注意:P操作、V操作都是原子操作,即其在执行时,不会被中断。

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

相关API

  1. 信号量的获取
    semget
    原型:int semget(key_t key, int nsems, int semflg);
    功能:获取一个已存在的、或创建一个新的信号量量,返回该信号量的标识符
    参数:key, 键值,该键值对应一个唯一的信号量。类似于共享内存的键值。
    不同的进程可通过该键值和semget获取唯一的信号量。特殊键值:IPC_PRIVAT该信号量只允许创建者本身, 可用于父子进程间通信。

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

sem_flags, 与共享内存的sem_flags类似。IPC_CREAT, 如果该信号量未存在,则创建该信号量如果该信号量已存在,也不发送错误。

返回值: 成功,则返回一个正数
失败, 返回返回-1

  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
  1. 信号量的控制
    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

测试demo

预期实现

打开两个终端,同时运行这段程序.会观察到一个进程阻塞的效果,当一个进程在执行这段程序时,另一个进程阻塞在外面. 当次进程执行到 v 操作时代表 此进程执行完毕,阻塞在外的进程立即开始执行,运行到 p 操作时,进程开始阻塞 只允许允许此进程

demo源码

#include<stdio.h>
#include<string.h>
#include<sys/types.h>
#include<sys/sem.h>
#include<stdlib.h>
#include<unistd.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 int sem_inital(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;
	
}

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;
	
}

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 failde!\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("semid get failed\n");
		exit(1);
		
	}
	
	/*初始化信号*/
	
	if(argc>1){
		ret = sem_inital(semid);
		if(ret == -1){
			exit(1);
			
		}
		
	}
/*这个循环便于我们观察执行的效果,for 循环交替阻塞执行函数, p 操作 v操作 交替进行,会看到这样一个效果 本来是 sleep 5s 之后打印数据,会看到一个  "爆破式的" 打印,当一个执行到 v 操作,那个等候多时的进程的程序 立即执行
*/
	for(i=0;i<5;i++){
		if(sem_p(semid)==-1){
			exit(1);
			
		}
		
		printf("Process(%d) In\n",getpid());
		sleep(5);
		printf("Process(%d) Out\n",getpid());
		
		if(sem_v(semid)==-1){
			
			exit(1);
		}
		sleep(1);
		
	}
	
	return 0;
}

测试效果

在这里插入图片描述

总结

信号量的作用是想让一个程序不能被指定个数的进程同时执行,项目中给的 demo 是测试的 不能被 2 个进程同时执行.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值