0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Linux下进程通讯之信号量集

嵌入式技术 来源:嵌入式技术 作者:嵌入式技术 2022-08-19 19:55 次阅读

Linux下进程通讯之信号量集

1.简介

信号量集,就是由多个信号量组成的一个数组。 作为一个整体, 信号量集中所有的信号量使用同一个等待队列。 Linux 的信号量集为进程请求多个资源创造了条件。 Linux 规定, 当进程的一个操作需要多个共享资源时, 如果只成功获得了其中部分资源, 那么这个请求即告失败, 进程必须立即释放所有已获得资源, 已防止形成死锁。

信号量本质是一个计数器(不设置全局变量是因为进程间是相互独立的), 用于多进程对共享数据对象的读取, 它和管道有所不同, 它不以传送数据为主要目 录, 主要是用来保护共享资源(信号量也属于临界资源), 使得资源在同一时刻只能由一个进程独享。

2.工作原理

信号量只能进行等待和发送信号两种操作, 即 P(申请)和 V(释放)。
(1) P(申请):如果 SV 的值大于 0, 就给他减 1, 如果它的值为 0, 就挂起进程。
(2) V(释放):如果有其他进程等待信号量而被挂起, 就让他恢复运行, 如果没有进程因等待信号量而挂起, 就给他加 1。
在信号量进程 PV 操作时都是为了原子操作(原子操作:单指令的操作, 单条指令的执行时不会被打断的)。

3. 二值信号量

   二元信号量(Binary Semaphore)是最简单的一种锁(互斥锁), 它只有两种状态:占用与非占用。 所以它的引 用计数为 1。

4. 查看系统信号量命令

  1.查看信号量组:ipcs -s

[wbyq@wbyq 0414work]$ ipcs -s

--------- 信号量数组 -----------
键        semid      拥有者  权限     nsems     
0xd2350092 3          wbyq       666        1  

  2.查看信号量限制信息ipcs -ls

[wbyq@wbyq 0414work]$ ipcs -ls

--------- 信号量限制 -----------
最大数组数量 = 32000
每个数组的最大信号量数目 = 32000
系统最大信号量数 = 1024000000
每次信号量调用最大操作数 = 500
信号量最大值=32767

  3.查看信号量详细信息:ipcs -s -i

[wbyq@wbyq 0414work]$ ipcs -s -i 3
信号量数组 semid=3
uid=1000	 gid=1000	 cuid=1000	 cgid=1000
模式=0666,访问权限=0666
nsems = 1
otime = Fri Apr 29 10:27:21 2022  
ctime = Fri Apr 29 10:25:28 2022  
semnum     值        ncount     zcount     pid       
0          1          0          0          13747   

  4.创建信号量:ipcmk -S <信号量个数>

//创建信号量,信号量个数为5个
[wbyq@wbyq 0414work]$ ipcmk -S 5  
信号量 id:6
//查看创建的信号量
[wbyq@wbyq 0414work]$ ipcs -s -i 6

信号量数组 semid=6
uid=1000	 gid=1000	 cuid=1000	 cgid=1000
模式=0644,访问权限=0644
nsems = 5
otime = 未设置                 
ctime = Fri Apr 29 14:17:16 2022  
semnum     值        ncount     zcount     pid       
0          0          0          0          0         
1          0          0          0          0         
2          0          0          0          0         
3          0          0          0          0         
4          0          0          0          0  

  5.删除信号量:ipcrm -s

//删除信号量6
[wbyq@wbyq 0414work]$ ipcs -s 6

--------- 信号量数组 -----------
键        semid      拥有者  权限     nsems     
0xd2350092 3          wbyq       666        1         
0xa8132e52 4          wbyq       644        1         
0x9416e553 6          wbyq       644        5         

[wbyq@wbyq 0414work]$ ipcrm -s 6
//删除后结果
[wbyq@wbyq 0414work]$ ipcs -s

--------- 信号量数组 -----------
键        semid      拥有者  权限     nsems     
0xd2350092 3          wbyq       666        1         
0xa8132e52 4          wbyq       644        1   

5.信号量相关函数

