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);printf

sem_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.mfbz.cn/a/384926.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Verilog刷题笔记29

题目&#xff1a; Create a 100-bit binary ripple-carry adder by instantiating 100 full adders. The adder adds two 100-bit numbers and a carry-in to produce a 100-bit sum and carry out. To encourage you to actually instantiate full adders, also output the ca…

《Linux 简易速速上手小册》第2章: 命令行的艺术(2024 最新版)

文章目录 2.1 基本 Linux 命令2.1.1 重点基础知识2.1.2 重点案例&#xff1a;整理下载文件夹2.1.3 拓展案例 1&#xff1a;批量重命名文件2.1.4 拓展案例 2&#xff1a;查找并删除特定文件 2.2 文件和目录管理2.2.1 重点基础知识2.2.2 重点案例&#xff1a;部署一个简单的网站2…

从零开始学howtoheap:理解fastbins的​unsorted bin攻击

how2heap是由shellphish团队制作的堆利用教程&#xff0c;介绍了多种堆利用技术&#xff0c;后续系列实验我们就通过这个教程来学习。环境可参见从零开始配置pwn环境&#xff1a;从零开始配置pwn环境&#xff1a;从零开始配置pwn环境&#xff1a;优化pwn虚拟机配置支持libc等指…

政安晨:在Jupyter中【示例演绎】Matplotlib的官方指南(二){Image tutorial}·{Python语言}

咱们接着上一篇&#xff0c;这次咱们讲使用Matplotlib绘制图像的简短尝试。 我的这个系列的上一篇文章在这里&#xff1a; 政安晨&#xff1a;在Jupyter中【示例演绎】Matplotlib的官方指南&#xff08;一&#xff09;{Pyplot tutorial}https://blog.csdn.net/snowdenkeke/ar…

【Java八股面试系列】JVM-类和对象加载过程

目录 类和对象的加载过程 类的生命周期 类的加载过程 加载 验证 准备 解析 初始化 类卸载 对象的加载过程 类和对象的加载过程 什么是类加载和对象加载? 类加载&#xff08;Class Loading&#xff09;&#xff1a;这是指JVM在运行时将类的字节码文件加载到内存中的…

【5G NR】【一文读懂系列】移动通讯中使用的信道编解码技术-卷积码原理

目录 一、引言 二、卷积编码的发展历史 2.1 卷积码的起源 2.2 主要发展阶段 2.3 重要里程碑 三、卷积编码的基本概念 3.1 基本定义 3.2 编码器框图 3.3 编码多项式 3.4 网格图(Trellis)描述 四、MATLAB示例 一、引言 卷积编码&#xff0c;作为数字通信领域中的一项…

快速学习Spring

Spring 简介 Spring 是一个开源的轻量级、非侵入式的 JavaEE 框架&#xff0c;它为企业级 Java 应用提供了全面的基础设施支持。Spring 的设计目标是简化企业应用的开发&#xff0c;并解决 Java 开发中常见的复杂性和低效率问题。 Spring常用依赖 <dependencies><!-…

java之Maven

1. maven Maven是管理和构建java项目的工具 项目依赖资源(jar包)的管理,避免版本冲突统一项目结构项目构建&#xff0c;标准跨平台(Linux,window,MacOS)的自动化项目管理 2.maven依赖仓库 2.maven安装 maven安装视频教程 3. IDEA集成Maven 4. maven的依赖范围 5. maven生命…

可视化大屏:工作要干的好,也要汇报好,不然资源为啥向你倾斜。

有些友友们感受不到可是大屏的价值&#xff0c;认为没啥作用&#xff0c;这就是典型的下层思维&#xff0c;格局小了。 估计也没有当过领导或者管理层。可视化大屏的其他价值放在一边不说&#xff0c;就单纯这个汇报价值就十分巨大&#xff0c;包括对内和对外的汇报。 如何让…

【51单片机】DS18B20(江科大)

一、DS18B20温度传感器 1.DS18B20介绍 DS18B20是一种常见的数字温度传感器,其控制命令和数据都是以数字信号的方式输入输出,相比较于模拟温度传感器,具有功能强大、硬件简单、易扩展、抗干扰性强等特点 测温范围 :- 55℃到125℃ 通信接口:1-Wire(单总线) 其它特征:可形成…

