System V 信号量是 Unix 和类 Unix 操作系统中的一种进程间通信(IPC)机制,它允许进程之间通过信号量进行同步。System V 信号量是 System V IPC 标准的一部分,该标准还包括共享内存和消息队列等其他 IPC 机制。
概述
System V 信号量允许进程通过增减信号量值来同步访问共享资源。它们主要用于解决互斥问题,并可以跨进程使用。System V 信号量支持多种操作,包括创建、增减信号量值、删除等。
特点
- 持久性:信号量可以持久存储在内核中,即使创建它的进程终止后仍然存在。
- 跨进程:信号量可以在没有亲缘关系的进程之间进行通信。
- 属性配置:可以配置信号量集的属性,如最大信号量值等。
API
System V 信号量主要由以下几个函数组成:
创建信号量集
- semget():
int semget(key_t key, int nsems, int semflg)
: 创建或打开信号量集。- 参数
key
是标识信号量集的键值,nsems
指定信号量集中信号量的数量,semflg
指定标志位,可以包含IPC_CREAT
来创建新的信号量集,也可以包含IPC_EXCL
来防止创建已存在的信号量集。
增减信号量值
- semop():
int semop(int semid, struct sembuf *sops, size_t nsops)
: 对信号量集执行操作。- 参数
semid
是信号量集标识符,sops
指向一系列信号量操作的数组,nsops
是操作数组的大小。
删除信号量集
- semctl():
int semctl(int semid, int semnum, int cmd, ... /* union semun arg */)
: 控制信号量集。- 参数
semid
是信号量集标识符,semnum
指定信号量集中的信号量编号,cmd
指定命令,如IPC_RMID
用于删除信号量集,后面的参数根据命令的不同而变化。
信号量操作结构体
为了使用 System V 信号量,你需要定义一个信号量操作结构体 sembuf
,它包含三个成员:
1struct sembuf {
2 short sem_num; // 信号量编号
3 short sem_op; // 操作值
4 short sem_flg; // 操作标志
5};
示例代码
下面是一个简单的示例,展示了如何使用 System V 信号量来同步两个进程的操作:
进程 A (processA.c)
1#include <sys/types.h>
2#include <sys/ipc.h>
3#include <sys/sem.h>
4#include <stdio.h>
5#include <stdlib.h>
6#include <unistd.h>
7
8#define SEMAPHORE_KEY 12345
9#define SEMAPHORE_ID 0 // 第一个信号量
10
11int main() {
12 int semid;
13 struct sembuf op;
14
15 // 创建或打开信号量集
16 semid = semget(SEMAPHORE_KEY, 1, 0666 | IPC_CREAT);
17 if (semid == -1) {
18 perror("semget");
19 exit(EXIT_FAILURE);
20 }
21
22 // 初始化信号量
23 if (semctl(semid, SEMAPHORE_ID, SETVAL, 1) == -1) {
24 perror("semctl");
25 exit(EXIT_FAILURE);
26 }
27
28 // 减少信号量值
29 op.sem_num = SEMAPHORE_ID;
30 op.sem_op = -1;
31 op.sem_flg = 0;
32 if (semop(semid, &op, 1) == -1) {
33 perror("semop");
34 exit(EXIT_FAILURE);
35 }
36
37 printf("Process A: Decremented semaphore value.\n");
38
39 sleep(1); // 休眠一秒
40
41 // 增加信号量值
42 op.sem_op = 1;
43 if (semop(semid, &op, 1) == -1) {
44 perror("semop");
45 exit(EXIT_FAILURE);
46 }
47
48 printf("Process A: Incremented semaphore value.\n");
49
50 return 0;
51}
进程 B (processB.c)
1#include <sys/types.h>
2#include <sys/ipc.h>
3#include <sys/sem.h>
4#include <stdio.h>
5#include <stdlib.h>
6#include <unistd.h>
7
8#define SEMAPHORE_KEY 12345
9#define SEMAPHORE_ID 0 // 第一个信号量
10
11int main() {
12 int semid;
13 struct sembuf op;
14
15 // 打开信号量集
16 semid = semget(SEMAPHORE_KEY, 1, 0666);
17 if (semid == -1) {
18 perror("semget");
19 exit(EXIT_FAILURE);
20 }
21
22 // 等待信号量值变为正
23 op.sem_num = SEMAPHORE_ID;
24 op.sem_op = 0;
25 op.sem_flg = SEM_UNDO;
26 if (semop(semid, &op, 1) == -1) {
27 perror("semop");
28 exit(EXIT_FAILURE);
29 }
30
31 printf("Process B: Semaphore value is now positive.\n");
32
33 return 0;
34}
编译和运行
为了编译上述代码,你可以使用以下命令:
1gcc -o processA processA.c
2gcc -o processB processB.c
然后运行这两个进程:
1./processA &
2./processB
注意事项
- 在使用 System V 信号量之前,确保检查所有 API 调用的返回值,以确保操作成功。
- 当使用完信号量集后,记得删除信号量集以释放资源。
- 如果信号量集不再需要,应使用
semctl()
的IPC_RMID
命令删除它,以避免占用不必要的系统资源。 - 在实际应用中,可能需要处理更复杂的错误情况,比如处理信号量值已满或为负的情况。
System V 信号量提供了一种简单而强大的机制来进行进程间的同步,非常适合那些需要跨进程同步的应用场景。理解和熟练掌握这些 API 对于开发可靠的多进程应用程序非常重要。