Linux系统编程——进程同步与互斥:System V 信号量

2024-03-24 21:48

本文主要是介绍Linux系统编程——进程同步与互斥:System V 信号量,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

信号量概述

信号量广泛用于进程或线程间的同步和互斥,信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问。


编程时可根据操作信号量值的结果判断是否对公共资源具有访问的权限,当信号量值大于 0 时,则可以访问,否则将阻塞。PV 原语是对信号量的操作,一次 P 操作使信号量减1,一次 V 操作使信号量加1。


在实际应用中两个进程间通信可能会使用多个信号量,因此 System V 的信号量以集合的概念来管理,具体操作和 Posix 信号量大同小异,详情请点此链接:http://blog.csdn.net/tennysonsky/article/details/46496201。


信号量集合数据结构:struct semid_ds,此数据结构中定义了整个信号量集的基本属性。

/* Obsolete, used only for backwards compatibility and libc5 compiles */
struct semid_ds
{struct ipc_perm	sem_perm;		/* permissions .. see ipc.h */__kernel_time_t	sem_otime;		/* last semop time */__kernel_time_t	sem_ctime;		/* last change time */struct sem	*sem_base;		/* ptr to first semaphore in array */struct sem_queue *sem_pending;		/* pending operations to be processed */struct sem_queue **sem_pending_last;	/* last pending operation */struct sem_undo	*undo;			/* undo requests on this array */unsigned short	sem_nsems;		/* no. of semaphores in array */
};

信号量数据结构:struct sem,此数据结构中定义了信号量的基本属性。

/* One semaphore structure for each semaphore in the system. */
struct sem
{int	semval;		/* current value *信号量的值*/int	sempid;		/* pid of last operation *最后一个操作信号量的进程号*/struct list_head sem_pending; /* pending single-sop operations */
};


System V 信号量基本操作

使用 shell 命令操作信号量:

查看信号量:ipcs -s

删除信号量:ipcrm -s semid



以下函数所需头文件如下:

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/sem.h>


1)创建信号量集合

int semget(key_t key, int nsems, int semflg);

功能:

创建或打开一个信号量集合,该集合中可以包含多个信号量。

参数:

key:进程间通信键值,通过调用 ftok() 函数得到的键值,详情请点此链接:http://blog.csdn.net/tennysonsky/article/details/46331643。

nsems:创建的信号量的个数。如果只是访问而不创建则可以指定该参数为 0,一旦创建了该信号量,就不能更改其信号量个数,只要不删除该信号量,重新调用该函数创建该键值的信号量,该函数只是返回以前创建的值,不会重新创建。

semflg:标识函数的行为及信号量的权限,其取值如下:

IPC_CREAT:创建信号量。
IPC_EXCL:检测信号量是否存在。
位或权限位:信号量位或权限位后可以设置信号量的访问权限,格式和 open 函数的 mode_ t 一样open() 的使用请点此链接,但可执行权限未使用。

返回值:

成功:信号量集标识符

失败:返回 -1


2)控制信号量集合、信号量

int semctl(int semid, int semnum, int cmd, ...);

功能:

对信号量集合以及集合中的信号量进行操作。

参数:

semid:信号量集标识符。

semnum:集合中信号量的序号,指定对哪个信号量操作, 只对几个特殊的 cmd 操作有意义。

cmd:信号量控制类型。semctl() 函数可能有3个参数,也可能有4个参数,参数的个数由 cmd 决定。当有4个参数时,第4个参数为联合体:

union semun{int			val;	/*信号量的值*/struct semid_ds *buf;	/*信号量集合信息*/unsigned short  *array;/*信号量值的数组*/struct seminfo  *__buf;/*信号量限制信息*/
};
cmd 的取值如下:

GETVAL:获取信号量的值。此时函数有3个参数。semctl() 函数的返回值即为信号量的值。

SETVAL:设置信号量的值。此时函数有4个参数。第4个参数为联合体中的val,其值为信号量的值。 