【lesson51】信号之信号处理

文章目录 信号处理可重入函数volatileSIGCHLD信号 信号处理 信号产生之后&#xff0c;信号可能无法被立即处理&#xff0c;一般在合适的时候处理。 1.在合适的时候处理&#xff08;是什么时候&#xff1f;&#xff09; 信号相关的数据字段都是在进程PCB内部。 而进程工作的状态…

【动态规划】【数学】【C++算法】1449. 数位成本和为目标值的最大数字

作者推荐 【深度优先搜索】【树】【图论】2973. 树中每个节点放置的金币数目 本文涉及知识点 动态规划汇总 LeetCode1449. 数位成本和为目标值的最大数字 给你一个整数数组 cost 和一个整数 target 。请你返回满足如下规则可以得到的 最大 整数&#xff1a; 给当前结果添加…

《UE5_C++多人TPS完整教程》学习笔记1 ——《P2 关于本课程(About This Course)》

本文为B站系列教学视频 《UE5_C多人TPS完整教程》 —— 《P2 关于本课程&#xff08;About This Course&#xff09;》 的学习笔记&#xff0c;该系列教学视频为 Udemy 课程 《Unreal Engine 5 C Multiplayer Shooter》 的中文字幕翻译版&#xff0c;UP主&#xff08;也是译者&…

使用 Windows 11/10 上的最佳 PDF 转 Word 转换器释放 PDF 的潜力

毫无疑问&#xff0c;PDF 是最好的文档格式之一&#xff0c;但就像其他格式一样&#xff0c;有时它们确实会带来一些限制。例如&#xff0c;在某些情况下&#xff0c;您可能想要将 PDF 转换为 Word。在这种情况下&#xff0c;您始终可以借助 PDF 到 Word 转换器的帮助。 为了说…

ChatGPT高效提问—prompt实践(生成VBA)

ChatGPT高效提问—prompt实践&#xff08;生成VBA&#xff09; 2. 生成VBA函数操作Excel ​ 当前Excel表格数据无背景颜色&#xff0c;区分不明显。假如我们想美化数据展示效果&#xff0c;把标题行设置为浅蓝色&#xff0c;其余奇数行设置为橙色&#xff0c;该怎么操作呢&am…

Spark MLlib

目录 一、Spark MLlib简介 &#xff08;一&#xff09;什么是机器学习 &#xff08;二&#xff09;基于大数据的机器学习 &#xff08;三&#xff09;Spark机器学习库MLlib 二、机器学习流水线 &#xff08;一&#xff09;机器学习流水线概念 &#xff08;二&#xff09…

【Java程序设计】【C00249】基于Springboot的私人健身与教练预约管理系统(有论文)

基于Springboot的私人健身与教练预约管理系统&#xff08;有论文&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于Springboot的私人健身与教练预约管理系统 本系统分为系统功能模块、管理员功能模块、教练功能模块以及用户功能模块。 系统功能模…

小白速成法:剖析一个Android项目以快速上手

这是一个基于Tasmota的设备、用MQTT协议来通信控制的安卓应用程序。支持ON/OFF命令插座和基本的RGB LED控制。 源码点击此处 只需要关注SmartController-main\app\src的代码 项目解压之后如图 只需要关注“app”文件夹里的东西即可&#xff0c;“gradle”是配置文件&#xf…

【国产MCU】-CH32V307-基本定时器(BCTM)

基本定时器(BCTM) 文章目录 基本定时器(BCTM)1、基本定时器(BCTM)介绍2、基本定时器驱动API介绍3、基本定时器使用实例CH32V307的基本定时器模块包含一个16 位可自动重装的定时器(TIM6和TIM7),用于计数和在更新新事件产生中断或DMA 请求。 本文将详细介绍如何使用CH32…

服务治理中间件-Eureka

目录 简介 搭建Eureka服务 注册服务到Eureka 简介 Eureka是Spring团队开发的服务治理中间件&#xff0c;可以轻松在项目中&#xff0c;实现服务的注册与发现&#xff0c;相比于阿里巴巴的Nacos、Apache基金会的Zookeeper&#xff0c;更加契合Spring项目&#xff0c;缺点就是…