linux之信号量

目录

  • 信号量的概念及特点
  • linux 信号量的相关函数
  • 案例
(一)信号量的概念及特点

上次分析的信号和这次信号量长得像,但不是一个东西。

  • 信号:相当于软中断
  • 信号量:调协进程对共享资源的访问,让一个临界区同一时间只有一个线程在访问它,为什么要引入信号量呢?举个例子,有两个进程要使用打印机,但是没有保证在同一时刻只有一个进程使用打印机,那打印的东西就会是乱的了。信号量在进程的同步与互斥有很重要的应用。
(二) linux 信号量的相关函数

(1)新建信号量
有些思路像消息队列,可以一起学习,类比起来,下面是消息队列的文章:
https://blog.csdn.net/weixin_39956356/article/details/86652957

//在程序的开头,就要使用这个函数,就像open一样返回的句柄,供semop,semctl使用
int semget(key_t key,int nsems,int semflg)

参数分析:

  • key:信号量关联的标识符,本质是整数,可以弄个整数强制转化下就可以了,如(key_t)1234
  • nsems:需要的信号量数目,它的值几乎总是1
  • msgflg:权限+创建(如果不存在则创建,存在则打开)
    IPC_CREAT:如果内核中没有此队列则创建它
    当IPC_EXCL和IPC_CREAT一起使用时,如果队列已经存在,则失败。

注:我们经常使用的两个组合:
IPC_CREAT|IPC_EXCL|0666IPC_CREAT|0666

  • IPC_CREAT|IPC_EXCL|0666:如果没有这个信号则创建一个, 如果已经存在则产生一个存在错误
  • IPC_CREAT|0666:没有创建信号,有则返回已经存在的信号 ID

在这里插入图片描述

(2)信号量控制函数

int semctl(int semid,int semnum,int cmd, /*union semun arg*/)
  • semid:semget返回的id
  • semnum:信号量集中的第 semnum 个信号量。它的取值范围:0~nsems-1 。(The semaphores in a set are numbered starting at 0.),从0开始的
  • cmd:要操作的具体命令
    以下是两个最常见的命令:
    SETVAL:用于初始化信号量
    IPC_RMID:用于删除信号量

如:初始化这么使用
注:编号从0~nsems-1的。

//初始化信号量--之前设置了一个信号量,所以从0开始初始化,而设置值要通过联合赋值
void cre_semaphore()
{
	union semun sem_union;									
	
	sem_union.val = 1;												//初始化信号量
	if(semctl(semid, 0, SETVAL, sem_union) == -1)
	{
		fprintf(stderr, "Initializing semaphore error!!!\n");
		exit(EXIT_FAILURE);
	}	
}

如:删除这么使用

//删除信号量-只有一个
void del_semaphore()
{
	union semun sem_union;
	
	if(semctl(semid, 0, IPC_RMID, sem_union) == -1)
	{
		fprintf(stderr, "Failed to delete semaphore!!!\n");
		exit(EXIT_FAILURE);
	}
}

在这里插入图片描述
在这里插入图片描述
这里涉及一个联合–要自己定义要自己定义:

//头文件在/usr/include/bits/sem.h
  union semun {
      int              val;    /* Value for SETVAL */
      struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
      unsigned short  *array;  /* Array for GETALL, SETALL */
      struct seminfo  *__buf;  /* Buffer for IPC_INFO (Linux-specific) */
  };
  • 第一个设置值的,第二个IPC_STAT、IPC_SET 使用的时候才用到,第三个GETALL、SETALL 使用的时候才用到

(3)p,v操作
semop可以实现p,v操作,还涉及一个很重要的结构体struct sembuf

int semop(int semid, struct sembuf *sops, unsigned nsops);

先讲结构体struct sembuf:

//源文件/usr/include/sys/sem.h
struct sembuf
{
  unsigned short int sem_num;   /* semaphore number */
  short int sem_op;             /* semaphore operation */
  short int sem_flg;            /* operation flag */
};

分析:

  • sem_num:除非使用一组信号量,否则它为0,这里就一个,所以为0
  • sem_op:操作时需要改变的数据, -1即P等待, +1即V发送信号
  • sem_flg:有两个值:IPC_NOWAIT和SEM_UNDO
    通常为SEM_UNDO,使操作系统跟踪信号,万一用户忘记处理,操作系统会自动跟踪解决

之后是semop参数分析:

  • semid:信号集的识别码
  • sops:指向struct sembuf指针
  • nsops:信号操作结构的数量,恒大于或等于1
  • 返回值:If successful semop() return 0; otherwise they return -1 with errno

P操作:-在man文档里面有个用法,其实差不多

//对信号量做减1操作,即等待P(sv) 
int semaphore_p()
{
	struct sembuf sem_b;										//先定义个struct sembuf
	 
	//struct sembuf赋值
	sem_b.sem_num = 0;
	sem_b.sem_op = -1;											//p操作-1
	sem_b.sem_flg = SEM_UNDO;									//系统跟踪
	
	if(semop(semid, &sem_b, 1) == -1)
	{
		fprintf(stderr, "Semaphore_p error!!!\n");
		return 1;												//出错
	}
	return 0;
}

V操作:

//对信号量做加1操作,即V(sv) 
int semaphore_v()
{
	struct sembuf sem_b;										//先定义个struct sembuf
	
	//struct sembuf赋值
	sem_b.sem_num = 0;
	sem_b.sem_op = 1;											//V操作+1
	sem_b.sem_flg = SEM_UNDO;									//系统跟踪
	
	if(semop(semid, &sem_b, 1) == -1)
	{
		fprintf(stderr, "semaphore_v error!!!\n");
		return 1;												//出错
	}
	return 0;
}

(三)案例

