第一个程序:共享内存读取程序(消费者)

该程序作为消费者,从共享内存中读取数据,通过信号量保证只有当生产者写入数据后才能读取。

/*4 - 读共享内存*/
#include<stdio.h>                  // 标准输入输出库
#include<stdlib.h>                 // 标准库,包含exit等函数
#include<unistd.h>                 // 包含UNIX系统调用,如sleep
#include <sys/types.h>             // 定义系统数据类型
#include <sys/ipc.h>               // 包含IPC(进程间通信)相关函数定义
#include <sys/shm.h>               // 共享内存相关函数定义
#include <sys/sem.h>               // 信号量相关函数定义
#include <string.h>                // 字符串处理函数库int main()
{//1、确定 文件路径名 ---获取消息队列的key值key_t key = ftok(".", 'a');    // 用当前目录和字符'a'生成唯一key值,用于标识共享内存和信号量if(key == -1){                 // 检查ftok函数调用是否失败perror("ftok error");      // 输出错误信息return -1;                 // 程序异常退出}//2、根据key值 获取共享内存的ID,如果共享内存不存在则创建// IPC_CREAT表示不存在则创建,0666表示权限(所有者、组、其他用户都可读写)int shmid = shmget(key,100,IPC_CREAT|0666);  if(shmid==-1){                 // 检查shmget函数调用是否失败perror("shmget");          // 输出错误信息exit(-1);                  // 程序异常退出}//根据key创建/获取信号量集,创建1个信号量int semid = semget(key,1,IPC_CREAT|0666);  if(semid==-1){                 // 检查semget函数调用是否失败perror("semget");          // 输出错误信息exit(-1);                  // 程序异常退出}//3.映射共享内存到当前进程的地址空间,第二个参数0表示由系统自动分配地址void *p = shmat(shmid,0,0);    if(p==(void *)-1){             // 检查shmat函数调用是否失败perror("shmat");           // 输出错误信息exit(-1);                  // 程序异常退出}//4.使用共享内存(消费者逻辑)struct sembuf buf;             // 定义信号量操作结构体while(1){                      // 无限循环,持续读取数据buf.sem_num = 0;           // 操作第0个信号量(信号量集索引)buf.sem_op = -1;           // 执行P操作(申请资源,信号量值减1)buf.sem_flg = 0;           // 0表示阻塞模式,若资源不可用则等待semop(semid,&buf,1);       // 执行信号量操作printf("data = %d\n",*(int *)p);  // 从共享内存读取整数并打印}//5.不再使用可以解除映射(实际不会执行,因为上面是无限循环)shmdt(p);//6.不再需要可以删除共享内存(注释掉表示不自动删除)//shmctl(shmid,IPC_RMID,0);return 0;
}

第二个程序:共享内存写入程序(生产者)

该程序作为生产者,向共享内存中写入数据,通过信号量通知消费者可以读取数据。

/*5 - 信号量集保护共享内存*/
#include<stdio.h>                  // 标准输入输出库
#include<stdlib.h>                 // 标准库,包含exit等函数
#include<unistd.h>                 // 包含UNIX系统调用,如sleep
#include <sys/types.h>             // 定义系统数据类型
#include <sys/ipc.h>               // 包含IPC(进程间通信)相关函数定义
#include <sys/shm.h>               // 共享内存相关函数定义
#include <string.h>                // 字符串处理函数库
#include <sys/sem.h>               // 信号量相关函数定义int main()
{//1、确定 文件路径名 ---获取消息队列的key值key_t key = ftok(".", 'a');    // 用当前目录和字符'a'生成唯一key值,与消费者保持一致if(key == -1){                 // 检查ftok函数调用是否失败perror("ftok error");      // 输出错误信息return -1;                 // 程序异常退出}//2、根据key值 获取共享内存的ID,如果共享内存不存在则创建// IPC_CREAT表示不存在则创建,0666表示权限(所有者、组、其他用户都可读写)int shmid = shmget(key,100,IPC_CREAT|0666);  if(shmid==-1){                 // 检查shmget函数调用是否失败perror("shmget");          // 输出错误信息exit(-1);                  // 程序异常退出}//根据key创建/获取信号量集,创建1个信号量int semid = semget(key,1,IPC_CREAT|0666);  if(semid==-1){                 // 检查semget函数调用是否失败perror("semget");          // 输出错误信息exit(-1);                  // 程序异常退出}//设置信号量的初始值为0(用于同步,初始状态下没有数据可读)semctl(semid,0,SETVAL,0);//3.映射共享内存到当前进程的地址空间,第二个参数0表示由系统自动分配地址void *p = shmat(shmid,0,0);    if(p==(void *)-1){             // 检查shmat函数调用是否失败perror("shmat");           // 输出错误信息exit(-1);                  // 程序异常退出}struct sembuf buf;             // 定义信号量操作结构体//生产者逻辑while(1){                      // 无限循环,持续写入数据printf("请输入一个整数:");  // 提示用户输入scanf("%d",(int *)p);      // 将用户输入的整数写入共享内存buf.sem_num = 0;           // 操作第0个信号量(信号量集索引)buf.sem_op = 1;            // 执行V操作(释放资源,信号量值加1)buf.sem_flg = 0;           // 0表示阻塞模式semop(semid,&buf,1);       // 执行信号量操作,通知消费者有新数据}//5.不再使用可以解除映射(实际不会执行,因为上面是无限循环)shmdt(p);//6.不再需要可以删除共享内存(注释掉表示不自动删除)//shmctl(shmid,IPC_RMID,0);return 0;
}

