linux应用 进程间通信之信号量(POSIX)

1、前言

1.1 定义

POSIX信号量是一种用于同步进程之间对共享资源访问的机制。它允许进程在访问共享资源之前进行互斥和同步操作,以确保数据的一致性和正确性。POSIX信号量通常由一个整数值表示,可以进行原子增减操作,以及等待和通知操作。

1.2 应用场景

  • 进程同步:当多个进程需要访问共享资源时,可以使用信号量来确保只有一个进程能够访问资源,从而避免数据竞争和冲突。
  • 控制资源访问:信号量可以用于限制对资源的访问数量,例如控制同时访问某个共享资源的进程数量。
  • 进程间通信:信号量也可以用于实现进程间的通信和同步,以确保进程之间的协作和顺序执行。

1.3 优缺点

1.3.1 优点
  • 灵活性:信号量可以用于实现不同类型的同步和通信需求,包括互斥访问、资源控制和进程同步等。
  • 多进程支持:信号量适用于多个进程之间的通信和同步,可以在不同进程之间进行共享和使用。
  • 高效性:信号量的实现通常是基于硬件原子操作的,因此在性能上可以比较高效。
1.3.2 缺点
  • 复杂性:使用信号量可能需要处理死锁、竞争条件等复杂问题,需要谨慎设计和管理。
  • 缺乏语义:信号量本身只是一个整数值,缺乏高层次的语义,需要程序员自行设计合适的同步和通信规则。
  • 跨平台兼容性:不同操作系统对于信号量的实现和语义可能存在差异,因此在跨平台开发时需要考虑兼容性问题。

1.4 重要概念

有名信号量(Named Semaphore):

  • 有名信号量是一种由操作系统内核维护的具有全局唯一名字的信号量。
  • 有名信号量可以在不同进程之间进行共享,因为它们可以通过名称在系统中进行识别和访问。
  • 有名信号量通常用于进程间通信和同步,可以在不同进程之间进行共享和使用。

无名信号量(Unnamed Semaphore):

  • 无名信号量是一种不具有全局唯一名字的信号量,通常只能在相关的进程或线程之间进行共享。
  • 无名信号量通常用于线程间通信和同步,因为它们只能在共享同一内存空间的线程之间进行使用。
  • 无名信号量通常使用 sem_init 函数进行初始化,使用 sem_destroy 函数进行销毁。

2、编程常用接口

2.1 sem_open 函数

创建或打开有名信号量

sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value);

参数:

  • name:信号量的名称
  • oflag:标志位,用于指定信号量的行为和权限
  • mode:权限模式
  • value:信号量的初始值

返回值:

  • 成功:返回指向信号量的指针
  • 失败:返回 SEM_FAILED,并设置 errno

其中入参的mode选择如下:

  • S_IRUSR:用户读权限
  • S_IWUSR:用户写权限
  • S_IRGRP:组读权限
  • S_IWGRP:组写权限
  • S_IROTH:其他用户读权限
  • S_IWOTH:其他用户写权限

2.2 sem_close 函数

关闭有名信号量

int sem_close(sem_t *sem);

参数:

  • sem:指向要关闭的信号量的指针

返回值:

  • 成功:返回 0
  • 失败:返回 -1,并设置 errno

2.3 sem_init 函数

初始化无名信号量

int sem_init(sem_t *sem, int pshared, unsigned int value);

参数:

  • sem:指向要初始化的信号量的指针
  • pshared:指定信号量的进程共享性质,非零值表示信号量在进程间共享,零值表示信号量在线程间共享
  • value:信号量的初始值

返回值:

  • 成功:返回 0
  • 失败:返回 -1,并设置 errno

2.4 sem_destroy 函数

销毁无名信号量

int sem_destroy(sem_t *sem);

参数:

  • sem:指向要销毁的信号量的指针

返回值:

  • 成功:返回 0
  • 失败:返回 -1,并设置 errno

2.5 sem_unlink 函数

从系统中删除有名信号量

int sem_unlink(const char *name);

参数:

  • name:信号量的名称

返回值:

  • 成功:返回 0
  • 失败:返回 -1,并设置 errno

2.6 sem_wait 函数

等待信号量减小,如果信号量的值大于0,则将其减小1并立即返回,否则会阻塞当前线程直到信号量变为大于0为止

int sem_wait(sem_t *sem);

参数:

  • sem:指向要等待的信号量的指针

返回值:

  • 成功:返回 0
  • 失败:返回 -1,并设置 errno

2.7 sem_trywait 函数

尝试等待信号量减小的非阻塞版本。如果信号量的值大于0,则将其减小1并立即返回,否则会立即返回,并且不会阻塞当前线程

int sem_trywait(sem_t *sem);

参数:

  • sem:指向要尝试等待的信号量的指针

返回值:

  • 成功:返回 0
  • 失败:若信号量当前不能立即获得,则返回 -1,并设置 errno 为 EAGAIN

2.8 sem_post 函数

增加信号量,信号量值+1

