欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 科技 > 名人名企 > Linux 基础入门操作 第九章 进程之间通讯信号量 2

Linux 基础入门操作 第九章 进程之间通讯信号量 2

2025/4/11 8:20:30 来源:https://blog.csdn.net/huanghongqi11/article/details/146977777  浏览:    关键词:Linux 基础入门操作 第九章 进程之间通讯信号量 2

1 信号量

信号量与其他进程间通信方式不大相同,它主要提供对进程间共享资源访问控制机制。相当于内存中的标志,进程可以根据它判定是否能够访问某些共享资源,同时,进程也可以修改该标志。除了用于访问控制外,还可用于进程同步。信号量有以下两种类型:

二值信号量:最简单的信号量形式,信号灯的值只能取 0 或 1,类似于互斥锁。
计算信号量:信号量的值可以取任意非负值(当然受内核本身的约束)

信号量只能进行两种操作等待和发送信号,即 P(sv)和 V(sv),他们的行为是这样的:
P(sv):如果 sv 的值大于零,就给它减 1;如果它的值为零,就挂起该进程的执行
V(sv):如果有其他进程因等待 sv 而被挂起,就让它恢复运行,如果没有进程因等待 sv 而挂起,就给它加 1.

POSIX 信号量 目前编程中比较常用的模块。

2 POSIX 信号量

2.1 特点

它有两个优势:

  1. 现代标准:遵循 POSIX 标准(<semaphore.h>)。
  2. 轻量级:设计更简洁,支持线程和进程间同步。

POSIX 信号量有两种类型:

  1. 命名信号量(Named Semaphore):通过文件名标识,可用于无亲缘关系的进程。
  2. 匿名信号量(Unnamed Semaphore):存在于共享内存中,通常用于线程或多进程(需共享内存)。

2.2 涉及到函数

POSIX 信号量 的主要函数如下所示, 将在下面的章节中进行具体介绍。
在这里插入图片描述

3 命名信号量

命名信号量可以在不同进程之间共享,可以没有亲缘关系的进程李米娜进行通讯。

3.1 相关函数

在这里插入图片描述

3.1.1 sem_open() 函数

sem_open() 是 POSIX 信号量 API 中的一个函数,用于创建或打开一个命名信号量。

#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value);

参数如下:

name:信号量的名字。必须以 / 开头,例如 /my_sem。
实际文件存储在 /dev/shm/sem.mysem。
名称长度限制(通常 NAME_MAX-4,即 251 字符)
oflag:打开标志,可以是以下值的组合:
O_CREAT:如果信号量不存在,则创建它。
O_EXCL:与 O_CREAT 一起使用,如果信号量已存在,则返回错误。
mode:信号量的权限(类似于文件权限),例如 0666。
value:信号量的初始值(仅在创建信号量时有效)。 一般为1

返回值
成功时返回指向信号量的指针。
失败时返回 SEM_FAILED,并设置 errno。

3.1.2 sem_close() 和 sem_unlink() - 清理资源

sem_unlink 是 POSIX 信号量(Named Semaphore) 的清理函数,用于 从文件系统中删除命名信号量的标识。它不会立即销毁信号量,而是标记删除,当所有引用该信号量的进程关闭后,内核会自动释放资源。

先要关闭信号量,删除信号量。

#include <semaphore.h>
int sem_close(sem_t *sem);          // 关闭信号量(进程内)
int sem_unlink(const char *name);   // 删除信号量(文件系统)

删除信号量的函数介绍如下:

#include <semaphore.h>
int sem_unlink(const char *name);

name 命名信号量的文件系统路径(如 “/mysem”),必须以 / 开头。
返回值
成功:返回 0。
失败:返回 -1 并设置 errno(如 ENOENT 表示信号量不存在)

3.1.3 sem_wait() 和 sem_post() - P/V 操作

int sem_wait(sem_t *sem);   // P操作(阻塞)
int sem_post(sem_t *sem);   // V操作(释放)

