📅  最后修改于: 2023-12-03 14:57:57.346000             🧑  作者: Mango
在多进程编程中,进程间通信(IPC,Inter-Process Communication)是一种重要的实现方式。信号量是一种常用的进程间通信机制,它可以用来同步不同进程之间的行为和互斥访问共享资源。
在操作系统中,信号量是一个计数器,它的值用来同步进程和线程对共享资源的访问。当一个进程或线程对共享资源进行访问时,它会尝试对信号量进行操作,从而获得对共享资源的控制权。如果信号量的值大于0,那么进程或线程可以访问共享资源;如果信号量的值等于0,那么进程或线程需要等待其他进程或线程释放信号量,才能访问共享资源。
信号量可以有两种类型:二进制信号量(Binary Semaphore)和计数信号量(Counting Semaphore)。二进制信号量只具有两个可能的值,通常为0和1,用于实现进程之间的互斥访问共享资源。计数信号量可以具有多个可能的值,用于实现进程之间的同步和协调。
在C语言中,信号量的使用可以通过操作系统的IPC机制调用实现。在Linux中,信号量是通过系统调用semctl、semop、semget来实现的。
semget
函数用于创建一个新的信号量,或获取一个已经存在的信号量。函数原型如下:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
参数key
用于指定信号量的键值,通常使用ftok
函数来生成。参数nsems
用于指定需要创建/获取的信号量个数;参数semflg
则用于指定信号量的创建/获取方式。
如果成功创建/获取信号量,则semget
函数返回一个非负整数值,即信号量集合的标识符;如果失败,函数返回-1。
semctl
函数用于进行信号量集合上的控制操作。函数原型如下:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, ...);
参数semid
用于指定信号量集合的标识符;参数semnum
用于指定需要操作的信号量在信号量集合中的下标(从0开始);参数cmd
用于指定需要执行的控制操作,可能的参数值有:
SETVAL
:设置信号量的值;GETVAL
:获取信号量的值;IPC_RMID
:删除信号量集合。如果执行成功,semctl
函数返回相应的值;如果执行失败,函数返回-1。
semop
函数用于进行对信号量的操作。函数原型如下:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf *sops, unsigned nsops);
参数semid
用于指定信号量集合的标识符;参数sops
是一个指向sembuf
结构体的指针,而sembuf
结构体是信号量操作的定义。参数nsops
用于指定需要执行操作的信号量数量。
sembuf
结构体定义如下:
struct sembuf {
short sem_num; // 信号量在信号量集合中的下标(从0开始)
short sem_op; // 信号量的操作值,可以为正值、零或负值
short sem_flg; // 信号量的操作标志
};
其中,sem_op
的取值可以为正值、零或负值。当sem_op
为正值时,表示对信号量的值进行增加;当sem_op
为零时,表示不对信号量进行操作,仅用于同步和协调;当sem_op
为负值时,表示对信号量的值进行减少。sem_flg
用于指定信号量的操作标志,可以为IPC_NOWAIT或SEM_UNDO。
例如,当需要对一个信号量集合中的第0个信号量进行减1的操作,可以定义一个sembuf
结构体:
struct sembuf sb = {0, -1, SEM_UNDO};
然后调用semop
函数进行操作:
int res = semop(semid, &sb, 1);
如果执行成功,semop
函数返回0;如果执行失败,函数返回-1。
下面是一个简单的利用信号量实现进程同步的例子,其中包含了信号量的创建、设置、操作和删除等操作。该例子中创建了三个进程,分别代表甲、乙、丙三个人,三个人同时从门口进入一个房间,但每次只能有一人通过。程序中使用了二进制信号量来控制进程操作。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#define SEM_KEY 12345 // 信号量键值
int main(int argc, char *argv[]) {
// 创建二进制信号量
int semid = semget(SEM_KEY, 1, IPC_CREAT | 0666);
if (semid == -1) {
perror("Error creating semaphore");
exit(EXIT_FAILURE);
}
// 初始化信号量的值为1
semctl(semid, 0, SETVAL, 1);
// 创建子进程
pid_t pid = fork();
if (pid == -1) {
perror("Error forking process");
exit(EXIT_FAILURE);
}
if (pid == 0) {
// 甲进入房间
struct sembuf sb = {0, -1, SEM_UNDO};
semop(semid, &sb, 1);
printf("Process A enters the room.\n");
sleep(2);
printf("Process A leaves the room.\n");
sb.sem_op = 1;
semop(semid, &sb, 1);
exit(EXIT_SUCCESS);
} else {
pid = fork();
if (pid == -1) {
perror("Error forking process");
exit(EXIT_FAILURE);
}
if (pid == 0) {
// 乙进入房间
struct sembuf sb = {0, -1, SEM_UNDO};
semop(semid, &sb, 1);
printf("Process B enters the room.\n");
sleep(2);
printf("Process B leaves the room.\n");
sb.sem_op = 1;
semop(semid, &sb, 1);
exit(EXIT_SUCCESS);
} else {
// 丙进入房间
struct sembuf sb = {0, -1, SEM_UNDO};
semop(semid, &sb, 1);
printf("Process C enters the room.\n");
sleep(2);
printf("Process C leaves the room.\n");
sb.sem_op = 1;
semop(semid, &sb, 1);
// 删除信号量
semctl(semid, 0, IPC_RMID);
exit(EXIT_SUCCESS);
}
}
return 0;
}
信号量是一种常用的进程间通信机制,它可以用于同步不同进程之间的行为和互斥访问共享资源。本文介绍了信号量的定义、使用方法和一些注意事项,希望对读者在编写多进程程序时有所帮助。