很简单的想法:就是两个进程一个带X,一个带a,谁先抢到临界区,谁就进去,在进去的时候打印一下,出来的时候打印下。即都是两个两个的,没有交替的!

#include <unistd.h>  
#include <sys/types.h>  
#include <sys/stat.h>  
#include <fcntl.h>  
#include <stdlib.h>  
#include <stdio.h>  
#include <string.h>  
#include <sys/sem.h>  
  
/****************************************************************************************
**										进程互斥
** semget():创建信号量--类比于open
**	 int semget(key_t key, int nsems, int semflg);
** key:
**	 信号量关联的标识符
** semflg:信号量的建立标志和存取权限
**	 IPC_CREAT:如果内核中没有此信号量则创建它
**	 当IPC_EXCL和IPC_CREAT一起使用时,如果信号量已经存在,则失败
** RETURN VALUE
** 	 执行成功则返回信号量的标识符(a nonnegative integer-非负整数),否则返回-1
** 注意:
**		key_t本质是整数,你自己弄个整数强制转化下就可以了,如(key_t)1234	 		
** semop():PV操作
** 	 int semop(int semid, struct sembuf *sops, size_t nsops);
** semid : 信号量的标识码(semget返回的id)
** *sops : 系统已经定义好了,自己赋值就可以了,和msgsnd不同哦,要自己定义。
** nsops : 信号操作结构的数量,恒大于或等于1
** RETURN VALUE
**   On success, zero is returned. On error, -1 is returned
** semctl():信号量的控制函数
** 	 int semctl(int semid,int semnum,int cmd, union semun arg(可选));
** semid : 信号量的标识码(semget返回的id)
** cmd   : SETVAL:用于初始化信号量
** 		   IPC_RMID:用于删除信号量
** union semun : 这个联合和cmd相关,要自己定义
** RETURN VALUE
**   成功返回数据长度,错误返回-1
** fflush():清除读写缓冲区,需要立即把输出缓冲区的数据进行物理写入时
** 	  int fflush(FILE *stream);
** stream: 几种标准的流/文件句柄
** RETURN VALUE
** 	 Upon successful completion 0 is returned.  Otherwise, EOF is returned and errno
****************************************************************************************/

union semun 
{
	int              val;    									//设置信号量的初始值   
	struct semid_ds *buf;    									//Buffer for IPC_STAT, IPC_SET
	unsigned short  *array; 									//Array for GETALL, SETALL
};

static int semid = 0;											//全局变量,供所有函数使用

static int cre_semaphore();								    		//初始化信号量
static void del_semaphore();											//删除信号量
static int semaphore_p();												//等待--semaphore_p---1
static int semaphore_v();												//发送信号--semaphore_v--1


int main(int argc, char *argv[])
{
	char message = 'X';
	int i = 0;
	
	semid = semget((key_t)1234, 1, 0666|IPC_CREAT);				//创建信号量
	
	if(argc>1)
	{
		//程序第一次被调用,初始化信号量  
		if(!cre_semaphore()){
			fprintf(stderr, "Failed to initialize semaphore\n");  
            exit(EXIT_FAILURE);
		}
		
		//设置参数的第一个参数第一个,message是char,所以要一个就可以了,比如你输入./seml a---a就是message了
		message = argv[1][0];
		sleep(2);
	}
	
	
	for(i = 0; i < 10; i++)
	{
		//进入临界区
		if(semaphore_p())
			exit(EXIT_FAILURE);
		printf("%c",message);
		//清除缓冲区,然后随机休眠
		fflush(stdout);
		sleep(rand()%3);
		
		//离开临界区前再一次向屏幕输出数据  
		printf("%c",message);
		fflush(stdout);
		if(semaphore_v())
			exit(EXIT_FAILURE);
		sleep(rand()%2);
	}
	
	
	sleep(10);
	printf("\n%d---finished!!!\n",getpid());
	
	
	//如果程序是第一次被调用,则在退出前删除信号量  
	if(argc > 1)
	{
		sleep(3);
		del_semaphore();
	}
	
	exit(EXIT_SUCCESS);
}


//初始化信号量--之前设置了一个信号量,所以从0开始初始化,而设置值要通过联合赋值
static int cre_semaphore()
{
	union semun sem_union;									
	
	sem_union.val = 1;												//初始化信号量
	if(semctl(semid, 0, SETVAL, sem_union) == -1)
        return 0;  
    return 1; 	
}

//删除信号量-只有一个
static void del_semaphore()
{
	union semun sem_union;										//先定义个struct sembuf
	
	if(semctl(semid, 0, IPC_RMID, sem_union) == -1)
		fprintf(stderr, "Failed to delete semaphore!!!\n");
}


//对信号量做减1操作,即等待P(sv) 
static int semaphore_p()
{
	struct sembuf sem_b;										//先定义个struct sembuf
	 
	//struct sembuf赋值
	sem_b.sem_num = 0;
	sem_b.sem_op = -1;											//p操作-1
	sem_b.sem_flg = SEM_UNDO;									//系统跟踪
	
	if(semop(semid, &sem_b, 1) == -1)
	{
		fprintf(stderr, "Semaphore_p error!!!\n");
		return 1;												//出错
	}
	return 0;
}

//对信号量做加1操作,即V(sv) 
static int semaphore_v()
{
	struct sembuf sem_b;										//先定义个struct sembuf
	
	//struct sembuf赋值
	sem_b.sem_num = 0;
	sem_b.sem_op = 1;											//V操作+1
	sem_b.sem_flg = SEM_UNDO;									//系统跟踪
	
	if(semop(semid, &sem_b, 1) == -1)
	{
		fprintf(stderr, "semaphore_v error!!!\n");
		return 1;												//出错
	}
	return 0;
}

输出:
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值