Linux管道共享内存

前言

进程虽然是独立运行的个体,但它们之间有时候需要协作才能完成一项工作,比如有两个进程需要同步数据,进程 A 把数据准备好后,想把数据发往进程 B,进程 B 必须被提前通知有数据即将到来,或者进程 A 想发送信号给进程 B,以控制进程 B 的运行模式,再或者数据被多个进程共享时,数据变更后应该被所有进程看到,总之诸如此类的需求很多,操作系统必须要实现进程间的相互通信。

进程间通信方式有很多种,有管道、消息队列、共享内存、socket 网络通信等。在认识通信方式之前,先来认识几个概念。

公共资源

可以是公共内存、公共文件、公共硬件等,总之是被所有任务共享的一套资源。

临界区

若多个任务都访问同一公共资源,那么各任务中访问公共资源的指令代码组成的区域就称为临界区。临界区是指程序中那些访问公共资源的指令代码,即临界区是指令,并不是受访的静态公共资源。

互斥

是指某一时刻公共资源只能被 1 个任务独享,即不允许多个任务同时出现在自己的临界区中。公共资源在任意时刻只能被一个任务访问,即只能有一个任务在自己的临界区中执行,其他任务想访问公共资源时,必须等待当前公共资源的访问者完全执行完他自己的临界区代码后(使用完资源后)再开始访问。

管道

管道是进程间通信的方式之一,在 Linux 中一切皆文件,因此管道也被视为文件,只是该文件并不存在于文件系统上,而是只存在于内存中。

既然是文件,管道就要按照文件操作的函数来使用,因此也要使用open 、close 、read 、write 等方法来操作管道。管道通常被多个进程共享,而且存在于内存之中,因此共享的原理是所有进程在地址空间中都可以访问到它,管道其实就是内核空间中的内存缓冲区。

管道是用于存储数据的中转站,当某个进程往管道中写入数据后,该数据很快就会被另一个进程读取,之后可以用新的数据覆盖老数据,继续被别的进程读取,因此管道属于临时存储区,其中的数据在读取后可被清除。

管道是个环形缓冲区,环形缓冲区中一个指针用于读数据,另一个用于写数据。当缓冲区己满时,生产者要睡眠,并在睡眠前唤醒消费者,当缓冲区为空时,消费者要睡眠,并在睡眠前唤醒生产者。当缓冲区满或空时,使一方休眠,这是保证数据不丢失的方法。

管道有两端,一端用于从管道中读入数据,另一端用于往管道中写入数据。这两端使用文件描述符的方式来读取,故进程创建管道实际上是内核为其返回了用于读取管道缓冲区的文件描述符,一个描述符用于读,另一个描述符用于写。fd[0]用于读取管道,fd[1]用于写入管道。

image-20240420110732072

一般情况下,父子进程中都是一个读数据,一个写数据,并不会存在一方又读又写的情况,因此在父子进程中会分别关掉不使用的管道描述符。比如父进程负责往管道中写数据,它只需要fd[1]描述符,因此只可以通过 close系统调用关闭fd[0]。子进程负责从管道中读数据,它只需要fd[0]描述符,因此只可以通过close系统调用关闭fd[1]。

匿名管道

pipe()

image-20240420111055102

flags介绍

当没有数据可读时
1.O_NONBLOCK disable:read调用阻塞,即进程暂停执行,一直等到有数据来到为止。
2.O_NONBLOCK enable:read调用返回-1,errno值为EAGAIN。
当管道满的时候
1.O_NONBLOCK disable: write调用阻塞,直到有进程读走数据
2.O_NONBLOCK enable:调用返回-1,errno值为EAGAIN

管道读写规则

  1. 读端不读或者读的慢,写端要等读端
  2. 读端关闭,写端收到SIGPIPE信号直接终止
  3. 写端不写或者写的慢,读端等写端
  4. 写端关闭,读端读完pipe内部的数据,读到0说明到文件结尾。
  5. 当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。
  6. 当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<sys/wait.h>
