Loading... # Linux System V 信号量详解 System V 信号量(semaphore)是用于进程间同步的机制,在多进程编程中用于控制对共享资源的访问。System V 信号量允许多个进程通过信号量集进行同步操作。本文将详细介绍 System V 信号量的概念及其相关函数 `semget()`、`semctl()` 和 `semop()` 的使用。  ## 一、System V 信号量概述 ### 1.1 信号量的基本概念 信号量是一个计数器,用于控制多个进程对共享资源的访问。信号量可以用来解决以下问题: - 互斥:确保一次只有一个进程访问共享资源。 - 同步:协调进程之间的执行顺序。 ### 1.2 信号量的类型 - **二元信号量**:取值只能是0和1,用于互斥锁(mutex)。 - **计数信号量**:可以取0和正整数,用于控制多个进程的访问。 ### 1.3 System V 信号量特点 - 信号量集:一个信号量集包含一个或多个信号量。 - 标识符:信号量集通过一个标识符进行标识。 - 内核持久性:信号量集在内核中持久存在,直到显式删除或系统重启。 ## 二、`semget()` 函数 ### 2.1 功能 `semget()` 用于创建一个新的信号量集或获取一个已有的信号量集。 ### 2.2 语法 ```c #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> int semget(key_t key, int nsems, int semflg); ``` - `key`:用于标识信号量集的键值。 - `nsems`:信号量集中的信号量数量。 - `semflg`:权限标志和控制标志。 ### 2.3 示例 创建一个包含一个信号量的信号量集: ```c key_t key = ftok("semfile", 65); int semid = semget(key, 1, 0666 | IPC_CREAT); if (semid == -1) { perror("semget"); } ``` ## 三、`semctl()` 函数 ### 3.1 功能 `semctl()` 用于控制信号量集,支持初始化、获取和设置信号量的值,以及删除信号量集。 ### 3.2 语法 ```c #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> int semctl(int semid, int semnum, int cmd, ...); ``` - `semid`:信号量集标识符。 - `semnum`:信号量编号。 - `cmd`:控制命令,如 `IPC_RMID`、`SETVAL`、`GETVAL` 等。 - 可变参数:根据 `cmd` 的不同,可以是 `int` 或 `union semun`。 ### 3.3 示例 初始化信号量的值: ```c union semun { int val; struct semid_ds *buf; unsigned short *array; } arg; arg.val = 1; // 初始化为 1 if (semctl(semid, 0, SETVAL, arg) == -1) { perror("semctl"); } ``` 删除信号量集: ```c if (semctl(semid, 0, IPC_RMID) == -1) { perror("semctl"); } ``` ## 四、`semop()` 函数 ### 4.1 功能 `semop()` 用于对信号量进行P(等待)和V(释放)操作,以实现对资源的控制和同步。 ### 4.2 语法 ```c #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> int semop(int semid, struct sembuf *sops, size_t nsops); ``` - `semid`:信号量集标识符。 - `sops`:指向 `sembuf` 结构数组的指针。 - `nsops`:数组中的元素个数。 ### 4.3 `sembuf` 结构 ```c struct sembuf { unsigned short sem_num; // 信号量编号 short sem_op; // 操作:P(-1) 或 V(+1) short sem_flg; // 操作标志 }; ``` ### 4.4 示例 执行P操作(等待)和V操作(释放): ```c struct sembuf sb = {0, -1, 0}; // P操作 if (semop(semid, &sb, 1) == -1) { perror("semop"); } // 临界区代码 sb.sem_op = 1; // V操作 if (semop(semid, &sb, 1) == -1) { perror("semop"); } ``` ## 五、完整示例 以下是一个完整的示例,演示如何创建信号量、初始化信号量值,以及在两个进程中使用P和V操作进行同步。 ### 5.1 创建和初始化信号量 创建一个信号量,并初始化其值为1: ```c #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> #include <unistd.h> union semun { int val; struct semid_ds *buf; unsigned short *array; }; int main() { key_t key = ftok("semfile", 65); int semid = semget(key, 1, 0666 | IPC_CREAT); if (semid == -1) { perror("semget"); exit(EXIT_FAILURE); } union semun arg; arg.val = 1; if (semctl(semid, 0, SETVAL, arg) == -1) { perror("semctl"); exit(EXIT_FAILURE); } return 0; } ``` ### 5.2 使用信号量进行同步 在两个进程中使用信号量进行同步,确保两个进程不会同时进入临界区。 #### 进程1: ```c #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> #include <unistd.h> int main() { key_t key = ftok("semfile", 65); int semid = semget(key, 1, 0666); if (semid == -1) { perror("semget"); exit(EXIT_FAILURE); } struct sembuf sb = {0, -1, 0}; // P操作 if (semop(semid, &sb, 1) == -1) { perror("semop P"); exit(EXIT_FAILURE); } printf("Process 1 in critical section\n"); sleep(2); // 模拟临界区操作 printf("Process 1 leaving critical section\n"); sb.sem_op = 1; // V操作 if (semop(semid, &sb, 1) == -1) { perror("semop V"); exit(EXIT_FAILURE); } return 0; } ``` #### 进程2: ```c #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> #include <unistd.h> int main() { key_t key = ftok("semfile", 65); int semid = semget(key, 1, 0666); if (semid == -1) { perror("semget"); exit(EXIT_FAILURE); } struct sembuf sb = {0, -1, 0}; // P操作 if (semop(semid, &sb, 1) == -1) { perror("semop P"); exit(EXIT_FAILURE); } printf("Process 2 in critical section\n"); sleep(2); // 模拟临界区操作 printf("Process 2 leaving critical section\n"); sb.sem_op = 1; // V操作 if (semop(semid, &sb, 1) == -1) { perror("semop V"); exit(EXIT_FAILURE); } return 0; } ``` ### 5.3 编译和运行 1. 编译程序: ```sh gcc -o sem_create sem_create.c gcc -o process1 process1.c gcc -o process2 process2.c ``` 2. 运行创建和初始化信号量的程序: ```sh ./sem_create ``` 3. 分别运行进程1和进程2: ```sh ./process1 ./process2 ``` 可以看到,两个进程会依次进入和离开临界区,而不会同时进入。 ## 六、总结 本文详细介绍了 System V信号量的概念及其在Linux中的使用,包括 `semget()`、`semctl()`和 `semop()`函数的具体使用方法。通过实际代码示例,演示了如何创建、初始化和使用信号量进行进程间同步。掌握这些知识,可以有效解决多进程编程中的同步问题,提高程序的可靠性和稳定性。 最后修改:2024 年 07 月 13 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 如果觉得我的文章对你有用,请随意赞赏