IPC_STAT:获取信号量集合的信息。此时函数有4个参数。第4个参数为联合体中的__buf

IPC_SET:设置信号量集合的信息。此时函数有4个参数。第4个参数为联合体中的__buf

IPC_RMID:删除信号量集。此时函数有3个参数,第2个参数semnum不起作用。

GETALL:获取所有信号量的值。此时函数有4个参数,第2个参数semnum不起作用。第4个参数为联合体中的array,其值为用来存放所有信号量值的数组的首地址。

SETALL:设置所有信号量的值 。参数说明同上。

IPC_INFO:获取信号量集合的限制信息。此时函数有4个参数,第2个参数semnum不起作用。第4个参数为联合体中的__buf。

GETPID:获取信号的进程号,即最后操作信号量的进程。此时函数有3个参数。semctl() 函数的返回值即为信号的进程号。

GETNCNT:获取等待信号的值递增的进程数。此时函数有3个参数。semctl() 函数的返回值即为进程数。

GETZCNT:获取等待信号的值递减的进程数。此时函数有3个参数。semctl() 函数的返回值即为进程数。

返回值:

成功:0

失败:-1


3)操作信号量

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

功能:

操作信号量,主要进行信号量加减操作。

参数:

semid:信号量集标识符。
sops:操作信号量的结构体(struct sembuf)数组的首地址( 结构体定义在 sys/sem.h ),此结构体中的数据表明了对信号量进行的操作。

struct sembuf{unsigned short  sem_num;	/*信号量的序号*/short       sem_op;		/*信号量的操作值*/short       sem_flg;	/*信号量的操作标识*/
};

结构体成员使用说明如下:

sem_num:信号量集中信号量的序号

sem_op 取值如下:

sem_op > 0:信号量的值在原来的基础上加上此值。

sem_op < 0:如果信号量的值小于 semop 的绝对值,则挂起操作进程。如果信号量的值大于等于 semop 的绝对值,则信号量的值在原来的基础上减去 semop 的绝对值。

sem_op = 0:对信号量的值进行是否为 0 测试。若为 0 则函数立即返回,若不为 0 则阻塞调用进程。

sem_flag 取值如下: 

IPC_NOWAIT:在对信号量的操作不能执行的情况下使函数立即返回。

SEM_UNDO:当进程退出后,该进程对信号量进行的操作将被撤销。

nsops:操作信号量的结构体数组中元素的个数。

返回值:

成功:0

失败:-1


使用示例

示例一:

#include <sys/types.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <stdlib.h>
#include <stdio.h>int main(int argc, char *argv[])
{key_t key;//创建key值key = ftok(".", 'a');if(key == -1){perror("ftok");}//查看信号量system("ipcs -s");int semid;//1: 创建的信号量的个数semid = semget(key, 1, IPC_CREAT|0666); //创建信号量if(semid == -1){perror("semget");}system("ipcs -s"); //查看信号量//删去信号量// 0: 代表对第0个信号量进行操作// IPC_RMID:删除信号量集semctl(semid, 0, IPC_RMID);system("ipcs -s"); //查看信号量return 0;
}


运行结果如下:



示例二:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdlib.h>
#include <stdio.h>/*解决编译出错的问题*/
#define IPC_INFO 3int main(int argc, char *argv[])
{key_t key;//创建key值key = ftok(".", 'a');if(key == -1){perror("ftok");}system("ipcs -s"); //查看信号量int semid;//1: 创建的信号量的个数semid = semget(key, 1, IPC_CREAT|0666);//创建信号量if(semid == -1){perror("semget");}system("ipcs -s"); //查看信号量struct seminfo buf;/*//struct seminfo相关成员struct seminfo {int semmap;int semmni;int semmns;int semmnu;int semmsl;int semopm;int semume;int semusz;int semvmx;int semaem;};*///IPC_INFO:获取信号量集合的限制信息。//此时函数有4个参数,第2个参数semnum不起作用。semctl(semid, 0, IPC_INFO, &buf);printf("buf.semmni = %d\n", buf.semmni);printf("buf.semmns = %d\n", buf.semmns);printf("buf.semmnu = %d\n", buf.semmnu);printf("buf.semmsl = %d\n", buf.semmsl);printf("buf.semopm = %d\n", buf.semopm);printf("buf.semume = %d\n", buf.semume);printf("buf.semusz = %d\n", buf.semusz);printf("buf.semvmx = %d\n", buf.semvmx);printf("buf.semaem = %d\n", buf.semaem);//删去信号量// 0: 代表对第0个信号量进行操作// IPC_RMID:删除信号量集semctl(semid, 0, IPC_RMID);system("ipcs -s"); //查看信号量return 0;
}