//管道是单向,面向字节流,管道是有大小的,自带同步机制
int main()
{int pipefd[2] = {0};if(pipe(pipefd) != 0){perror("pipe false!");return 1;}printf("pipefd[0]:%d\n", pipefd[0]);printf("pipefd[1]:%d\n", pipefd[1]);// pipefd[0] refers to theread end of the pipe// pipefd[1] refers to the  write  end  of  the  pipe.//子进程写if(fork() == 0){close(pipefd[0]);const char* msg = "hello world\n";while(1){//只要有缓冲区就一直写sleep(1);write(pipefd[1], msg, strlen(msg));//验证管道的大小//write(pipefd[1], "c", 1);// count++;// printf("%d\n", count);}}close(pipefd[1]);//父进程读while(1){char buffer[1024] = {0};//只要有数据就一直读ssize_t s = read(pipefd[0], buffer, sizeof(buffer));if(s == 0){printf("写端退出\n");break;} else if(s > 0) {buffer[s] = '\0';printf("父进程读到子进程发来的:%s", buffer);}else break;}int status = 0;waitpid(-1, &status, 0);printf("exit code: %d\n", (status >> 8)&0xFF);printf("exit signal: %d\n", (status)&0x7F);}

总结

  1. 只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信;通常,一个管道由一个进程创建,然后该进程调用fork,此后父、子进程之间就可应用该管道
  2. 管道提供流式服务
  3. 一般而言,进程退出,管道释放,所以管道的生命周期随进程
  4. 一般而言,内核会对管道操作进行同步与互斥
  5. 管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道

命名管道

命名管道相比匿名管道,可以在不同的进程之前进行通信。相比直接继承父进程资源,命名管道要想让不同进程看到同一份资源,是通过文件名来实现的。

mkfifo()

image-20240420112229698
实现一个client和server通信的例子。

client

#include<stdio.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>
#define MY_FIFO "./fifo"
int main()
{int fd = open(MY_FIFO, O_WRONLY);if(fd < 0){perror("open error\n");return 1;}while(1){printf("请输入字符");fflush(stdout);char buffer[32] = {0};ssize_t s = read(0, buffer, sizeof(buffer) - 1);if(s > 0){buffer[s - 1] = 0;//printf("%s\n", buffer);write(fd, buffer, strlen(buffer));}}close(fd);
}

server

#include<stdio.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<string.h>
#define MY_FIFO "./fifo"int main()
{umask(0); //避免影响mkfifo的权限设置if(mkfifo(MY_FIFO, 0666) < 0){perror("mkfifo error");return 1;}int fd = open(MY_FIFO, O_RDONLY);if(fd < 0){perror("open error");return 2;}while(1){char buffer[32] = {0};ssize_t s = read(fd, buffer, sizeof(buffer) - 1);if(s > 0){buffer[s] = 0;if(strcmp(buffer, "ls") == 0){if(fork() == 0){execl("/usr/bin/ls", "ls", "-a","-l", NULL);exit(1);}waitpid(-1, NULL, 0);}printf("client#:%s\n", buffer);}else if(s == 0){printf("client quit...\n");}else{perror("read error");}}close(fd);return 0;
}

image-20240420094254168

共享内存

共享内存是最快的通信方式,systemV的IPC资源生命周期是随内核的,要通过显示释放[ipcrm -m shmid 删除memory ],或者os重启。不提供同步互斥机制。

创建共享内存shmget()

image-20240420163631476

参数

key是共享内存的名字,key能保证这个共享内存的名字是唯一的。

size:共享内存大小

shmflg:IPC_CREAT、IPC_EXCL 等IPC_CREAT和IPC_EXCL一起用能保证创建出来的共享内存是最新的

返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1

共享内存链接到进程地址空间shmat()

image-20240420164101869

参数

shmid: 共享内存标识
shmaddr:指定连接的地址
shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY
返回值:成功返回一个指针,指向共享内存第一个节;失败返回-1

shmaddr为NULL,核心自动选择一个地址。
shmaddr不为NULL且shmflg无SHM_RND标记,则以shmaddr为连接地址。
shmaddr不为NULL且shmflg设置了SHM_RND标记,则连接的地址会自动向下调整为SHMLBA的整数倍。

公式:shmaddr-(shmaddr%SHMLBA)

shmflg=SHM_RDONLY,表示连接操作用来只读共享内存。

共享内存和当前进程脱离shmdt()

int shmdt(const void *shmaddr);

参数
shmaddr: 由shmat所返回的指针
返回值:成功返回0;失败返回-1
注意:将共享内存段与当前进程脱离不等于删除共享内存段。

控制共享内存shmctl()

image-20240420164835293

参数
shmid:由shmget返回的共享内存标识码
cmd:将要采取的动作(有三个可取值)
buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
返回值:成功返回0;失败返回-1

CMD

命令说明
IPC_STAT把shmid_ds结构中的数据设置为共享内存的当前关联值
IPC_SET在进程有足够的权限下,把共享内存的当前关联值设置为shmid_ds给出的值
IPC_RMID删除共享内存段

查看共享内存

image-20240420103030140

例子

server

#include<stdio.h>
#include<sys/ipc.h>
#include<sys/shm.h>#define PATH_NAME "./"
#define PROJ_ID 0x6666#define SIZE 4096 //最好是4096的整数倍int main()
{//ftok创建key (自定义路径,自定义ID)key_t key=ftok(PATH_NAME,PROJ_ID);if(key<0){perror("ftok"); return 1;}//创建共享内存,返回的是共享内存的标识码int shmid=shmget(key,SIZE,IPC_CREAT);if(shmid<0){perror("shmget"); return 2;}printf("key:%u,shmid:%d\n",key,shmid);//attaches the shared memory//返回指向共享内存的一个地址的指针char* mesg=(char*)shmat(shmid,NULL,0);printf("attaches the shared memory success!\n");//通信开始while(1){sleep(1); printf("%s\n",mesg);}//detaches the shared memoryshmdt(mesg);printf("detaches shm success!\n");//删除共享内存段shmctl(shmid,IPC_RMID,NULL);printf("key:0x%x,shmid:%d->shm delete success\n",key,shmid);return 0;}

client

#include<stdio.h>
#include<sys/ipc.h>
#include<sys/shm.h>#define PATH_NAME "./"
#define PROJ_ID 0x6666#define SIZE 4096 //最好是4096的整数倍int main()
{key_t key=ftok(PATH_NAME,PROJ_ID);//相同的方法创建出相同的keyif(key<0){perror("ftok"); return 1;}//创建共享内存,返回的是共享内存的标识码int shmid=shmget(key,SIZE,IPC_CREAT);if(shmid<0){perror("shmget"); return 1;}//attaches the shared memory//返回指向共享内存的一个地址的指针char* mesg=(char*)shmat(shmid,NULL,0);printf("client attaches the shared memory success!\n");//开始通信//detachesshmdt(mesg);printf("client detaches success!\n");return 0;
}

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

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

相关文章

项目七:学会使用python爬虫解析库(小白大成级)

前期我们学会了怎么使用python爬虫请求库和解析库的简单应用和了解&#xff0c;同时能够对爬虫有一个较为清晰的体系&#xff0c;毕竟简单的爬虫基本上都是请求数据——解析数据——存储数据的大概流程。 那么回忆一下&#xff0c;请求库我们学的是requests模块&#xff0c;解…

项目管理-项目开发计划介绍

目录 一、内容总览 二、项目开发计划概述 2.1 概述 2.2 项自开发计划的目的和作用 2.3 项目开发计划的内容 2.3.1 工作计划 2.3.2 人员组织计划 2.3.3 设备采购和资源供应计划 2.3.4 配置管理计划 2.3.5 进度安排计划 2.3.6 成本投资计划 2.3.7 质量保证计划 2.3.8…

Linux 磁盘分区详解以及知识点分解

Linux 磁盘分区 主要命令解释 lsblk&#xff1a;查看磁盘信息&#xff0c;这个命令后面可以直接跟设备名 blkid&#xff1a;查看UUID&#xff08;全局单一标识符&#xff09;和查看文件系统&#xff0c;这个命令后面要跟分区号 fdisk&#xff1a;fdisk命令工具默认将磁盘划分…

实现I.MX6ULL开发板与Windows和Ubuntu系统之间的通信

虚拟机与主机之间的连接方式确实包括桥接模式、NAT模式和仅主机模式。 桥接模式&#xff08;Bridged&#xff09;&#xff1a;在桥接模式下&#xff0c;虚拟机通过虚拟交换机直接连接到主机的物理网络上&#xff0c;就像一台独立的物理机器一样&#xff0c;拥有自己的IP地址&a…

[阅读笔记23][JAM]JOINTLY TRAINING LARGE AUTOREGRESSIVE MULTIMODAL MODELS

这篇论文是24年1月发表的&#xff0c;然后是基于的RA-CM3和CM3Leon这两篇论文。它所提出的JAM结构系统地融合了现有的文本模型和图像生成模型。 主要有两点贡献&#xff0c;第一点是提出了融合两个模型的方法&#xff0c;第二点是为混合模型精心设计的指令微调策略。 下图是一个…

Unity射击游戏开发教程:(1)玩家控制

玩家的移动 玩家控制和移动是视频游戏中最酷的事情之一,因为你正在控制游戏中的某些东西 现在游戏中的玩家是我们的蓝色方块英雄。我在游戏开发中了解到,游戏是用简单的对象制作原型,然后添加所有漂亮的艺术和声音。代码… 我们要做的第一件事是在游戏开始时为玩家提供一个…

MATLAB 体素滤波(62)

MATLAB 体素滤波(62) 一、算法介绍二、算法实现1.代码(已验证,直接运行)一、算法介绍 这里的代码完成文件读入,体素滤波,效果显示,结果输出的操作,下面是效果截图,后面是代码。 体素滤波(Voxel Filtering)是一种用于三维点云数据处理的方法,其原理类似于二维图像…

力扣HOT100 - 142. 环形链表 II

解题思路&#xff1a; public class Solution {public ListNode detectCycle(ListNode head) {Set<ListNode> set new HashSet<>();while (head ! null) {if (!set.add(head)) {return head;}head head.next;}return null;} }

广西建筑模版厂家批发供应,当天发货全国可达

近年来&#xff0c;建筑行业蓬勃发展&#xff0c;对高质量的建筑模板需求量逐渐增加。在这个竞争激烈的市场中&#xff0c;贵港市能强优品木业有限公司以其出色的产品和卓越的服务迅速崭露头角&#xff0c;成为知名的建筑模板生产厂家。 作为一家拥有25年生产经验的公司&#x…

(SpringBoot)第十二章第一节:Spring事务和@Transactioal

文章目录 一:回顾事务的基本概念(1)事务A:定义B:事务的定义(2)事务的四个特性——ACIDA:数据库的ACID①:原子性(Atomicity)②:一致性(Consistency)③:隔离性(Isolation)④:持续性(Durability)B:破坏ACID的因素二:Spring

用FRP配置toml文件搭建内网穿透

需求场景 1、一台外网可访问的有固定ip的云服务器&#xff0c;Ubuntu系统 2、一台外网无法访问的无固定ip的本地家用电脑&#xff0c;Ubuntu系统 需求&#xff1a;将云服务器搭建为一台内网穿透服务器&#xff0c;实现通过外网访问家用电脑&#xff08;网页&#xff09;的功能。…

人工智能|机器学习——基于机器学习的信用卡办卡意愿模型预测项目

一、背景介绍 在金融领域&#xff0c;了解客户的信用卡办卡意愿对于银行和金融机构至关重要。借助机器学习技术&#xff0c;我们可以根据客户的历史数据和行为模式预测其是否有办理信用卡的倾向。本项目通过Python中的机器学习库&#xff0c;构建了两个常用的分类模型&#xff…

服务器使用-三丰云

网址&#xff1a;https://www.sanfengyun.com 部署bot使用&#xff1a;三丰云免费云服务器 bot&#xff1a;tx官方bot&#xff0c;使用 python 的 sdk 服务器体验&#xff1a;部署在云服务器上可以长期运行&#xff0c;使用宝塔面板操作方便&#xff0c;流畅&#xff0c;未出…

视频号的7个引流方式

一、账号简介引流 1、获客场景&#xff1a;账号简介相当于一张名片&#xff0c;几乎所有用户进入你的账号后&#xff0c;必然先要看简介 2、优势&#xff1a;相比于抖音、快手等短视频平台&#xff0c;视频号引流私域的路径更短&#xff0c;而且在微信端内有着得天独厚的优势…

Android JNI使用dlopen动态链接库

dlopen 是一个用于在系统中动态加载共享库&#xff08;动态链接库&#xff09;的函数。它的主要作用是在程序运行时动态地加载共享库&#xff0c;使得程序可以在不重新编译的情况下加载并使用库中的函数和变量。 具体来说&#xff0c;dlopen 函数可以接受一个共享库的路径作为…

Ardupilot无人船(车)自动调参

无人船&#xff08;车&#xff09;快速调参脚本简化了为无人船&#xff08;车&#xff09;角速率和速度控制器调参的过程。 该脚本应该在无人船&#xff08;车&#xff09;在Circle模式下循环绕圈时运行。它记录转向和油门输出以及由此产生的响应(例如无人船&#xff08;车&am…

高精度算法(2)

前言 延续上次所讲的内容再对乘法和除法进行说明&#xff0c;希望有所帮助 注意这里的乘除法都是针对于整数如果要是涉及到小数&#xff0c;我们得使用二分法 通过二分同样可以解决小数精度问题 高精度乘法 思路 我们只能用字符串来读取一个很大很大的数&#xff0c;所以…

阿赵UE学习笔记——28、粒子系统Niagara简介

阿赵UE学习笔记目录 大家好&#xff0c;我是阿赵。   继续学习虚幻引擎的使用。这次开始学习粒子系统的使用。 一、Cascade系统 在介绍UE5的Niagara系统之前&#xff0c;必须先介绍一下旧版本的粒子系统。   在UE4的时候&#xff0c;虚幻引擎的粒子系统叫做Cascade&#x…

淘宝交易API,修改交易备注taobao.trade.memo.update

淘宝交易API&#xff0c;修改交易备注taobao.trade.memo.update 需要商家或以上权限才可调用此接口&#xff0c;可重复调用本接口更新交易备注&#xff0c;本接口同时具有添加备注的功能 公共请求参数: 公共请求参数: 名称 类型 必须 描述 method String 是 API接口名…

【论文阅读】Attention is all you need

摘要 主要的序列转换模型是基于复杂的循环或卷积神经网络&#xff0c;其中包括一个编码器和一个解码器。性能最好的模型还通过一种注意力机制将编码器和解码器连接起来。我们提出了一种新的简单的网络架构&#xff0c;Transformer&#xff0c;完全基于注意机制&#xff0c;完全…