两个程序的整体作用

这两个程序配合实现了基于共享内存和信号量的进程间通信,具体说明如下:

  1. 核心功能

    • 生产者程序(第二个)从用户输入获取整数,写入共享内存,并通过信号量通知消费者。
    • 消费者程序(第一个)通过信号量等待,当检测到生产者写入数据后,从共享内存读取并打印该整数。
  2. 信号量的作用

    • 实现了生产者和消费者的同步:消费者必须等待生产者写入数据后才能读取,避免读取到空数据或旧数据。
    • 信号量初始值为 0,生产者写入数据后执行 V 操作(值 + 1),消费者读取前执行 P 操作(值 - 1),确保数据读写顺序正确。
  3. 共享内存的作用
    提供了一个两个进程都能访问的内存区域,实现高效的数据传递(相比管道等方式,共享内存无需数据拷贝,速度更快)。

通过这种机制,两个独立进程可以安全、有序地进行数据交换。


如果没有 PV 操作(或类似的同步互斥机制),在多任务 / 多进程环境中会出现一系列严重问题,核心是共享资源访问冲突任务执行顺序混乱,具体表现如下:

1. 共享资源 “争抢” 导致数据混乱(互斥问题)

假设有两个任务同时操作一个共享变量(比如银行账户余额):

  • 任务 A 要给账户加 100 元,读取当前余额为 500 元,准备计算成 600 元;
  • 此时任务 B 突然介入,读取余额还是 500 元,减去 50 元(计算成 450 元)并写入;
  • 任务 A 接着执行,把自己计算的 600 元写入。

最终余额变成 600 元(正确结果应为 500+100-50=550 元),数据被破坏。

没有 PV 操作时,多个任务会 “同时” 抢占共享资源,导致操作交叉覆盖,结果错误。

2. 任务执行顺序失控(同步问题)

比如任务 A 负责采集传感器数据,任务 B 负责处理数据:

  • 任务 B 可能在任务 A 还没采集到数据时就开始处理,导致处理 “空数据”;
  • 任务 A 可能连续采集了多组数据,但任务 B 只处理了其中一部分,导致数据积压或丢失。

没有 PV 操作时,任务间缺乏 “等待 - 通知” 机制,无法按逻辑顺序执行,系统功能紊乱。

3. 系统陷入 “死锁” 或 “饥饿”

  • 死锁:两个任务分别占用对方需要的资源,且都不释放,互相等待对方放手,导致系统卡死。例如:任务 A 占用打印机,等待扫描仪;任务 B 占用扫描仪,等待打印机,两者永远僵持。
  • 饥饿:高优先级任务持续抢占资源,低优先级任务永远得不到执行机会(比如一个后台统计任务始终无法运行)。

没有 PV 操作时,无法合理分配资源使用权,容易出现这类极端问题。

4. 实时系统失去 “实时性”

在实时系统(如工业控制、自动驾驶)中,任务必须在严格时间内响应。

  • 若多个任务无序争抢 CPU 或外设,关键任务(如紧急刹车控制)可能被低优先级任务打断,导致响应超时,引发安全事故。

没有 PV 操作时,无法保证高优先级任务的优先执行权,实时性无从谈起。

总结

PV 操作(及信号量机制)是多任务系统的 “交通规则”:

  • 没有规则,车辆(任务)会乱抢车道(资源),导致撞车(数据错误)、堵车(死锁)、紧急车辆被堵(实时性失效)。
  • 因此,任何支持多任务的系统(如 μC/OS、Linux 内核)都必须有类似 PV 操作的同步互斥机制,否则根本无法稳定运行。


P 和 V 是荷兰语 “Proberen” 和 “Verhogen” 的缩写)。在操作系统和多任务编程中,PV 操作是配合信号量使用的一组原子操作,主要作用是实现任务间的同步与互斥,确保共享资源的安全访问。

具体作用可以用两个生活场景理解:

1. 实现 “互斥”:防止多个任务同时操作共享资源

