Linux进程间通信-信号量

摘要:
如果有一个信号量SV。它们被设计为操作一组信号量,即一组信号,而不是一个信号。key参数是用于标识全局唯一信号量集的键值。想要通过信号量进行通信的进程需要使用相同的键值来创建或获取信号量。num_ sems参数指定要创建或获取的信号量集中的信号量数量。则必须指定值;假设获取了现有的信号量,则可以将其设置为0sem_flags参数指定一组标志。sem_num参数指定信号量集中操作的信号量的数量。

        当多个进程表同一时候訪问系统上的某个资源的时候,比方同一时候写一个数据库的某条记录,或者同一时候改动某个文件,就须要考虑进城的同步问题,以确保任一时刻仅仅有一个进程能够拥有对资源的独占式訪问。

通常。程序对共享资源的訪问的代码仅仅是非常短的一段。你就是这一段代码引发了进程之间的竞态条件。我们称这段代码为关键代码段,或者临界区。

        信号量是一种特殊的变量,它仅仅能取自然数并仅仅支持两种操作:等待(wait)和信号(signal),这两种操作更常见的称呼是P、V操作。

如果有信号量SV。则对它的P、V操作含义例如以下:

l  P(SV),假设SV的值大于0。就将它减1:;假设SV的值为0。则挂起进程的运行。

l  V(SV),假设有其它进程由于等待SV二挂起,,则换星之。假设没有,则将SV加1。

        信号量API主要包括3个系统调用:semget、semop和semctl。

它们都被设计为操作一组信号量,即信号量集,而不是单个信号量。

#include <sys/sem.h>
int semget(key_t key, int num_sems, int sem_flags);
int semop(int sem_id, struct sembuf *sem_ops, size_t num_sem_ops);
int semctl(int sem_id, int sem_num, int command, ...);

semget函数调用创建一个新的信号量集或者获得一个存在的信号量。key參数是一个键值,用来标识一个全局唯一的信号量集。就像文件名称全局唯一的标识一个文件一样。

要通过信号量通信的进程须要使用同样的键值来创建或获取该信号量。

num_sems參数指定要创建或获取的信号量集中信号量的数目。假设是创建信号量。则该值必须指定;假设是获取已存在的信号量,则能够设置为0

sem_flags參数指定一组标志。它低端的9个比特是该信号量的权限,其格式和含义都与系统调用open的mode參数同样。我们能够和IPC_CREAT标志做按位“或”运算,此时即使信号量存在。semget也不会报错。我们还能够用IPC_CREAT和IPC_EXCL标志来确保创建一组新的、唯一的信号量集,此时若信号量存在,semget返回错误并设置errno为EEXIST。

semget调用成功时返回一个正整数,它是信号量集的标识符;失败时返回-1,并设置errno


semop函数改变信号量的值。即运行P和V操作

sem_id參数是由semget调用返回的信号量集标识符,用以指定被操作的目标信号量集。

sem_ops參数指向一个sembuf结构体的数组,定义例如以下:

struct sembuf
{
    unsigned short int sem_num;
    short int sem_op;
    short int sem_flg;
};

sem_num是信号量集中信号量的编号,像数组一样,从0開始。

sem_op指定操作类型,可选值为正整数、0、负整数。每种类型的操作行为又受到sem_flg的影响。

sem_flg的可选值是IPC_NOWAIT、SEM_UNDO。IPC_NOWAIT指不管信号量操作是否成功,semop调用都将马上返回,类似于非堵塞。

SEM_UNDO指当进程退出时取消正在进行的semop操作。

num_sem_ops參数指定要运行的操作个数,即sem_ops数组中无素的个数。semop对数组中的每一个成员按数组顺序依次运行,该过程是原子操作。

semop成功时返回0,失败时返回-1并设置errno


semctl函数同意调用者直接对信号量直接操作

sem_id參数是由semget调用返回的信号量集标识符,用以指定被操作的信号量集。

sem_num參数指定被操作的信号量在信号量集中的编号。

command參数指定要运行的命令。

有的命令须要第4个參数。

其由用户自定义。但sys/sem.h中给出了推荐格式:

union semun
{
    int val;                         //用于SETVAL命令
    struct semid_ds *buf;             //用于IPC_STAT和IPC_SET命令
    unsigned short *array;            //用于GETALL和SETALL命令
    struct seminfo *__buf;            //用于IPC_INFO命令
};
sem_ctl成功时的返回值取决于command參数,失败时返回-1并设置errno


