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 语法
#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 示例
创建一个包含一个信号量的信号量集:
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 语法
#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 示例
初始化信号量的值:
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");
}
删除信号量集:
if (semctl(semid, 0, IPC_RMID) == -1) {
perror("semctl");
}
四、semop()
函数
4.1 功能
semop()
用于对信号量进行P(等待)和V(释放)操作,以实现对资源的控制和同步。
4.2 语法
#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
结构
struct sembuf {
unsigned short sem_num; // 信号量编号
short sem_op; // 操作:P(-1) 或 V(+1)
short sem_flg; // 操作标志
};
4.4 示例
执行P操作(等待)和V操作(释放):
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:
#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:
#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:
#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 编译和运行
- 编译程序:
gcc -o sem_create sem_create.c
gcc -o process1 process1.c
gcc -o process2 process2.c
- 运行创建和初始化信号量的程序:
./sem_create
- 分别运行进程1和进程2:
./process1
./process2
可以看到,两个进程会依次进入和离开临界区,而不会同时进入。
六、总结
本文详细介绍了
System V信号量的概念及其在Linux中的使用,包括 semget()
、semctl()
和 semop()
函数的具体使用方法。通过实际代码示例,演示了如何创建、初始化和使用信号量进行进程间同步。掌握这些知识,可以有效解决多进程编程中的同步问题,提高程序的可靠性和稳定性。