比如多个任务需要使用同一个打印机(共享资源):

  • P 操作:相当于 “申请使用打印机”。如果打印机空闲(信号量 > 0),任务占用它;如果被占用(信号量 = 0),任务就排队等待,不能强行使用。
  • V 操作:相当于 “用完打印机归还”。释放打印机后,通知排队的任务 “现在可以使用了”。

通过 PV 操作,能保证同一时间只有一个任务使用打印机,避免打印内容混乱。

2. 实现 “同步”:控制任务的执行顺序

比如任务 A 需要先生产数据,任务 B 才能处理数据:

  • 任务 A 生产完数据后,执行V 操作(相当于 “通知 B 可以处理了”)。
  • 任务 B 开始前执行P 操作(相当于 “等待 A 的通知”),如果没收到通知(信号量 = 0),就暂停等待,直到 A 通知后再执行。

通过 PV 操作,能确保 B 在 A 完成后才执行,避免 B 因没数据而 “做无用功”。

总结

PV 操作的核心作用是通过信号量的 “申请 - 释放” 机制,协调多个任务的执行节奏

  • 防止多个任务同时操作共享资源(互斥);
  • 保证任务按预期顺序执行(同步);
  • 避免数据混乱、资源冲突等问题,让多任务系统稳定运行。

这在嵌入式系统(如 μC/OS)、操作系统内核等场景中非常重要。

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

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

相关文章

JeecgBoot(1):前后台环境搭建

1 项目介绍 JeecgBoot 是一款基于 Java 的 AI 低代码平台&#xff0c;它采用了 SpringBoot、SpringCloud、Ant Design Vue3、Mybatis 等技术栈&#xff0c;并集成了代码生成器、AI 对话助手、AI 建表、AI 写文章等功能。JeecgBoot 的设计宗旨是实现简单功能零代码开发&#xf…

Nestjs框架: 关于 OOP / FP / FRP 编程

概述 在软件开发过程中&#xff0c;不同的编程范式为我们提供了多样化的思维方式与实现路径它们不仅影响着代码的结构和逻辑组织方式&#xff0c;也深刻影响着项目的可维护性、可扩展性以及团队协作效率 什么是 OOP、FP 和 FRP&#xff1f;首先从三个术语的含义入手 1 &#xf…

elememtor 添加分页功能

各位看官好&#xff0c;最近在忙着使用elementor搭建自己的网站&#xff0c;由于我不是专业的程序员和前端&#xff0c;又没有很多钱去找外包公司实现自己的设计&#xff0c;所以选择了elementor. 总的来说这是一个不错的wordpress 插件&#xff0c;也让我们这种非专业的网站设…

关于“PromptPilot” 之2 -目标系统:Prompt构造器

目标系统&#xff1a;Prompt构造器想法首先&#xff0c;在抽象层对PromptPilot进行封装给出提示词形成过程的全部环节。然后&#xff0c;在 形成一套确定的提示词后再为 小规模试点方案生成一整套开发工具并配套集成开发环境和指南。最后&#xff0c;在小规模试点成功后进行拓展…

短剧小程序系统开发:重塑影视内容消费格局

在数字化浪潮的推动下&#xff0c;影视内容消费正经历着深刻的变革。短剧小程序系统开发作为这一变革的重要力量&#xff0c;正在重塑影视内容消费的格局&#xff0c;为用户带来更加个性化、便捷化的观影体验。传统影视内容消费往往受到时间和空间的限制&#xff0c;用户需要前…

一文掌握最新版本Monocle3单细胞轨迹(拟时序)分析

许多大佬的软件想要构建一个大而美的生态&#xff0c;从 monocle2 开始就能做单细胞的质控、降维、分群、注释这一系列的分析&#xff0c;但不幸的是我们只知道 monocle 系列还是主要做拟时序分析&#xff0c;一方面是因为 Seurat 有先发优势&#xff0c;出名要趁早&#xff0c…

spark入门-helloword

我们学习编程语言的时候&#xff0c;第一个程序就是打印一下 “hello world” &#xff0c;对于大数据领域的第一个任务则是wordcount。那我们就开始我们的第一个spark任务吧&#xff01; 下载spark 官方下载地址&#xff1a;Apache Download Mirrors 下载完毕以后&#xff0c…

雷达系统设计学习:自制6GHz FMCW Radar

国外大神自制6GHZ FMCW Radar开源项目: https://github.com/Ttl/fmcw3 引言 之前我做过一个简单的调频连续波&#xff08;FMCW&#xff09;雷达&#xff0c;能够探测到100米范围内人体大小的物体。虽然它确实能用&#xff0c;但由于预算有限&#xff0c;还有很大的改进空间。 …

系统选择菜单(ubuntu grub)介绍