特殊键值 IPC_PRIVATE(semget函数的key參数)

        semget调用者能够给其Key參数传递一个特殊的键值IPC_PRICATE(其值为0)。这样不管该信号量是否已经存在,semget都将创建一个新的信号量。使用该键值创建的信号量并不是像其它的名字声称的那样是进程私有的。其它进程,尤其是子进程,也有方法来訪问这个信号量

使用IPC_PRIVATE的程序演示样例

#include <sys/sem.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

#define err_sys(msg) 
	do { perror(msg); exit(-1); } while(0)

union semun
{
	int val;
	struct semid_ds *buf;
	unsigned short int *array;
	struct seminfo * __buf;
};

void pv(int sem_id, int op)
{
	struct sembuf sem_b;
	sem_b.sem_num = 0;
	sem_b.sem_op = op;
	sem_b.sem_flg = SEM_UNDO;
	semop(sem_id, &sem_b, 1);
}

int main(void)
{
	int sem_id = semget(IPC_PRIVATE, 1, 0666); /* 该信号量集大小为1 */

	union semun sem_un;
	sem_un.val = 1; /* 该该值为2时,表示能够同一时候两个进程运行P操作(即进行-1操作) */
	semctl(sem_id, 0, SETVAL, sem_un); /* 对信号量集中第一个信号进行操作 */

	pid_t pid = fork();
	if(pid < 0)
		err_sys("fork");
	else if(pid == 0)
	{
		pv(sem_id, -1);
		printf("child get the sem and would sleep 5s.
");
		sleep(5);
		pv(sem_id, 1);
		exit(0);
	}
	else
	{
		pv(sem_id, -1);
		printf("parent get the sem and would sleep 5s.
");
		sleep(5);
		pv(sem_id, 1);
	}

	waitpid(pid, NULL, 0);
	semctl(sem_id, 0, IPC_RMID, sem_un);

	return 0;
}

參考:

        1、《Linux高性能server编程》 第13章 多进程编程/信号量

        2、Linux多进程编程

免责声明:文章转载自《Linux进程间通信-信号量》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇MySQL优化实例Python面试题目之(针对dict或者set数据类型)边遍历 边修改 报错dictionary changed size during iteration下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

inno setup读取注册表遇到的一个坑

一、背景       目前,公司针对PR开发的一个插件需要发布到64位系统上。该插件包括一个prm格式的文件和若干个DLL文件。其中,prm文件需要复制到PR公共插件目录下,DLL需要复制到Windows系统目录中去,这样插件才能正常的工作。公司现在要求发布插件时制作一个安装包,让用户点击安装包后自动将插件相关文件拷贝到相应目录去。本来用inno setu...

WireShark抓包分析(二)

简述:本文介绍了抓包数据含义,有TCP报文、Http报文、DNS报文。如有错误,欢迎指正。 1、TCP报文 TCP:(TCP是面向连接的通信协议,通过三次握手建立连接,通讯完成时要拆除连接,由于TCP 是面向连接的所以只能用于点对点的通讯)源IP地址:发送包的IP地址;目的IP地址:接收包的IP地址;源端口:源系统上的连接的端口;目的端口:目的系统上的连...

IBinder对象在进程间传递的形式(一)

命题 当service经常被远程调用时,我们经常常使用到aidl来定一个接口供service和client来使用,这个事实上就是使用Binder机制的IPC通信。当client bind service成功之后,系统AM会调用回调函数onServiceConnected将service的IBinder传递给client, client再通过调用aidl生...

JAVA编程心得-JAVA实现CRC-CCITT(XMODEM)算法

CRC即循环冗余校验码(Cyclic Redundancy Check):是数据通信领域中最常用的一种差错校验码,其特征是信息字段和校验字段的长度可以任意选定。 1 byte checksum CRC-16 CRC-16 (Modbus)CRC-16 (Sick)CRC-CCITT (XModem)CRC-CCITT (0xFFFF) CRC-CCITT...

vue中的父子组件之间的通信--新增、修改弹框

在一个vue页面中有时候内容会很多,为了方便编写查看,可以分为多个子组件,最后在父组件中引入对应的子组件即可。 下面这个是父子组件通信中的一个具体实例:新增、修改弹框。子组件中主要写了关于新增、修改的弹框, 子组件: 1.弹框: <div class="newDocuments"> <div class="newDocuments_...

中介者模式

Mediator 中介者模式:中介模式封装一系列的对象交互,集中管理相关对象之间复杂的沟通和交互。中介者使各对象不需要明显的相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。简单点来说,将原来两个直接引用或者依赖的对象拆开,在中间加入一个“中介”对象,使得两头的对象分别和“中介”对象引用或者依赖。 实现: Mediator:定义一个接...