Linux下进程间的通信--信号量

news2024/11/15 0:20:35

前言:

资源竞争:

资源竞争(Race Condition)是多线程或多进程环境中的一种常见问题,它发生在多个进程或线程并发访问和修改同一资源(如内存位置、文件、数据库记录等)时,而最终结果依赖于竞争者的执行顺序。如果不同的执行顺序会导致不同的结果,那么就存在资源竞争问题。

资源竞争可能导致多种问题,包括数据损坏、不一致性和系统崩溃。为了解决资源竞争问题,通常需要使用同步机制来确保在任何时候只有一个线程或进程可以访问特定的资源

临界资源:

临界资源(Critical Resource)是指在多线程或多进程环境中,一次只能被一个线程或进程访问的资源。这种资源通常包括硬件设备、文件、数据库、内存区域等,它们不能被多个执行线程或进程同时使用,否则可能会导致数据不一致、资源损坏或其他未定义的行为。

为了安全地使用临界资源,必须采取措施来确保一次只有一个线程或进程可以访问它。

临界区:

临界区(Critical Section)是指程序中访问临界资源的代码区域。由于临界资源一次只能被一个线程或进程安全地访问,因此必须确保在任何时候,只有一个线程可以执行临界区代码。如果多个线程同时执行临界区,可能会导致数据不一致或竞态条件。

为了保护临界区,通常需要使用同步机制,这些同步机制确保了在任何时候,只有一个线程可以进入临界区。

同步与互斥的概念:

同步(Synchronization)和互斥(Mutual Exclusion)是并发编程中的两个核心概念,它们用于控制多个线程或进程对共享资源的访问,以防止数据不一致性和竞态条件。

互斥(Mutual Exclusion):

互斥是指在任何时候,只允许一个线程或进程访问特定的临界资源。这是为了防止多个线程同时修改同一数据,从而导致数据不一致的问题。互斥通常通过锁机制实现,比如互斥锁(mutexes)或信号量(semaphores)。

互斥的目的是确保临界区代码一次只能被一个线程执行。临界区是指访问共享资源的代码段,如果不加以控制,可能会被多个线程同时执行,从而导致不确定的行为。

同步(Synchronization):

同步是指协调多个线程或进程的执行顺序,使得它们按照预定的规则合作完成任务。同步机制允许线程或进程在某些情况下等待或通知其他线程,以便它们能够协调工作。

同步和互斥是相辅相成的。互斥是同步的一种特殊情况,它专注于确保对共享资源的访问是互斥的。而同步则是更广泛的概念,它不仅包括互斥,还包括线程之间的协调和合作。

信号量概述:

信号量(Semaphore)是一种用于多线程环境下同步和互斥控制的机制,它是一个计数器,用于控制对共享资源的访问。信号量可以解决多个线程或进程同时访问同一资源时可能出现的竞争条件问题。

信号量的核心概念是计数器,它表示可用资源的数量。

信号量的类型:

  1. 二进制信号量

    • 用于实现互斥,计数器只有两个值:0和1。
    • 通常用于保护临界资源,确保一次只有一个线程可以访问。
  2. 计数信号量

    • 用于同步,计数器可以有多个值。
    • 可以用来控制对有限资源的访问数量

信号量机制的核心:

信号量在操作系统中通常是由内核维护的,它是一个整数值,用于控制对共享资源的访问。

信号量的操作:

  1. 初始化(Set Value)
    • 在创建信号量时,可以将其初始化为一个具体的值,这个值表示可用资源的数量或信号量的初始状态。
  2. 增加(Increment)
    • 在信号量当前值的基础上加上一个数值,这通常对应于信号量的V操作(释放操作)。当一个资源被释放时,信号量的值会增加,表示现在有更多的资源可用。
  3. 减少(Decrement)
    • 在信号量当前值的基础上减去一个数值,这通常对应于信号量的P操作(等待操作)。当一个资源被占用时,信号量的值会减少,表示现在可用的资源减少了。
  4. 等待(Wait)
    • 如果信号量的值为0,则执行等待操作的进程或线程会被阻塞,直到信号量的值变为正数。这表示进程或线程在等待资源变得可用