#include 
#include 
#include 
int semget(key_t key, int nsems, int semflg);
函数功能:创建信号量
形参:key  键值,ftok产生
   nsems 信号量个数,几乎总是为1
     semflg 标志 IPC_CREAT|0666
返回值:失败返回-1,成功返回semid
int semctl(int semid, int semnum, int cmd, …);
函数功能:信号量控制函数
形参:semid semget函数返回值
   semnum 信号量数组下标,semnum=0表示第一个信号量
   cmd 控制命令:SETVAL 初始化信号量值
           IPC_RMID 删除信号量,删除只需要前三个参数即可
           GETVAL 获取信号量值
   可变参数共用体类型(需要自己定义):
      union semun {
         int val; /* 要设置的初始值 */
         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) */
         };
返回值:失败返回-1,成功根据cmd值返回
int semop(int semid, struct sembuf *sops, size_t nsops);
函数功能: 使用或者释放信号量pv操作
形参:semid semget函数返回值
   sops 信号量pv操作结构体
    struct sembuf sops
    {
     unsigned short sem_num; /* 信号量集下标,0表示第一个信号量 */
     short sem_op; /*>0则v操作,释放信号量
           <0则p操作,使用信号量*/
     short sem_flg; / 0 表示默认操作,没有信号量可用就等待。IPC_NOWAIT 表示不等待。 */
    }
返回值:成功返回信号量标志符,失败返回-1;

6.创建一个信号量示例

  (1)创建信号量

#include 
#include 
#include 
#include 
#include 
#include 
#include 
union semun 
{
   int  val;    /* 信号量值 */
   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) */
};
int main(int argc,char *argv[])
{
	if(argc!=2)
	{
		printf("格式:./a.out <信号量值>\n");
		return 0;
	}
	key_t key=ftok("semget.c",1234);//产生键值
	if(key==-1)
	{
		printf("产生键值失败res=%s\n",strerror(errno));
		return 0;
	}
	printf("key=%#x\n",key);
	int semid=semget(key,1,IPC_CREAT|0666);//创建1个信号量
	if(semid==-1)
	{
		printf("创建信号量集失败res=%s\n",strerror(errno));
		return 0;
	}
	printf("semid=%d\n",semid);
	union semun  sem;
	sem.val=atoi(argv[1]);
	/*初始化信号量*/
	if(semctl(semid,0,SETVAL,sem))
	{
		printf("初始化信号量值失败err=%s\n",strerror(errno));
		return 0;
	}
	/*获取信号量*/
	int val=semctl(semid,0,GETVAL,NULL);
	printf("信号量值:%d\n",val);
	/*通过系统命令查看信号量详细信息*/
	char buff[20];
	snprintf(buff,sizeof(buff),"ipcs -s -i %d",semid);
	system(buff);
	return 0;
}

  (2)使用信号量

#include 
#include 
#include 
#include 
#include 
#include 
#include 
union semun 
{
   int  val;    /* 信号量值 */
   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) */
};
int main(int argc,char *argv[])
{
	if(argc!=2)
	{
		printf("格式:./a.out <使用信号值>\n");
		return 0;
	}
	key_t key=ftok("semget.c",1234);//产生键值
	if(key==-1)
	{
		printf("产生键值失败res=%s\n",strerror(errno));
		return 0;
	}
	printf("key=%#x\n",key);
	int semid=semget(key,1,IPC_CREAT|0666);//创建1个信号量
	if(semid==-1)
	{
		printf("创建信号量集失败res=%s\n",strerror(errno));
		return 0;
	}
	printf("semid=%d\n",semid);
	/*获取信号量*/
	int val=semctl(semid,0,GETVAL,NULL);
	printf("信号量值:%d\n",val);
	/*pv操作*/
	struct sembuf sops=
	{
		.sem_num=0,//信号量下标
		.sem_op=atoi(argv[1]),
		.sem_flg=0//阻塞等待
	};
	int ret=semop(semid,&sops,1);
	printf("ret=%d\n",ret);
	/*获取信号量*/
	val=semctl(semid,0,GETVAL,NULL);
	printf("信号量值:%d\n",val);
	return 0;
}
pYYBAGL_eEaAH_D3AAKPgdfERdw629.png#pic_center

