Linux下的进程通信方式之System_V信号量

Linux下的通信方式之 System_V 信号量

对于每个信号量来说,都用一个结构体:

1
2
3
4
5
struct semid ds {
struct ipc_perm sem_perm;/* Ownership and permissions */
time_t sem_otime:/* Time of last semop() */time_t sem_ctime;/* Time of last change */
unsigned long sem_nsems;/* Number of semaphores in set */
};

信号量的创建和操作是以集合为单位的,一个集合包含一个或多个信号量。集合中的每个信号量都是一个整数,其值永远大于或等于 0。semop()系统调用允许调用者在一个信号量上加上一个整数、从一个信号量中减去一个整数、或等待一个信号量等于 0。后两个操作可能会导致调用者阻塞。

semget()

1
2
3
4
5
6
7
#include <sys/sem.h>

int semget(key_t key, int num_sems, int sem_flags);
第一个参数key:一个整型值对应内核中一个信号量对象,可以自己指定,不同信号量的key值不一样。不相关的进程可以通过它访问同一个信号量。程序对所有信号量的访问都是间接的,它先提供一个键,再由系统生成一个响应的信号标识符。 
第二个参数num_sems:指定需要的信号量数目。
第三个参数sem_flags:设置一组标志,与open函数的标志非常相似,包括信号量的权限等。IPC_CREAT标志是创建或者使用已有的信号量。             而IPC_CREAT和IPC_EXCL结合使用可以确保创建出的是一个新的、唯一的信号量,如果该信号量已存在,它将返回一个错误。             创建时给出的权限可以是:0600。 
返回值: 成功:正数(非零)值; 失败:-1

作用:第一次使用时创建信号量,以后使用时获取信号量。

注意:如果是创建新的信号量(一般在服务器进程中),则必须指定num_sems。如果是引用现有信号量,则将num_sems指定为0。

semot()

1
2
3
4
5
6
7
8
9
10
#include <sys/sem.h>

int semop(int sem_id, struct sembuf *sem_ops, size_t num_sem_ops);
第一个参数sem_id:信号量标识符。
第二个参数sem_ops是指向一个结构数组的指针,每个数组元素至少包含以下几个成员:
struct sembuf{
short sem_num; //信号量编号,除非使用一组信号量,否则它的取值为0
short sem_op; //信号量在一次操作中需要改变的数值。通常用到两个值,-1,也就是p操作,它等待信号量变为可用;+1,也就是V操作,它发送信号表示信号量现在已可用。
short sem_flg; //通过被设置为SEM_UNDO。表示操作系统会跟踪当前进程对这个信号量的修改情况,如果这个进程在没有释放该信号量的情况下终止,操作系统将自动释放该进程持有的信号量,防止其他进程一直处于等待状态。 
};第三个参数num_sem_ops指的是结构数组的元素个数。就上上面这个结构数组。

这里的第四个参数的类型是一个 menu 类型,虽然将 semun union的定义放在标准头文件中是比较明智的做法,但 SUSv3要求程序员显式地定义这个 union。然而,一些 UNIX实现在 <sys/sem.h> 中提供了这个定义。glibc较早以前的版本(2.0以下,包括 2.0)也提供了这个定义。为了与 SUSv3保持一致,glibc最近的版本并没有提供这个定义,并且通过将 <sys/sem.h> 中的_SEM_SEMUN_UNDEFINED宏的值定义为 1来表明这个事实(即使用 glibc编译的应用程序可以通过测试这个宏来确定程序自己是否需要定义 semun union)。

1
2
3
4
5
6
7
8
9
10
#ifndef _SEM_SEMUN_UNDEFINED
#define _SEM_SEMUN_UNDEFINED

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 */
};
#endif

作用:对信号量进行操作,包括P操作(等待)、V操作(发送)、获取信号量值。

semctl()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <sys/shm.h>

int semctl(int sem_id, int sem_num, int command,...);
第一个参数sem_id:信号量标识符
第二个参数sem_num:信号量编号,当用到成组的信号量,就要用到这个参数。它一般取值为0,表示这是第一个信号量。
第三个参数command:要采取的动作
有很多不同的值,有两个常用:
SETVAL:用来把信号量初始化成一个已知的值,这个值通过union semun中的val成员设置。
IPC_RMID:用于删除一个已经无需使用的信号量标识符。
第四个参数:可选。是否使用取决于所请求的命令。如果使用该参数,则其类型是semun

union semun{
  int val;
  struct semid_ds *buf;
  unsigned short *array;
};
一般只使用val这个成员,来为信号量赋初值。当信号量值为0时,进程会阻塞运行。

 作用:初始化或者删除信号量。

信号量的命令操作

在 Linux 中可以使用命令 ipcs -s 来查看系统中所有的信号量。删除信号量可以是 ipcs -s semid 来删除指定的信号量,其中 semid 是信号量标识符。


Linux下的进程通信方式之System_V信号量
https://ysc2.github.io/ysc2.github.io/2024/08/17/Linux下的进程通信方式之System_V信号量/
作者
Ysc
发布于
2024年8月17日
许可协议