在 Linux 系统中查询信号量使用 ipcs -s

 1.创建信号量集合

semget() 函数是 Linux 系统中用于创建新的信号量集合或获取现有信号量集合标识符的 System V IPC 函数。这个函数允许进程创建或访问一组相关的信号量。

semget()函数描述:

函数头文件:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

函数原型:
int semget(key_t key, int nsems, int semflg);

函数参数:
key_t key:这是一个键值,用于标识信号量集合。如果key是 IPC_PRIVATE,则创建一个只由调用进程及其子进程访问的信号量集合。否则key通常由ftok()函数生成,用于创建一个可以被其他进程访问的信号量集合。

int nsems:这指定了信号量集合中信号量的数量。

int semflg:这是一组标志和权限位,用于控制信号量集合的创建和访问。可以使用 IPC_CREAT 来创建信号量集合(如果它尚不存在),并且可以使用 IPC_EXCL 与 IPC_CREAT 结合使用,以确保信号量集合是新创建的(如果已经存在,则semget()调用失败)。权限位通常使用八进制数来设置,例如 0644 表示用户可读写,组和其他用户可读

函数返回值:
成功:返回信号量集标识符(一个非负整数)
失败:返回-1,设置errno表示错误原因
错误原因:
EACCES(权限不足)
EEXIST(IPC_CREAT | IPC_EXCL 被设置,但键已存在)
ENOENT(未找到键)
ENOMEM(内存不足)

 示例代码:

#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>

int main() {
    key_t key;
    int semid;
    int nsems = 3; // 需要3个信号量

    // 生成唯一的键值
    key = ftok("data", 'a');
    if (key == -1) {
        perror("ftok");
        exit(EXIT_FAILURE);
    }

    // 创建信号量集合
    semid = semget(key, nsems, 0666 | IPC_CREAT);
    if (semid == -1) {
        perror("semget");
        exit(EXIT_FAILURE);
    }

    printf("信号量集合的标识符是: %d\n", semid);

    // ... 使用信号量集合 ...

    return 0;
}

2.控制信号量集合:

semctl() 函数是 System V 信号量的一部分,用于控制信号量集合。它可以用于执行多种操作,包括获取信号量集合的信息、设置信号量的值、删除信号量集合等

semctl()函数描述:

函数头文件:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

函数原型:
int semctl(int semid, int semnum, int cmd, ...);

函数参数:
int semid:信号量集合的标识符,由semget()函数创建时返回。
int semnum:信号量集合中特定信号量的索引号,范围从 0 到 nsems - 1(nsems 是创建信号量集合时指定的数量)。
int cmd:指定要执行的操作。不同的命令需要不同的额外参数。
...:根据 cmd 指定的命令,可能需要额外的参数。

cmd命令:
IPC_STAT:获取信号量集合的状态信息。需要提供指向 struct semid_ds 的指针作为额外参数。
IPC_SET:设置信号量集合的属性。需要提供指向 struct semid_ds 的指针作为额外参数。
IPC_RMID:删除信号量集合。成功删除后,所有信号量都将被销毁。
GETVAL:获取信号量 semnum 的当前值。
SETVAL:设置信号量 semnum 的值为额外参数指定的值。
GETALL:获取信号量集合中所有信号量的值,结果存放在数组中。
SETALL:设置信号量集合中所有信号量的值,值存放在数组中

函数返回值:
成功:返回非负值。对于 IPC_STAT、IPC_SET、GETVAL、GETALL 等命令,返回的是一个值,它取决于具体的命令。
失败:返回-1,并设置 errno 以指示错误原因

示例代码:

a.初始化信号量

int semid;
union semun {
    int val;
    struct semid_ds *buf;
    unsigned short *array;
} arg;

...创建信号量集合


// 初始化信号量的值
arg.val = 1; // 设置信号量的初始值为1
if (semctl(semid, 0, SETVAL, arg) == -1) {
    perror("semctl");
    exit(EXIT_FAILURE);
}

