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.mzph.cn/news/680447.shtml

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

相关文章

【Python网络编程之DHCP服务器】

&#x1f680; 作者 &#xff1a;“码上有前” &#x1f680; 文章简介 &#xff1a;Python开发技术 &#x1f680; 欢迎小伙伴们 点赞&#x1f44d;、收藏⭐、留言&#x1f4ac; Python网络编程之DHCP服务器 代码见资源&#xff0c;效果图如下一、实验要求二、协议原理2.1 D…

线性时间非比较类排序之计数排序

计数排序 计数排序由 HaroldH.Seward 于1954年提出&#xff0c;它是一种非基于比较的排序算法&#xff0c;通过辅助数组来确定各元素的最终位置。因为在排序过程中不存在元素之间的比较和交换操作&#xff0c;所以当待排序数组为整数且数组内数据的范围较小时&#xff0c;其优…

计算x的平方根x含负数和复数cmath.sqrt(x)

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 计算x的平方根 x含负数和复数 cmath.sqrt(x) cmath.sqrt(-4)输出的结果是&#xff1f; import cmath import math a 4 print("【显示】a ",a) print("【执行】math.sqrt(a)&…

有状态DHCPv6快速模式配置及EUI-64介绍

正文共&#xff1a;1024 字 15 图&#xff0c;预估阅读时间&#xff1a;3 分钟 我们现在已经熟悉了IPv6的地址架构&#xff08;IPv6地址架构一本通&#xff09;&#xff0c;掌握了IPv6地址的手工配置方式&#xff08;IPv6从入门到精通&#xff09;和DHCPv6有状态地址配置&#…

openssl3.2 - osslsigncode工程的学习

文章目录 openssl3.2 - osslsigncode工程的学习概述笔记工程库地址工程的编译osslsigncodeM工程文件列表osslsigncodeM工程搭建细节原始工程实现的改动自己封装的包含openssl和curl的实现osslsigncodeM工程命令行的用法备注 - VS2019调试环境备注 - 如果要单步openssl的API学学…

第六篇:MySQL图形化管理工具

经过前五篇的学习&#xff0c;对于数据库这门技术的理解&#xff0c;我们已经在心中建立了一个城堡大致的雏形&#xff0c;通过命令行窗口&#xff08;cmd&#xff09;快速上手了【SQL语法-DDL-数据定义语言】等相关命令 道阻且长&#xff0c;数据库技术这一宝藏中还有数不清的…

人脸追踪案例及机器学习认识

1.人脸追踪机器人初制 用程序控制舵机运动的方法与机械臂项目完全相同。 由于摄像头的安装方式为上下倒转安装&#xff0c;我们在编写程序读取图像时需使用 flip 函数将 图像上下翻转。 现在&#xff0c;只需要使用哈尔特征检测得到人脸在图像中的位置&#xff0c;再指示舵机运…

796. 子矩阵的和

Problem: 796. 子矩阵的和 文章目录 思路解题方法复杂度Code 思路 这是一个二维前缀和的问题。二维前缀和的主要思想是预处理出一个二维数组&#xff0c;使得每个位置(i, j)上的值表示原数组中从(0, 0)到(i, j)形成的子矩阵中所有元素的和。这样&#xff0c;对于任意的子矩阵(x…

MySQL数据库应用实验报告——实验1 表结构创建

实验1 表结构创建 创建用于大学管理的高校管理数据库&#xff0c;数据库名为GXGL&#xff0c;包含学生的信息&#xff0c;教学单位信 息、专业信息&#xff0c;教职工信息、课程的相关信息以及学生选课信息。数据库GXGL包含下列 六个表: (1) Students: 学生信息表 (2) Depar…

N1CTF奖品一个月的ZoomEye账户使用与子域名收集(网络渗透测)

首页 - 网络空间测绘,网络安全,漏洞分析,动态测绘,钟馗之眼,时空测绘,赛博测绘 - ZoomEye("钟馗之眼")网络空间搜索引擎https://www.zoomeye.org/ZoomEye - Cyberspace Search Enginehttps://www.zoomeye.org/aboutZoomEye&#xff08;“钟馗之眼”&#xff09;是知道…

