Linux进程间共享信号量的实现 (linux不同进程信号灯共享)
在Linux系统中,进程间的通信是非常常见的场景。其中,信号量是一种常用的进程间通信机制,它可以用来实现进程的同步和互斥。在多进程或多线程的环境中,控制进程之间的访问同步和互斥是非常重要的,而信号量正是解决这一问题的有效机制。
本文将介绍Linux中进程间共享信号量的实现方法,包括信号量的定义、初始化、操作等,希望能够帮助读者深入理解信号量的知识,更好地应用它们进行进程间通信。
一、信号量的定义
信号量是一种用于同步进程和线程的机制,它主要用于控制进程的互斥和同步。在Linux系统中,信号量是由一个整形变量和一组信号量操作组成的。信号量变量的值表示当前资源的可用数目。当信号量的值为正数时,表示还有可用资源;当信号量的值为0时,表示所有资源都被占用;当信号量的值为负数时,表示有进程正在等待释放资源。
在Linux系统中,信号量的定义如下:
“`
struct sembuf{
unsigned short sem_num; // 信号量编号
short sem_op; // 信号量操作
short sem_; // 操作标志
};
union semun{
int val; // 信号量初始值
struct semid_ds *buf; // IPC_STAT、IPC_SET 操作用缓存区
unsigned short *array; // SETALL、GETALL 操作用数组
};
int semget(key_t key, int nsems, int sem); // 创建或打开一个信号量
int semctl(int semid, int semnum, int cmd, union semun arg); // 对一个信号量或信号量执行控制操作
int semop(int semid, struct sembuf *sops, unsign int nsops); // 执行信号量操作
“`
二、信号量的初始化
在使用信号量进行进程间通信时,需要先创建或打开一个信号量,并进行初始化。在Linux中,信号量的初始化可以使用 semget() 函数和 semctl() 函数进行。
1.使用 semget() 函数创建信号量
(1)semget() 函数原型
“`
#include
#include
#include
int semget(key_t key, int nsems, int sem);
“`
(2)参数说明
key:用于标识信号集的key值。
nsems:创建的信号集的数量。
sem:信号集的访问权限。
(3)函数返回值
成功:返回信号量集的标识符。
失败:返回 -1 并设置 errno 来指示错误的类型。
(4)示例代码
“`
#include
#include
#include
#include
int mn()
{
int semid;
key_t key = 1234;
semid = semget(key, 1, 0666 | IPC_CREAT);
if (semid == -1) {
perror(“semget”);
return -1;
}
printf(“succeed to create semaphore: %d\n”, semid);
return 0;
}
“`
上面的示例代码使用 key 为 1234 创建了一个信号量,该包含了一个信号量。sem 的值被设置为 0666 | IPC_CREAT 来表示新创建的信号量,如果已经存在该信号量,则直接打开该信号量。
2. 使用 semctl() 函数初始化信号量
使用 semctl() 函数初始化信号量时,可以设置信号量的值、操作等。semctl() 函数使用如下:
(1)semctl() 函数原型
“`
#include
#include
#include
int semctl(int semid, int semnum, int cmd, union semun arg);
“`
(2)参数说明
semid:信号量集的标识符。
semnum:对信号量中的第几个信号量进行操作。
cmd:操作类型,包括如下:
– SETVAL:设置信号量集中的一个信号量的值。
– IPC_RMID:从内核中删除信号量。
arg:参数类型 union semun,可以是如下情况:
– val:val 成员用于 SETVAL 操作设置一个信号量或数组的值。
– buf:buf 成员用于 IPC_STAT 和 IPC_SET 操作,获取或修改信号量的状态信息。
– array:array 成员用于 SETALL 和 GETALL 操作,设置或获取整个信号量数组的值。
(3)函数返回值
成功:返回信号量集的标识符。
失败:返回 -1 并设置 errno 来指示错误的类型。
(4)示例代码
“`
#include
#include
#include
#include
int mn()
{
int semid;
key_t key = 1234;
union semun arg;
struct sembuf sembuf;
int val = 1;
semid = semget(key, 1, 0666 | IPC_CREAT);
if (semid == -1) {
perror(“semget”);
return -1;
}
arg.val = val;
if (semctl(semid, 0, SETVAL, arg) == -1) {
perror(“semctl”);
return -1;
}
printf(“succeed to initialize semaphore: %d\n”, semid);
return 0;
}
“`
上面的示例代码创建一个 key 值为 1234 的信号量,包含了一个信号量。使用 SETVAL 操作设置该信号量的值为 1。如果初始化成功,则返回该信号量的标识符 semid。
三、信号量的操作
在信号量的中,可以对单个信号量进行操作,也可以对信号量中的所有信号量进行操作。信号量的操作主要包括加操作和减操作。在 Linux 平台下,可以使用 semop() 函数完成对信号量的操作。
在使用 semop() 函数对信号量进行操作时,需要传入一个结构体 sembuf 作为信号量操作的参数,或者传入多个 sembuf,以便一次执行多个信号量操作。
(1)sembuf 结构体的定义
“`
struct sembuf {
unsigned short sem_num; // 信号量中的信号量编号
short sem_op; // 信号量操作
short sem_; // 操作标志
};
“`
sem_num:信号量中的信号量编号。
sem_op:信号量的操作,其中,信号量的操作可以为如下值之一:
– 正整数:表示向信号量中增加信号量值;
– 负整数:表示从信号量中减去信号量值;
– 0:表示读取信号量中的值。
sem_:操作标志,可用于 IPC_NOWT 等标志的设置。
(2)semop() 函数原型
“`
#include
#include
#include
int semop(int semid, struct sembuf *sops, unsigned nsops);
“`
(3)参数说明
semid:信号量的标识符。
sops:指向 sembuf 结构数组的指针,其中每一个 sembuf 结构体描述了一个信号量的操作。
nsops:sops 指向的 sembuf 结构体的数量。
(4)函数返回值
成功:返回 0。
失败:返回 -1 并设置 errno 来指示错误的类型,如 EAGN 表示操作被阻塞,EINVAL 不能操作等等。
(5)信号量操作的示例代码
下面的示例代码展示了如何在两个进程中对共享的信号量进行操作。两个进程的实现方式分别是获取和释放共享资源。
之一个进程:
“`
#include
#include
#include
#include
#include
#include
const char *data = “hello, world”;
int p(int semid, int semnum)
{
struct sembuf sem_buf;
sem_buf.sem_num = semnum;
sem_buf.sem_op = -1; // 清空信号量
sem_buf.sem_ = SEM_UNDO; // 系统内核在进程退出时恢复信号量
return semop(semid, &sem_buf, 1);
}
int v(int semid, int semnum)
{
struct sembuf sem_buf;;
sem_buf.sem_num = semnum;
sem_buf.sem_op = 1; // 加 1
sem_buf.sem_ = SEM_UNDO; // 系统内核在进程退出时恢复信号量
return semop(semid, &sem_buf, 1);
}
int mn()
{
int semid;
key_t key = 1234;
pid_t pid;
union semun arg;
struct sembuf sembuf;
int val = 1;
semid = semget(key, 1, 0666 | IPC_CREAT);
if (semid == -1) {
perror(“semget”);
return -1;
}
arg.val = val;
if (semctl(semid, 0, SETVAL, arg) == -1) {
perror(“semctl”);
return -1;
}
pid = fork();
if (pid == -1) {
perror(“fork”);
return -1;
} else if (pid == 0) {
// 子进程
p(semid, 0);
printf(“child process get the semaphore, write data\n”);
write(STDOUT_FILENO, data, strlen(data));
sleep(1);
v(semid, 0);
printf(“child process release the semaphore\n”);
} else {
// 父进程
struct sembuf sem_buf;
sem_buf.sem_num = 0;
sem_buf.sem_op = -1; // 清空信号量
sem_buf.sem_ = SEM_UNDO; // 系统内核在进程退出时恢复信号量
semop(semid, &sem_buf, 1);
printf(“parent process get the semaphore, read data\n”);
read(STDIN_FILENO, data, strlen(data));
v(semid, 0);
printf(“parent process release the semaphore\n”);
}
semctl(semid, 0, IPC_RMID, arg);
return 0;
}
“`
上面的代码中,fork() 函数创建一个子进程,在子进程中利用信号量实现获取和释放共享资源的操作。在父进程中也是利用信号量控制读取和释放共享资源的操作。
四、