b.删除信号量集合

 if (semctl(semid, 0, IPC_RMID, arg) == -1) {
        perror("semctl IPC_RMID");
        exit(EXIT_FAILURE);
    }

 semctl() 函数的最后一个参数是一个 union semun 类型的变量,这个联合体用于兼容不同类型的命令和参数

 semctl() 函数的最后一个参数是表示没有额外的参数传递给 semctl() 函数.

3.信号量的操作:

semop()函数描述:

在 Linux 系统中,semop() 函数是用于对 System V 信号量进行操作的函数。它可以对信号量集合中的一个或多个信号量执行原子操作,如 P(等待)操作和 V(信号)操作

​​函数头文件:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

函数原型:
int semop(int semid, struct sembuf *sops, size_t nsops);

函数参数:
int semid:信号量集合的标识符,由 semget() 函数创建时返回。
struct sembuf *sops:指向 sembuf 结构数组的指针,每个结构指定了对信号量集合中某个信号量的操作。
size_t nsops:sops 数组中的元素数量,即要执行的操作数量。

函数返回值:
成功:返回 0。
失败;返回 -1,并设置 errno 以指示错误原因
E2BIG(sem_op 值超出范围)
EACCES(权限不足)
EINTR(操作被信号中断)
EINVAL(无效的信号量编号或操作)

  • sem_num:指定信号量集合中信号量的索引号。
  • sem_op:指定要执行的操作。可以是 -1(等待,P 操作),0(测试,非阻塞等待),或 +1(信号,V 操作)。
  • sem_flg:指定操作的标志。通常使用 IPC_NOWAIT 使操作非阻塞,或者使用 SEM_UNDO 在进程退出时自动释放信号量

 示例代码:

 struct sembuf sop;

    // 等待信号量 0
    sop.sem_num = 0;
    sop.sem_op = -1; // P 操作
    sop.sem_flg = 0;  // 阻塞模式

    // 执行信号量操作
    if (semop(semid, &sop, 1) == -1) {
        perror("semop wait");
        exit(EXIT_FAILURE);
    }

    // ... 临界区代码 ...

    // 信号量 0 释放
    sop.sem_op = 1; // V 操作
    if (semop(semid, &sop, 1) == -1) {
        perror("semop signal");
        exit(EXIT_FAILURE);
    }

semtimedop() 函数描述:

semtimedop() 函数是 Linux 系统中用于对 System V 信号量进行带超时的操作的函数。它类似于 semop(),但它允许您指定一个超时时间,这样在信号量的值在指定的时间内没有变为正值时,操作不会无限期地阻塞。

函数头文件:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

函数原型:
int semtimedop(int semid, struct sembuf *sops, size_t nsops,
                      const struct timespec *timeout);

函数参数:
int semid:信号量集合的标识符,这个标识符是通过 semget() 函数创建信号量集合时返回的。
struct sembuf *sops:指向 sembuf 结构体数组的指针,该数组包含了要执行的操作。
size_t nsops:sops 数组中结构体的数量,即要执行的操作数。
const struct timespec *timeout:指向 timespec 结构的指针,该结构指定了操作的最大等待时间。如果 timeout 为 NULL,则表示如果没有信号量可用,操作将无限期地等待

函数返回值:
成功:返回 0。
失败:返回 -1,并设置 errno 以指示错误原因
EAGAIN(超时)
E2BIG(请求的操作数超出信号量集合的大小)
EACCES(权限不足)
EINTR(操作被信号中断)
EINVAL(无效的信号量编号或操作)。

结语:

无论你是初学者还是有经验的开发者,我希望我的博客能对你的学习之路有所帮助。如果你觉得这篇文章有用,不妨点击收藏,或者留下你的评论分享你的见解和经验,也欢迎你对我博客的内容提出建议和问题。每一次的点赞、评论、分享和关注都是对我的最大支持,也是对我持续分享和创作的动力

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

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

相关文章

mysql学习教程,从入门到精通,SQL AND OR 运算符(12)