7.创建多个信号量

  (1)创建多个信号量

#include 
#include 
#include 
#include 
#include 
#include 
#include 
union semun 
{
   int  val;    /* 信号量值 */
   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) */
};
int main(int argc,char *argv[])
{
	if(argc!=4)
	{
		printf("格式:./a.out <信号量值1> <信号量值2> <信号量值3>\n");
		return 0;
	}
	key_t key=ftok("semget.c",1234);//产生键值
	if(key==-1)
	{
		printf("产生键值失败res=%s\n",strerror(errno));
		return 0;
	}
	printf("key=%#x\n",key);
	int semid=semget(key,3,IPC_CREAT|0666);//创建3个信号量
	if(semid==-1)
	{
		printf("创建信号量集失败res=%s\n",strerror(errno));
		return 0;
	}
	printf("semid=%d\n",semid);
	union semun  sem[3];
	sem[0].val=atoi(argv[1]);//初始化第一个信号量
	sem[1].val=atoi(argv[2]);//初始化第二个信号量
	sem[2].val=atoi(argv[3]);//初始化第三个信号量
	int i=0;
	for(i=0;i<3;i++)
	{
		/*初始化信号量*/
		if(semctl(semid,i,SETVAL,sem[i]))
		{
			printf("初始化信号量值失败err=%s\n",strerror(errno));
			return 0;
		}
		/*获取信号量*/
		int val=semctl(semid,i,GETVAL,NULL);
		printf("第%d个信号量值:%d\n",i,val);
	}
	/*通过系统命令查看信号量详细信息*/
	system("ipcs -s");
	return 0;
}

  (2)使用多个信号量

#include 
#include 
#include 
#include 
#include 
#include 
#include 
union semun 
{
   int  val;    /* 信号量值 */
   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) */
};
int main(int argc,char *argv[])
{
	if(argc!=2)
	{
		printf("格式:./a.out <使用信号值>\n");
		return 0;
	}
	key_t key=ftok("semget.c",1234);//产生键值
	if(key==-1)
	{
		printf("产生键值失败res=%s\n",strerror(errno));
		return 0;
	}
	printf("key=%#x\n",key);
	int semid=semget(key,3,IPC_CREAT|0666);//创建3个信号量
	if(semid==-1)
	{
		printf("创建信号量集失败res=%s\n",strerror(errno));
		return 0;
	}
	printf("semid=%d\n",semid);
	int i=0;
	/*获取信号量*/
	for(i=0;i<3;i++)
	{
		/*获取信号量*/
		int val=semctl(semid,i,GETVAL,NULL);
		printf("第%d个信号量值:%d\n",i,val);
	}
	/*pv操作*/
	
	struct sembuf sops[3]=
	{
		{
			.sem_num=0,//信号量下标
			.sem_op=atoi(argv[1]),
			.sem_flg=0//阻塞等待
		},
		{
			.sem_num=1,//信号量下标
			.sem_op=atoi(argv[1]),
			.sem_flg=0//阻塞等待
		},
		{
			.sem_num=2,//信号量下标
			.sem_op=atoi(argv[1]),
			.sem_flg=0//阻塞等待
		}
	};
	int ret=semop(semid,sops,3);//一次使用3个信号量
	printf("ret=%d\n",ret);
	/*获取信号量*/
	/*获取信号量*/
	for(i=0;i<3;i++)
	{
		/*获取信号量*/
		int val=semctl(semid,i,GETVAL,NULL);
		printf("第%d个信号量值:%d\n",i,val);
	}
	return 0;
}

  (3)运行效果

poYBAGL_eEeAB3BeAAJ-UvQXGSQ340.png#pic_center

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Linux
    +关注

    关注

    87

    文章

    11310

    浏览量

    209616
  • IPC
    IPC
    +关注

    关注

    3

    文章

    347

    浏览量

    51933
  • 进程
    +关注

    关注

    0

    文章

    203

    浏览量

    13962
