linux系统编程——信号量的编程学习

信号量概述:
信号量和前面所说的IPC(管道,FIFO,消息队列等)有所不同,它是一个计数器,用于为多个进程提供对共享数据的访问,信号量是一种特殊的变量,访问具有原子性,
信号量只允许对它进行两个操作:
<1>等待信号量
当信号量的值为0时,程序等待;当信号量的值大于0时,信号量的值减一,程序继续运行;
<2>发送信号量
将信号量的值加一;
我们使用信号量,来解决进程或线程间共享资源引发的同步问题:
(1)测试控制该资源的信号量
(2)若此信号量的值为正,则进程可以使用该资源,在这种情况下,进程会将信号量值减一,表示使用了一个资源单位。
(3)否则,若此信号量的值为0,则进程进入休眠状态,直至信号量大于0,进程被唤醒后返回步骤(1)。
注:当进程不再使用由一个信号量控制的共享资源时,该信号量值加一,如果有进程正在休眠等待此信号,则唤醒他们,为了正确实现信号量,信号量值得测试和减一操作是原子操作,为此,信号量通常是在内核实现的。
一,信号量的使用
信号量相关API介绍

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

int semget(key_t key, int nsems, int semflg);//获取信号量id或者创建信号量  
//参数1为key值(通过ftok获取),
//参数2为该集合中的信号量数
//参数3为对信号量id操作创建信号量若存在返回id值(IPC_CREAT)
int semctl(int semid, int semnum, int cmd, ...);//用于信号量的初始化和删除
//semid 信号集表示符
//semnum 信号量数组上的下标,表示某一个信号量
//cmd 命令 :
//SET_VAL 用联合体中val成员的值设置信号量集合中单个信号量的值。
//IPC_RMID 从内核删除信号量
int semop(int semid, struct sembuf *sops, size_t nsops);//完成对信号量的P操作和V操作
//semid 信号量集标识符
//sops  指向进行操作的信号量集结构体数组的首地址
//nsops 进行操作信号量的个数,即sops结构变量的个数,须大于或者等于1,最常见的设置此值等于1,只完成对一个信号量的操作。
struct sembuf{
	short semnum;
	/*信号量集合中的信号量编号*/
	short val;
	/*若val>0进行V操作信号量加val,表示进程释放控制的资源 */

   /*若val<0进行P操作信号量值减val,若(semval-val)<0(semval为该信号量值),则调用进程阻塞,直到资源可用;若设置IPC_NOWAIT不会睡眠,进程直接返回EAGAIN错误*/

    /*若val==0时阻塞等待信号量为0,调用进程进入睡眠状态,直到信号值为0;若设置IPC_NOWAIT,进程不会睡眠,直接返回EAGAIN错误*/

    short flag; 
     /*0 设置信号量的默认操作*/

     /*IPC_NOWAIT设置信号量操作不等待*/

     /*SEM_UNDO 选项会让内核记录一个与调用进程相关的UNDO记录,如果该进程崩溃,则根据这个进程的UNDO记录自动恢复相应信号量的计数值*/

};

相关代码实现:

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>
#include <stdlib.h>
  
union semun {
               int              val;   
               struct semid_ds *buf;    
               unsigned short  *array;  
               struct seminfo  *__buf; 
                                          
};


void P_get_handler(int id)
{
	struct sembuf sops;
	sops.sem_num = 0;//信号量集合中的信号量编号
	sops.sem_op = -1;//表示资源被占用
	sops.sem_flg = SEM_UNDO;//设置为阻塞当val == 0时
    semop(id,&sops,1);
	printf("get key !\n");
}


void V_ret_handler(int id )
{
	struct sembuf sops;
	
    sops.sem_num = 0;//信号量集合中的信号量编号
    sops.sem_op = 1;//进程释放控制的资源
    sops.sem_flg = SEM_UNDO;//当val值=0时阻塞
    
	semop(id,&sops,1);
	printf("put back key!\n");

}

int main()
{
	
	key_t key;
	int semid;
	union semun initsem;
	int  pid;

	key = ftok(".",'1');//获取key值
	semid = semget(key,1,IPC_CREAT|0666)
	if(semid == -1)
	{
		exit(0);
	}
	
	initsem.val = 0;//val =0,让先运行的进程阻塞

	semctl(semid,0,SETVAL,initsem);//初始化信号量val的值
	pid = fork();//父进程创建子进程

	if(pid > 0 ){//父进程
		get_handler(semid);//如果父进程先运行由于val=0会造成进程阻塞现象直到val>0会继续运行
		printf("this is father!\n");
		return_handler(semid);//运行完成后进行V操作
		semctl(semid,0,IPC_RMID);//从内核中删除信号量
	}else if(pid == 0){
	//	return_handler(semid);
		printf("this is child!\n");
		return_handler(semid);
	}else if(pid == -1){//创建进程失败
		printf("fork failed!\n");
	}
	
	return 0;
}

注:不管运行多少次,每次都是子进程先运行,后父进程再运行。最后从内核中删除信号量

tips:
程序的原子性:整个程序中所有的操作,要么全部完成,要么全部不完成。不能停滞在中间某个环节。
原子性操作:原子性在一个操作中是不可以中断的,要么全部执行成功要么全部执行失败,有着“同生共死”的感觉。即使在多个线程一起执行时,一个操作一旦开始,也不会被别的线程所干扰。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值