1、SQL AND & OR 运算符 在本教程中&#xff0c;您将学习如何在子句中使用ASELECT column1_name, column2_name, columnN_nameFROM table_nameWHERE condition1 AND condition2;ND&#xff06;OR运算符&#xff0c;WHERE以根据多个条件过滤记录。 1.1、根据条件选择记录 …

从代码直观理解Self-Attention和Cross-Attention的本质区别

Transformer的模型架构实际上非常简单&#xff0c;Self-Attention 和 Cross-Attention 仅仅是在 k, v上有所不同&#xff08;这里不讨论 mask&#xff09;。 论文原文&#xff1a;Attention Is All You Need 我们可以使用同一个 Attention 类来实现 Self-Attention 和 Cross-At…

day11-多线程

一、线程安全问题 线程安全问题出现的原因&#xff1f;存在多个线程在同时执行同时访问一个共享资源存在修改该共享资源 线程安全:多个线程同时修改同一个资源 取钱案例 小明和小红是一对夫妻&#xff0c;他们有一个共同的账户&#xff0c;余额是10万元 如果小明和小红同时来取…

速看!6款可以写论文的ai写作网站,这才是真正的论文神器!(含教程)

在当今信息爆炸的时代&#xff0c;AI写作工具的出现极大地提高了写作效率和质量。特别是对于需要撰写论文的学生和研究人员来说&#xff0c;这些工具提供了极大的便利。本文将重点介绍一款备受推荐的AI写作平台——千笔-AIPassPaper&#xff0c;并结合相关教程帮助用户更好地使…

【北京迅为】《STM32MP157开发板使用手册》- 第二十四章 STM32CubeIDE的初步使用

iTOP-STM32MP157开发板采用ST推出的双核cortex-A7单核cortex-M4异构处理器&#xff0c;既可用Linux、又可以用于STM32单片机开发。开发板采用核心板底板结构&#xff0c;主频650M、1G内存、8G存储&#xff0c;核心板采用工业级板对板连接器&#xff0c;高可靠&#xff0c;牢固耐…

校园水电费管理微信小程序的设计与实现+ssm(lw+演示+源码+运行)

校园水电费管理小程序 摘 要 随着社会的发展&#xff0c;社会的方方面面都在利用信息化时代的优势。互联网的优势和普及使得各种系统的开发成为必需。 本文以实际运用为开发背景&#xff0c;运用软件工程原理和开发方法&#xff0c;它主要是采用java语言技术和mysql数据库来…

基于SSM的学生信息管理系统(选课管理系统)的设计与实现 (含源码+sql+视频导入教程)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1 、功能描述 基于SSM的学生信息管理系统&#xff08;选课管理系统&#xff09;13拥有三种角色 管理员&#xff1a;学生管理、教师管理、专业管理、课程管理、审批管理、课程表管理、开课管理、教室管…

高德地图JS API加载行政区边界AMap.Polygon

&#x1f916; 作者简介&#xff1a;水煮白菜王 &#xff0c;一位资深前端劝退师 &#x1f47b; &#x1f440; 文章专栏&#xff1a; 高德AMap专栏 &#xff0c;记录一下平时在博客写作中&#xff0c;总结出的一些开发技巧✍。 感谢支持&#x1f495;&#x1f495;&#x1f49…

大模型LLM之SpringAI:Web+AI(二)

2.2.2、ChatModel API(聊天模型API) 聊天模型太多了,这里只写OpenAI和Ollama ChatModel和ChatClient区别:ChatClient针对的是所有模型,共用一个客户端。而ChatModel是针对各个模型实现的。 (1)OpenAI 自动配置 <dependency><groupId>org.springframework…

vue3 内置组件 <Suspense>

官方文档&#xff1a; <Suspense> 指南-Suspense 官方提示&#xff1a; <Suspense> 是一项实验性功能。它不一定会最终成为稳定功能&#xff0c;并且在稳定之前相关 API 也可能会发生变化。 <Suspense>是一个内置组件&#xff0c;用来在组件树中协调对异步依…

git删除本地分支报错:error: the branch ‘xxx‘ is not fully merged

