linux进程间通信——深入理解linux信号量

信号灯
信号灯与其他进程间通信方式不大相同,它主要提供对进程间共享资源访问控制机制 。相当于内存中的标志,进程可以根据它判定是否能够访问某些共享资源(临界区,类似于互斥锁),同时,进程也可以修改该标志 。除了用于访问控制外,还可用于进程同步 。
1. 信号灯概述信号灯与其他进程间通信方式不大相同,它主要提供对进程间共享资源访问控制机制 。相当于内存中的标志,进程可以根据它判定是否能够访问某些共享资源(临界区,类似于互斥锁),同时,进程也可以修改该标志 。除了用于访问控制外,还可用于进程同步 。信号灯有以下两种类型:

  • 二值信号灯:最简单的信号灯形式,信号灯的值只能取0或1,类似于互斥锁 。
    注:二值信号灯能够实现互斥锁的功能,但两者的关注内容不同 。信号灯强调共享资源,只要共享资源可用,其他进程同样可以修改信号灯的值;互斥锁更强调进程,占用资源的进程使用完资源后,必须由进程本身来解锁 。
  • 计算信号灯:信号灯的值可以取任意非负值(当然受内核本身的约束) 。
2. linux信号灯linux对信号灯的支持状况与消息队列一样,在red had 8.0发行版本中支持的是系统V的信号灯 。因此,本文将主要介绍系统V信号灯及其相应API 。在没有声明的情况下,以下讨论中指的都是系统V信号灯 。
注意,通常所说的系统V信号灯指的是计数信号灯集 。
3. 信号灯与内核1、系统V信号灯是随内核持续的,只有在内核重起或者显示删除一个信号灯集时,该信号灯集才会真正被删除 。因此系统中记录信号灯的数据结构(struct ipc_ids sem_ids)位于内核中,系统中的所有信号灯都可以在结构sem_ids中找到访问入口 。
2、下图说明了内核与信号灯是怎样建立起联系的:
linux进程间通信——深入理解linux信号量

文章插图
 
【linux进程间通信——深入理解linux信号量】其中:structipc_ids sem_ids是内核中记录信号灯的全局数据结构;描述一个具体的信号灯及其相关信息 。
其中,struct sem结构如下:
struct sem{int semval; // current valueint sempid; // pid of last operation}从上图可以看出,全局数据结构struct ipc_ids sem_ids可以访问到struct ipc_id ipcid的一个成员:struct kern_ipc_perm;而每个struct kern_ipc_perm能够与具体的信号灯集对应起来是因为在该结构中,有一个key_t类型成员key,而key则唯一确定一个信号灯集struct sem_array;同时,结构struct sem_array的最后一个成员sem_nsems确定了该信号灯在信号灯集中的顺序,这样内核就能够记录每个信号灯的信息了 。
kern_ipc_perm结构如下:
//内核中全局数据结构sem_ids能够访问到该结构;struct kern_ipc_perm{key_tkey;//该键值则唯一对应一个信号灯集uid_tuid;gid_tgid;uid_tcuid;gid_tcgid;mode_tmode;unsigned long seq;}/*系统中的每个信号灯集对应一个sem_array 结构 */struct sem_array {struct kern_ipc_perm sem_perm; /* permissions .. see ipc.h */time_t sem_otime; /* last semop time */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 long sem_nsems; /* no. of semaphores in array */};其中,sem_queue结构如下:
/* 系统中每个因为信号灯而睡眠的进程,都对应一个sem_queue结构*/struct sem_queue {struct sem_queue * next; /* next entry in the queue */struct sem_queue ** prev; /* previous entry in the queue, *(q->prev) == q */struct task_struct* sleeper; /* this process */struct sem_undo * undo; /* undo structure */int pid; /* process id of requesting process */int status; /* completion status of operation */struct sem_array * sma; /* semaphore array for operations */int id; /* internal sem id */struct sembuf * sops; /* array of pending operations */int nsops; /* number of operations */int alter; /* operation will alter semaphore */};需要C/C++ Linux服务器架构师学习资料私信“资料”(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等),免费分享
linux进程间通信——深入理解linux信号量

文章插图
 
4. 操作信号灯对信号灯的操作无非有下面三种类型:
1、打开或创建信号灯 与消息队列的创建及打开基本相同,不再详述 。


推荐阅读