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() 函数创建一个子进程,在子进程中利用信号量实现获取和释放共享资源的操作。在父进程中也是利用信号量控制读取和释放共享资源的操作。

四、


数据运维技术 » Linux进程间共享信号量的实现 (linux不同进程信号灯共享)