int sem_post(sem_t *sem);

参数:

  • sem:指向要增加的信号量的指针

返回值:

  • 成功:返回 0
  • 失败:返回 -1,并设置 errno

2.9 sem_timedwait 函数

函数允许设置一个超时时间,如果在指定的时间内未能获得信号量,函数将返回一个特定的错误码。

int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);

参数:

  • sem:指向要等待的信号量的指针
  • abs_timeout:绝对时间,指定等待的超时时间

返回值:

  • 成功:返回 0
  • 失败:若超时或出错,则返回 -1,并设置 errno
struct timespec {time_t tv_sec;  // 秒long tv_nsec;   // 纳秒
};

2.10 sem_getvalue函数

获取当前信号量的值

int sem_getvalue(sem_t *sem, int *sval);

参数:

  • sem:指向要获取值的信号量的指针。
  • sval:一个整数指针,用于存储信号量的当前值。

返回值:

  • 成功:返回 0
  • 失败:若超时或出错,则返回 -1,并设置 errno

2.11 接口适用总结

函数适用
sem_open有名信号量
sem_close有名信号量
sem_init无名信号量
sem_destroy无名信号量
sem_unlink有名信号量
sem_wait两者皆可
sem_trywait两者皆可
sem_post两者皆可
sem_timedwait两者皆可
sem_getvalue两者皆可

3、编程测试

3.1 有名信号量编程测试

测试代码如下:

#include <semaphore.h>
#include <stdio.h>
#include <fcntl.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>// 打印时分秒的宏        
#define PRINT_MIN_SEC do { \time_t t = time(NULL); \struct tm *tm_ptr = localtime(&t); \printf("%02d:%02d:%02d:", tm_ptr->tm_hour, tm_ptr->tm_min, tm_ptr->tm_sec);\} while (0);printf// 打印当前信号量的值
void printfValue(sem_t *sem)
{// 打印当前值int value;sem_getvalue(sem, &value);PRINT_MIN_SEC("Current value of semaphore: %d\n", value);
}int main(int argc, char *argv[]) 
{sem_t *sem;// 命令行参数 // 第一个参数      I表示初始化 W表示等待 P表示增加信号量的值if (argc != 2) {printf("Usage: %s I|W|P", argv[0]);return 0;}if (!strcmp(argv[1], "I")){PRINT_MIN_SEC("Init sem ...\n");if((sem = sem_open("sem_p", O_CREAT, 0644, 0)) == SEM_FAILED){perror("sem_open err");return 0;}PRINT_MIN_SEC("Init sem OK\n");} else if (!strcmp(argv[1], "W")) {if((sem = sem_open("sem_p", 0)) == SEM_FAILED){perror("sem_open err");return 0;}PRINT_MIN_SEC("Wait sem ...\n");sem_wait(sem);PRINT_MIN_SEC("Wait sem OK\n");} else if (!strcmp(argv[1], "P")) {if((sem = sem_open("sem_p", 0)) == SEM_FAILED){perror("sem_open err");return 0;}PRINT_MIN_SEC("Post sem ...\n");sem_post(sem);PRINT_MIN_SEC("Post sem OK\n");} else{printf("Usage: %s I|W|P", argv[0]);return 0;}printfValue(sem);sem_close(sem);    return 0;
}

通过命令行不同参数实现不同功能I表示初始化 W表示等待 P表示增加信号量的值,编译的时候需要加-pthread,开启一个控制台,执行初始化和等待:

另起一个控制台,发起新的进程执行post操作,阻塞的进程可以成功执行完毕,完成进程间通信:

在/dev/shm目录下可以查看到信号量对应的文件:

3.2 无名信号量编程测试

测试代码如下:

#include <semaphore.h>
#include <stdio.h>
#include <fcntl.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>// 打印时分秒的宏        
#define PRINT_MIN_SEC do { \time_t t = time(NULL); \struct tm *tm_ptr = localtime(&t); \printf("%02d:%02d:%02d:", tm_ptr->tm_hour, tm_ptr->tm_min, tm_ptr->tm_sec);\} while (0);printfsem_t mutex;void* thread_function_1(void* arg) 
{PRINT_MIN_SEC("In thread_function_1\n");while(1){sleep(3);sem_post(&mutex);}
}void* thread_function_2(void* arg) 
{PRINT_MIN_SEC("In thread_function_2\n");while(1){PRINT_MIN_SEC("thread_function_2 sem_wait ...\n");sem_wait(&mutex);PRINT_MIN_SEC("thread_function_2 sem_wait OK\n");}
}int main(int argc, char *argv[]) 
{sem_init(&mutex, 0, 0);pthread_t thread;pthread_create(&thread, NULL, thread_function_1, NULL);pthread_create(&thread, NULL, thread_function_2, NULL);pthread_join(thread, NULL);sem_destroy(&mutex);return 0;
}

测试无名信号线程间通信,发起两个线程,线程1每隔3秒执行一次sem_post,线程2一直等待获取,测试结果如下:

4、总结

本文阐述了进程间通信之信号量(POSIX)的定义、应用场景、优缺点等,列举了编程中使用的接口,编写了测试用例测试相关功能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/467168.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《A++ 敏捷开发》- 8 获取高层支持

我&#xff1a;对过程改进来说&#xff0c;最重要的成功要素是什么&#xff1f; 客户&#xff1a;最难的是如何得到高层的支持&#xff0c;这不仅仅是嘴巴说说而已&#xff0c;而是要切实地给人、给时间。高层往往不清楚什么是质量改进的重点&#xff0c;但他们对员工的人均收入…

数据工程工程师学习路线图

数据工程岗位要求 Skill Sets required: - Hands on experience enabling data via Adobe Analytics and/or Google Analytics - Understanding of how customer level data is captured and stitched with behavioural data - Experience working with Testing (QA) and D…

Matplotlib核心:掌握Figure与Axes

详细介绍Figure和Axes&#xff08;基于Matplotlib&#xff09; &#x1f335;文章目录&#x1f335; &#x1f333;引言&#x1f333;&#x1f333; 一、Figure&#xff08;图形&#xff09;&#x1f333;&#x1f341;1. 创建Figure&#x1f341;&#x1f341;2. 添加Axes&am…

多态

多态的基本语法 多态分为两类 静态多态: 函数重载 和 运算符重载属于静态多态&#xff0c;复用函数名动态多态: 派生类和虚函数实现运行时多态 静态多态和动态多态区别&#xff1a; 静态多态的函数地址早绑定 - 编译阶段确定函数地址动态多态的函数地址晚绑定 - 运行阶段确…

C++实现二分查找

目录 例1 例2 例3 例4 例5 例6 例1 704. 二分查找 注意&#xff1a; ①left < right,这里的号是最后一次通过下标mid来判断 ②在偶数的时候mid&#xff0c;左右无所谓&#xff0c;因为left和right都有1&#xff1b; 参考代码 class Solution { public:int search…

Ubuntu Linux使用PL2302串口和minicom进行开发板调试

调试远程的服务器上面的BMC&#xff0c;服务器上面安装了Ubuntu&#xff0c;想着可以在服务器接个串口到BMC&#xff0c;然后SSH到服务器的Ubuntu&#xff0c;用minicom来查看串口信息。 准备&#xff1a; 服务器Ubuntu安装mimicom 本机可以ssh到Ubuntu 串口工具PL2302 或者CH3…

物资捐赠管理系统

文章目录 物资捐赠管理系统一、项目演示二、项目介绍三、系统部分功能截图四、部分代码展示五、底部获取项目&#xff08;9.9&#xffe5;带走&#xff09; 物资捐赠管理系统 一、项目演示 爱心捐赠系统 二、项目介绍 基于springboot的爱心捐赠管理系统 开发语言&#xff1a…

Java实现软件学院思政案例库系统 JAVA+Vue+SpringBoot+MySQL

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 系统管理员2.2 普通教师 三、系统展示四、核心代码4.1 查询思政案例4.2 审核思政案例4.3 查询思政课程4.4 思政案例点赞4.5 新增思政案例评语 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBootMySQL的软件学…

比较6*6范围内8个点425个结构的顺序

( A, B )---6*30*2---( 1, 0 )( 0, 1 ) 让网络的输入有6个节点&#xff0c;训练集AB各由6张二值化的图片组成&#xff0c;让A中有8个点&#xff0c;让B全是0&#xff0c;收敛误差7e-4&#xff0c;收敛199次&#xff0c;统计迭代次数平均值并排序。 假设这个6*6的结构的行和列都…

【C++ 02】类和对象 1:初识类和对象

文章目录 &#x1f308; Ⅰ 面向对象介绍&#x1f308; Ⅱ 类的引入&#x1f308; Ⅲ 类的定义格式1. 声明和定义不分离2. 声明和定义分离 &#x1f308; Ⅳ 类的访问限定符&#x1f308; Ⅴ 类的作用域&#x1f308; Ⅵ 类的实例化&#x1f308; Ⅶ this 指针 &#x1f308; Ⅰ…

零基础学python之高级编程(3)---面向对象多态与封装(含有代码示例)

面向对象多态与封装 文章目录 面向对象多态与封装前言一、多态方法重写&#xff08;Overriding&#xff09;方法重载(Overloading)抽象基类和接口&#xff08;Abstract Base Classes and Interfaces&#xff09; 二、封装私有变量和私有方法属性装饰器(property) 和 getter和se…

MySQL数据库-索引概念及其数据结构、覆盖索引与回表查询关联、超大分页解决思路

索引是帮助mysql高效获取数据的数据结构,主要用来提高检索的效率,降低数据库的IO成本(输入输出成本&#xff08;Input-Output Cost&#xff09;),同时通过索引对数据进行排序也能降低数据排序的成本,降低了CPU的消耗。 Mysql的默认存储引擎InnoDB&#xff0c;InnoDB采用的B树的…