git删除本地分支报错&#xff1a;error: the branch xxx is not fully merged error: the branch xxx is not fully merged 直接&#xff1a; git branch -D xxx 就可以。 如果删除远程分支&#xff1a; git push origin --delete origin/xxx git强制删除本地分支 git branc…

如何将Git本地代码推送到Gitee云端仓库

如何将Git本地代码推送到Gitee云端仓库 在使用Git进行版本控制时&#xff0c;将本地代码推送到远程仓库是一个基本且重要的操作。本文将详细介绍如何将你的Git本地代码推送到Gitee&#xff08;码云&#xff09;云端仓库。Gitee是一个国内非常流行的代码托管平台&#xff0c;类…

NX—UI界面生成的文件在VS上的设置

UI界面保存生成的三个文件 打开VS创建项目&#xff0c;删除自动生成的cpp文件&#xff0c;将生成的hpp和cpp文件拷贝到项目的目录下&#xff0c;并且在VS项目中添加现有项目。 修改VS的输出路径&#xff0c;项目右键选择属性&#xff0c;链接器中的常规&#xff0c;文件路径D:…

线性代数 第七讲 二次型_标准型_规范型_坐标变换_合同_正定二次型详细讲解_重难点题型总结

文章目录 1.二次型1.1 二次型、标准型、规范型、正负惯性指数、二次型的秩1.2 坐标变换1.3 合同1.4 正交变换化为标准型 2.二次型的主要定理3.正定二次型与正定矩阵4.重难点题型总结4.1 配方法将二次型化为标准型4.2 正交变换法将二次型化为标准型4.3 规范型确定取值范围问题4.…

《中国制药设备行业市场现状分析与发展前景预测研究报告》

报告导读&#xff1a;本报告从国际制药设备发展、国内制药设备政策环境及发展、研发动态、供需情况、重点生产企业、存在的问题及对策等多方面多角度阐述了制药设备市场的发展&#xff0c;并在此基础上对制药设备的发展前景做出了科学的预测&#xff0c;最后对制药设备投资潜力…

​​操作系统 ---- 进程调度的时机、切换与过程

目录 一、进程调度的时机 1.1 什么时候需要进行进程调度与切换&#xff1f; 1.2 什么情况下不能进行进程调度与切换&#xff1f; 二、进程调度的方式 2.1 非抢占方式(Nonpreemptive Mode) 2.2 抢占方式(Preemptive Mode) 三、总结 一、进程调度的时机 进程调度&am…

FreeRTOS内部机制学习04(任务通知和软件定时器)

文章目录 何为任务通知&#xff1f;任务通知使用例子任务通知的优势以及劣势优势劣势 深入源码看看API函数内部干了什么函数的种类函数都做了啥&#xff1f; 软件定时器软件定时器的作用软件定时器内部到底做了什么实现了“闹钟”功能引入守护任务&#xff0c;守护任务做了啥&a…

SprinBoot+Vue网上购物商城的设计与实现

目录 1 项目介绍2 项目截图3 核心代码3.1 Controller3.2 Service3.3 Dao3.4 application.yml3.5 SpringbootApplication3.5 Vue 4 数据库表设计5 文档参考6 计算机毕设选题推荐7 源码获取 1 项目介绍 博主个人介绍&#xff1a;CSDN认证博客专家&#xff0c;CSDN平台Java领域优质…

我们怎么把自动化测试落地到一个项目上呢?

现在的软件测试行业已经不是原先的点点点的功能测试&#xff0c;要想在软件测试这一行中扎根稳住&#xff0c;就需要你会的很多&#xff0c;不局限于功能测试&#xff0c;还要会自动化测试、接口测试、性能测试等。 今天就来说一下自动化测试&#xff0c;首先什么是自动化测试…

简单分享-获取.txt文件内数据 文件内数据逗号分隔 分隔符 C语言

简单分享-获取.txt文件内数据 文件内数据逗号分隔 分隔符 C语言 数据存储到文件中&#xff0c;把文件数据读取到数组&#xff0c;方便数据处理。 # include <stdio.h> # include <stdlib.h> # include <string.h>#define DATANUM 307200 //数组个数 int ma…