收藏 人收藏

    评论

    相关推荐

    FreeRTOS信号量使用教程

    信号量是操作系统中重要的一部分,信号量一般用来进行资源管理和任务同步, FreeRTOS中信号量又分为二值信号量、 计数型信号量、互斥
    的头像 发表于 12-19 09:22 3229次阅读
    FreeRTOS<b class='flag-5'>信号量</b>使用教程

    linux操作系统进程通信设计

    用户进程。我们可以从头文件/usr/src/linux/include/linux/sem.h中看到内核用来维护信号量状态的各个结构的定义。信号量
    发表于 04-16 09:17

    进程间通信 信号量

    定义:信号量是一个计数器,用于多进程对共享数据对象的存取访问控制。为了获得共享资源,进程需要执行下列操作信号量使用步骤:1:初始化信号量--
    发表于 07-20 10:15

    关于ucosii中信号量的问题

    我定义了一个信号量: OS_FLAG_GRP *FlagTest1;INT8U Flag_Error在任务初始化之前,创建:FlagTest1=OSFlagCreate((OS_FLAGS)0
    发表于 10-19 16:49

    芯灵思SinlinxA33开发板的Linux内核信号量学习

    用户进程。我们可以从头文件/usr/src/linux/include/linux/sem.h 中看到内核用来维护信号量状态的各个结构的定义。信号量
    发表于 02-20 15:50

    芯灵思SinlinxA64开发板 Linux内核信号量学习

    信号量状态的各个结构的定义。信号量是一个数据集合,用户可以单独使用这一合的每个元素。要调用的第一个函数是semget,用以获得一个信号量ID。Li
    发表于 03-15 16:10

    linux多线程编程中,一次等待多个信号量怎么解决

    linux多线程(非进程)编程中,一次等待多个信号量怎么解决?并且等到信号量来了后,能判断是那一个​功能如同window
    发表于 06-17 05:55

    你了解Linux 各类信号量

    内核信号量与用户信号量,用户信号量分为POXIS信号量和SYSTEMV信号量,POXIS信号量
    发表于 05-04 17:19 2519次阅读
    你了解<b class='flag-5'>Linux</b> 各类<b class='flag-5'>信号量</b>?

    Linux IPC POSIX 信号量

    //获得信号量sem的当前的值,放到sval中。如果有线程正在block这个信号量,sval可能返回两个值,0或“-正在block的线程的数目”,Linux返回0//成功返回0,失败返回-1设
    发表于 05-16 17:39 928次阅读

    Linux IPC System V 信号量

     立即销毁指定的信号量,调用的进程的的effective UID必须和信号量的创建者或所有者相匹配,或者这个
    发表于 04-02 14:46 330次阅读

    Linux 多线程信号量同步

    直到系统将资源分配给该进程(进入等待队列,一直等到资源轮到该进程)。V操作:如果在该信号量的等待队列中有进程在等待资源,则唤醒一个阻塞进程;
    发表于 04-02 14:47 400次阅读

    Linux信号量(2):POSIX 信号量

    上一章,讲述了 SYSTEM V 信号量,主要运行于进程之间,本章主要介绍 POSIX 信号量:有名信号量、无名信号量。 POSIX
    的头像 发表于 10-29 17:34 721次阅读

    LINUX内核的信号量设计与实现

    控制路径可以睡眠。我们从 LINUX内核信号量最直观的设计/实现出发,通过一步步改进,揭示在x86平台上完整的信号量设计/实现,然后探讨在不同平台上通用的信号量设计/实现。
    发表于 01-14 16:55 18次下载

    LINUX内核的信号量设计与实现

    控制路径可以睡眠。我们从 LINUX内核信号量最直观的设计/实现出发,通过一步步改进,揭示在x86平台上完整的信号量设计/实现,然后探讨在不同平台上通用的信号量设计/实现。
    发表于 01-14 16:55 5次下载

    使用Linux信号量实现互斥点灯

    信号量常用于控制对共享资源的访问,有计数型信号量和二值信号量之分。初始化时信号量值大于1的,就是计数型信号量,计数型
    的头像 发表于 04-13 15:12 813次阅读
    使用<b class='flag-5'>Linux</b><b class='flag-5'>信号量</b>实现互斥点灯