运行结果如下:



示例三:

#include <sys/types.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <stdlib.h>
#include <stdio.h>int main(int argc, char *argv[])
{key_t key;//创建key值key = ftok(".", 'a');if(key == -1){perror("ftok");}//查看信号量system("ipcs -s");int semid;//1: 创建的信号量的个数semid = semget(key, 1, IPC_CREAT|0666); //创建信号量if(semid == -1){perror("semget");}system("ipcs -s"); //查看信号量int ret;/*//SETVAL: 设置信号量的值。此时函数有4个参数。第4个参数为联合体中的val,其值为信号量的值。 union semun{int			val;		//信号量的值struct semid_ds *buf;	//信号量集合信息unsigned short  *array;	//信号量值的数组struct seminfo  *__buf;	//信号量限制信息};*/ret = semctl(semid, 0, SETVAL, 20);if(ret == -1){perror("semctl");}//GETVAL:获取信号量的值。函数返回值即为信号量的值。ret = semctl(semid, 0, GETVAL);if(ret == -1){perror("semctl");}printf("ret = %d\n", ret);// 0: 代表对第0个信号量进行操作// IPC_RMID:删除信号量集semctl(semid, 0, IPC_RMID);system("ipcs -s");return 0;
}

运行结果如下:



示例四:

#include <sys/types.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>int main(int argc, char *argv[])
{key_t key;//创建key值key = ftok(".", 'a');if(key == -1){perror("ftok");}//查看信号量system("ipcs -s");int semid;//2: 创建的信号量的个数semid = semget(key, 2, IPC_CREAT|0666); //创建信号量if(semid == -1){perror("semget");}system("ipcs -s"); //查看信号量int ret;unsigned short sem_arry[2] = {30,20};/*//SETALL: 设置所有信号量的值。此时函数有4个参数,第2个参数semnum不起作用。第4个参数为联合体中的array,其值为用来存放所有信号量值的数组的首地址。union semun{int			val;		//信号量的值struct semid_ds *buf;	//信号量集合信息unsigned short  *array;	//信号量值的数组struct seminfo  *__buf;	//信号量限制信息};*/ret = semctl(semid, 0, SETALL, sem_arry);if(ret == -1){perror("semctl");}bzero(sem_arry, sizeof(sem_arry));//GETALL:获取所有信号量的值。此时函数有4个参数,第2个参数semnum不起作用。第4个参数为联合体中的array,其值为用来存放所有信号量值的数组的首地址。ret = semctl(semid, 0, GETALL, sem_arry);if(ret == -1){perror("semctl");}printf("sem_arry[0] = %d\n", sem_arry[0]);printf("sem_arry[1] = %d\n", sem_arry[1]);// IPC_RMID:删除信号量集semctl(semid, 0, IPC_RMID);system("ipcs -s");return 0;
}

运行结果如下:



示例五:

//此范例使用信号量来同步共享内存的操作
#include <stdio.h> 
#include <sys/types.h> 
#include <sys/ipc.h> 
#include <sys/sem.h> 
#include <sys/shm.h>
#include <unistd.h>
#include <sys/wait.h>#define SHM_KEY 0x33 
#define SEM_KEY 0x44 union semun 
{ int val; struct semid_ds *buf; unsigned short *array; 
}; int P(int semid) 
{ struct sembuf sb;/*//操作信号量的结构体struct sembuf{unsigned short  sem_num;//信号量的序号short       sem_op;		//信号量的操作值short       sem_flg;	//信号量的操作标识};	*/sb.sem_num = 0; sb.sem_op = -1;//SEM_UNDO:当进程退出后,该进程对信号量进行的操作将被撤销。sb.sem_flg = SEM_UNDO; //操作1个信号量if(semop(semid, &sb, 1) == -1){ perror("semop"); return -1; } return 0; 
} int V(int semid) 
{ struct sembuf sb;/*//操作信号量的结构体struct sembuf{unsigned short  sem_num;//信号量的序号short       sem_op;		//信号量的操作值short       sem_flg;	//信号量的操作标识};	*/sb.sem_num = 0; sb.sem_op = 1;//SEM_UNDO:当进程退出后,该进程对信号量进行的操作将被撤销。sb.sem_flg = SEM_UNDO; //操作1个信号量if(semop(semid, &sb, 1) == -1){ perror("semop"); return -1; } return 0; 
} int main(int argc, char **argv) 
{pid_t pid; int i, shmid, semid; int *ptr = NULL; union semun semopts; /*union semun{int			val;		//信号量的值struct semid_ds *buf;	//信号量集合信息unsigned short  *array;	//信号量值的数组struct seminfo  *__buf;	//信号量限制信息};*///创建一块共享内存, 存一个int变量if ((shmid = shmget(SHM_KEY, sizeof(int), IPC_CREAT | 0600)) == -1) { perror("msgget"); return -1;} //将共享内存映射到进程, fork后子进程可以继承映射ptr = (int *)shmat(shmid, NULL, 0);if (ptr == (int *)-1) { perror("shmat");return -1;}*ptr = 0; //赋值为0// 创建一个信号量用来同步共享内存的操作 if ((semid = semget(SEM_KEY, 1, IPC_CREAT | 0600)) == -1) { perror("semget"); return -1;} //初始化信号量  semopts.val = 1; if (semctl(semid, 0, SETVAL, semopts) < 0) { perror("semctl"); return -1;} if ((pid = fork()) < 0) { //创建进程perror("fork");_exit(0);}else if (pid == 0){ // Child// 子进程对共享内存加1 for (i = 0; i < 100000; i++) { P(semid); (*ptr)++; V(semid); printf("child: %d\n", *ptr); } } else { //Parent// 父进程对共享内存减1 for (i = 0; i < 100000; i++) { P(semid); (*ptr)--; V(semid); printf("parent: %d\n", *ptr); } //如果子进程结束,回收其资源wait(NULL);//如果同步成功, 共享内存的值为0 printf("finally: %d\n", *ptr); } return 0; 
} 

运行结果如下:



本教程示例代码下载请点此链接:http://download.csdn.net/detail/tennysonsky/9029479

这篇关于Linux系统编程——进程同步与互斥:System V 信号量的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/842890

相关文章

linux生产者,消费者问题

pthread_cond_wait() :用于阻塞当前线程,等待别的线程使用pthread_cond_signal()或pthread_cond_broadcast来唤醒它。 pthread_cond_wait() 必须与pthread_mutex 配套使用。pthread_cond_wait()函数一进入wait状态就会自动release mutex。当其他线程通过pthread

Linux 安装、配置Tomcat 的HTTPS

Linux 安装 、配置Tomcat的HTTPS 安装Tomcat 这里选择的是 tomcat 10.X ,需要Java 11及更高版本 Binary Distributions ->Core->选择 tar.gz包 下载、上传到内网服务器 /opt 目录tar -xzf 解压将解压的根目录改名为 tomat-10 并移动到 /opt 下, 形成个人习惯的路径 /opt/tomcat-10

RedHat运维-Linux文本操作基础-AWK进阶