3.2 命名信号量的使用示例

父子进程同时打印数据,通过互斥量保护打印数据。

#include <stdio.h>
#include <fcntl.h>
#include <semaphore.h>
#include <unistd.h>int main() {const char *sem_name = "/my_sem";  // 信号量名称sem_t *sem = sem_open(sem_name, O_CREAT, 0644, 1);  // 创建信号量,初始值为 1if (sem == SEM_FAILED) {perror("sem_open");return 1;}pid_t pid = fork();if (pid == 0) {// 子进程sem_wait(sem);  // 等待信号量(P 操作)printf("Child process acquired the semaphore\n");sleep(2);  // 模拟临界区操作printf("Child process releasing the semaphore\n");sem_post(sem);  // 释放信号量(V 操作)} else if (pid > 0) {// 父进程sem_wait(sem);  // 等待信号量(P 操作)printf("Parent process acquired the semaphore\n");sleep(2);  // 模拟临界区操作printf("Parent process releasing the semaphore\n");sem_post(sem);  // 释放信号量(V 操作)wait(NULL);  // 等待子进程结束sem_close(sem);  // 关闭信号量sem_unlink(sem_name);  // 删除信号量} else {perror("fork");return 1;}return 0;
}

4 未命名信号量

未命名信号量(Unnamed Semaphore)是 POSIX 信号量 的一种,仅通过内存地址访问(不依赖文件系统路径),适用于 线程间同步 或 共享内存的进程间同步。

4.1 核心函数

常见函数如下所示:
在这里插入图片描述

4.1.1 sem_init 函数

创建信号量的, 一般初始值为1。

int sem_init(sem_t *sem, int pshared, unsigned int value);
pshared:0:信号量在线程间共享。
1:信号量在进程间共享(需放在共享内存中)。
value:信号量初始值(如 1 表示二进制信号量)。

4.1.2 sem_destroy()

匿名信号量不会自动释放资源,需显式销毁。

sem_destroy(sem);  // 销毁信号量

4.2 参考案例

4.2.1 线程同步

这里创建两个线程。 对线程的资源进行保护。关于线程的介绍,可以参考这篇文章。

#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>sem_t sem;  // 未命名信号量void* thread_func(void* arg) {sem_wait(&sem);  // 等待信号量(P 操作)printf("Thread acquired the semaphore\n");sleep(2);  // 模拟临界区操作printf("Thread releasing the semaphore\n");sem_post(&sem);  // 释放信号量(V 操作)return NULL;
}int main() {sem_init(&sem, 0, 1);  // 初始化未命名信号量,初始值为 1pthread_t tid;pthread_create(&tid, NULL, thread_func, NULL);sem_wait(&sem);  // 等待信号量(P 操作)printf("Main thread acquired the semaphore\n");sleep(2);  // 模拟临界区操作printf("Main thread releasing the semaphore\n");sem_post(&sem);  // 释放信号量(V 操作)pthread_join(tid, NULL);  // 等待线程结束sem_destroy(&sem);  // 销毁信号量return 0;
}

4.2.2 进程同步-共享内存

#include <semaphore.h>
#include <sys/shm.h>
#include <stdio.h>int main() {// 1. 创建共享内存int shmid = shmget(IPC_PRIVATE, sizeof(sem_t), 0666);sem_t *sem = (sem_t*)shmat(shmid, NULL, 0);// 2. 初始化信号量(1表示进程间共享)sem_init(sem, 1, 1); // 初始值=1if (fork() == 0) {// 子进程sem_wait(sem);printf("Child process in critical section\n");sem_post(sem);shmdt(sem);} else {// 父进程sem_wait(sem);printf("Parent process in critical section\n");sem_post(sem);wait(NULL);sem_destroy(sem);  // 销毁信号量shmctl(shmid, IPC_RMID, NULL); // 删除共享内存}return 0;
}

5 总结

综合前面两个文章,两个信号量特点如下:
在这里插入图片描述

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词