Linux IPC:System V信号量

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 对于开发可靠的多进程应用程序非常重要。