你不用整理,跟着敲一遍,有个印象,然后把它保存到本地,以后要用再去看,如果有了新东西,你自个再添加。这是我参考牛客上的shell编程专项题,只不过换成了问答的方式而已。不用背,就算是我自己亲自敲,我现在好多也记不住。 1. 输出nowcoder.txt文件第5行的内容 2. 输出nowcoder.txt文件第6行的内容 3. 输出nowcoder.txt文件第7行的内容 4. 输出nowcode

【Linux进阶】UNIX体系结构分解——操作系统,内核,shell

1.什么是操作系统? 从严格意义上说,可将操作系统定义为一种软件,它控制计算机硬件资源,提供程序运行环境。我们通常将这种软件称为内核(kerel),因为它相对较小,而且位于环境的核心。  从广义上说,操作系统包括了内核和一些其他软件,这些软件使得计算机能够发挥作用,并使计算机具有自己的特生。这里所说的其他软件包括系统实用程序(system utility)、应用程序、shell以及公用函数库等

零基础STM32单片机编程入门(一)初识STM32单片机

文章目录 一.概要二.单片机型号命名规则三.STM32F103系统架构四.STM32F103C8T6单片机启动流程五.STM32F103C8T6单片机主要外设资源六.编程过程中芯片数据手册的作用1.单片机外设资源情况2.STM32单片机内部框图3.STM32单片机管脚图4.STM32单片机每个管脚可配功能5.单片机功耗数据6.FALSH编程时间,擦写次数7.I/O高低电平电压表格8.外设接口

16.Spring前世今生与Spring编程思想

1.1.课程目标 1、通过对本章内容的学习,可以掌握Spring的基本架构及各子模块之间的依赖关系。 2、 了解Spring的发展历史,启发思维。 3、 对 Spring形成一个整体的认识,为之后的深入学习做铺垫。 4、 通过对本章内容的学习,可以了解Spring版本升级的规律,从而应用到自己的系统升级版本命名。 5、Spring编程思想总结。 1.2.内容定位 Spring使用经验

通信系统网络架构_2.广域网网络架构

1.概述          通俗来讲,广域网是将分布于相比局域网络更广区域的计算机设备联接起来的网络。广域网由通信子网于资源子网组成。通信子网可以利用公用分组交换网、卫星通信网和无线分组交换网构建,将分布在不同地区的局域网或计算机系统互连起来,实现资源子网的共享。 2.网络组成          广域网属于多级网络,通常由骨干网、分布网、接入网组成。在网络规模较小时,可仅由骨干网和接入网组成

Windows/macOS/Linux 安装 Redis 和 Redis Desktop Manager 可视化工具

本文所有安装都在macOS High Sierra 10.13.4进行,Windows安装相对容易些,Linux安装与macOS类似,文中会做区分讲解 1. Redis安装 1.下载Redis https://redis.io/download 把下载的源码更名为redis-4.0.9-source,我喜欢跟maven、Tomcat放在一起,就放到/Users/zhan/Documents

Linux系统稳定性的奥秘:探究其背后的机制与哲学

在计算机操作系统的世界里,Linux以其卓越的稳定性和可靠性著称,成为服务器、嵌入式系统乃至个人电脑用户的首选。那么,是什么造就了Linux如此之高的稳定性呢?本文将深入解析Linux系统稳定性的几个关键因素,揭示其背后的技术哲学与实践。 1. 开源协作的力量Linux是一个开源项目,意味着任何人都可以查看、修改和贡献其源代码。这种开放性吸引了全球成千上万的开发者参与到内核的维护与优化中,形成了

Linux 下的Vim命令宝贝

vim 命令详解(转自:https://www.cnblogs.com/usergaojie/p/4583796.html) vi: Visual Interface 可视化接口 vim: VI iMproved VI增强版 全屏编辑器,模式化编辑器 vim模式: 编辑模式(命令模式)输入模式末行模式 模式转换: 编辑-->输入: i: 在当前光标所在字符的前面,转为输入模式