幻兽帕鲁游戏官方更新了版本,联机时提示版本不适用,无法加入,怎么办?

如果你在登录游戏的时候提示&#xff1a;您正在尝试加入的比赛正在运行不兼容的游戏版本。请尝试升级游戏版本。此时就说明你需要更新部署在服务器内的幻兽帕鲁了。 1、如果你使用幻兽帕鲁应用模板部署游戏&#xff0c;那么可以选择使用游戏配置面板一键更新。 2、如果你使用一…

Day46 300最长递增子序列 674最长连续递增子序列 718最长重复子数组 1143最长公共子序列

300 最长递增子序列 给你一个整数数组 nums &#xff0c;找到其中最长严格递增子序列的长度。 子序列是由数组派生而来的序列&#xff0c;删除&#xff08;或不删除&#xff09;数组中的元素而不改变其余元素的顺序。例如&#xff0c;[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序…

《UE5_C++多人TPS完整教程》学习笔记7 ——《P8 为项目配置 Steam(Configuring A Project for Steam)》

本文为B站系列教学视频 《UE5_C多人TPS完整教程》 —— 《P8 为项目配置 Steam&#xff08;Configuring A Project for Steam&#xff09;》 的学习笔记&#xff0c;该系列教学视频为 Udemy 课程 《Unreal Engine 5 C Multiplayer Shooter》 的中文字幕翻译版&#xff0c;UP主&…

基于设计模式,实现分布式锁的资源管理

org.redisson.api.RLock&#xff0c;是目前较为常见的分部署锁实现方式。我们的目的是实现自动管理锁的获取和释放。 但遗憾的是&#xff0c;RLock并不实现AutoCloseable接口&#xff0c;因此不能直接用在try-with-resources结构中。不过&#xff0c;我们可以通过创建一个包装类…

【RISC-V DSP设计】基于CEVA DSP架构的指令集分析(一)-总体介绍

目录 一、引言 二、CEVA-BX1™ DSP Library 概述 三、CEVA-BX1™ DSP Library 功能与特点 四、CEVA-BX1™ DSP Library 优势 今天开始我们继续对CEVA DSP的架构和指令集进行分析&#xff0c;基于对CEVA DSP的分析和了解&#xff0c;后续可以进行基于RISC-V内核架构的DSP指令…

【Python】洛谷P4325 [COCI2006-2007#1] Modulo

P4325 [COCI2006-2007#1] Modulo 题面翻译 给出 10 10 10 个整数&#xff0c;问这些整数除以 42 42 42 后得到的余数有多少种。 第一个样例的十个结果是 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 1,2,3,4,5,6,7,8,9,10 1,2,3,4,5,6,7,8,9,10&#xff0c;有 10 10 10 个不…

软件架构与系统架构:区别与联系的分析

软件架构与系统架构&#xff1a;区别与联系的分析 在信息技术领域&#xff0c;软件架构和系统架构这两个术语经常被提及。尽管它们在某些方面有重叠&#xff0c;但它们确实代表了不同的概念和聚焦点。理解这两种架构之间的区别和联系对于任何从事技术开发和设计的专业人士都是至…

【C语言】指针专项练习 都是一些大厂的笔试真题 附有详细解析,带你深入理解指针

一.sizeof()与strlen() sizeof是一个操作符&#xff0c;而strlen是一个库函数。 数组名代表首元素地址&#xff0c;有两种情况例外&#xff0c;第一种是数组名单独放在sizeof内部&#xff0c;第二种是&数组名&#xff0c;这两种情况下数组名代表的是整个数组。sizeof(arr…

ES实战--wildcard正则匹配exists过滤字段是否存在

wildcard 通配符中的 * 表示任意数量的字符 ?表示任意单个字符 #正则匹配 GET /wildcard-test/_search {"query": {"wildcard": {"title": {"wildcard": "ba*n"}}} } #响应:"hits": {"total": {"…

Vue + Element UI el-table + sortablejs 行、列拖拽排序

实现Element UI中的el-table表格组件的行和列的拖拽排序 使用 Vue3 Element Plus UI sortablejs 安装sortablejs pnpm install sortablejs行拖拽 基本实现 效果 <script setup> import { onMounted, ref } from "vue"; import Sortable from "sort…