好的&#xff0c;我们来详细解释一下什么是Ubuntu的GRUB菜单。 简单来说&#xff0c;GRUB菜单是您电脑启动时看到的第一个交互界面&#xff0c;它就像一个“系统选择”菜单&#xff0c;让您决定接下来要启动哪个操作系统或进入哪种模式。详细解释 1. GRUB是什么&#xff1f; GR…

方案C,version2

实现一个简单的Helloworld网页,并通过GitHub Actions自动构建并推送到公开仓库的gh-pages分支。同时,使用PAT进行认证,确保源码在私有仓库中,构建后的静态文件在公开仓库中。 重新设计deploy.yml内容如下(针对纯静态文件,无需构建过程): 步骤: 检出私有仓库源码。 由于…

R 语言科研绘图 --- 其他绘图-汇总1

在发表科研论文的过程中&#xff0c;科研绘图是必不可少的&#xff0c;一张好看的图形会是文章很大的加分项。 为了便于使用&#xff0c;本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中&#xff0c;获取方式&#xff1a; R 语言科研绘图模板 --- sciRplothttps://mp.…

webpack 原理及使用

【点赞收藏加关注,前端技术不迷路~】 一、webpack基础 1.核心概念 1)entry:定义入口,webpack构建的第一步 module.exports ={entry:./src/xxx.js } 2)output:出口(输出) 3)loader:模块加载器,用户将模块的原内容按照需求加载成新内容 比如文本加载器raw-loade…

「日拱一码」039 机器学习-训练时间VS精确度

目录 时间-精度权衡曲线&#xff08;不同模型复杂度&#xff09; 训练与验证损失对比 帕累托前沿分析&#xff08;3D&#xff09; 在机器学习实践中&#xff0c;理解模型收敛所需时间及其与精度的关系至关重要。下面介绍如何分析模型收敛时间与精度之间的权衡&#xff0c;并…

面试刷题平台项目总结

项目简介&#xff1a; 面试刷题平台是一款基于 Spring Boot Redis MySQL Elasticsearch 的 面试刷题平台&#xff0c;运用 Druid HotKey Sa-Token Sentinel 提高了系统的性能和安全性。 第一阶段&#xff0c;开发基础的刷题平台&#xff0c;带大家熟悉项目开发流程&#xff…

负载均衡、算法/策略

负载均衡一、负载均衡层级对比特性四层负载均衡 (L4)七层负载均衡 (L7)工作层级传输层 (TCP/UDP)应用层 (HTTP/HTTPS等)决策依据源/目标IP端口URL路径、Header、Cookie、内容等转发方式IP地址/端口替换重建连接并深度解析报文性能更高吞吐量&#xff0c;更低延迟需内容解析&…

StackingClassifier参数详解与示例

StackingClassifier参数详解与示例 StackingClassifier是一种集成学习方法&#xff0c;通过组合多个基分类器的预测结果作为元分类器的输入特征&#xff0c;从而提高整体模型性能。以下是关键参数的详细说明和示例&#xff1a; 1. classifiers&#xff08;基分类器&#xff09;…

嵌入式中间件-uorb解析

uORB系统详细解析 1. 系统概述 1.1 设计理念 uORB&#xff08;Micro Object Request Broker&#xff09;是一个专为嵌入式实时系统设计的发布-订阅式进程间通信框架。该系统借鉴了ROS中topic的概念&#xff0c;为无人机飞控系统提供了高效、可靠的数据传输机制。 1.2 核心特征 …

HTTP.Client 库对比与选择

HTTP.Client 库对比与选择在 Python 中&#xff0c;除了标准库 http.client&#xff0c;还有许多功能更强大、使用更便捷的 HTTP 库。以下是一些常用的库及其特点&#xff1a;1. Requests&#xff08;最流行&#xff09;特点&#xff1a;高层 API&#xff0c;简单易用&#xff…

RabbitMQ面试精讲 Day 5:Virtual Host与权限控制

【RabbitMQ面试精讲 Day 5】Virtual Host与权限控制 开篇 欢迎来到"RabbitMQ面试精讲"系列的第5天&#xff01;今天我们将深入探讨RabbitMQ中Virtual Host与权限控制的核心机制&#xff0c;这是构建安全、隔离的消息系统必须掌握的重要知识。在面试中&#xff0c;面…

【前端实战】纯HTML+CSS+JS实现蜡笔小新无尽冒险:从零打造网页版超级玛丽

摘要&#xff1a;本文将详细介绍一款完全由HTMLCSSJS实现的网页版横版闯关游戏——"蜡笔小新无尽冒险"。游戏采用纯前端技术实现&#xff0c;无需任何外部依赖&#xff0c;完美复刻了经典超级玛丽的核心玩法&#xff0c;并创新性地融入了蜡笔小新角色元素。通过本文&…