Linux进程间通信【一】

进程间通信介绍

进程间通信的概念

进程间通信简称IPC(Interprocess communication),进程间通信就是在不同进程之间传播或交换信息。

进程间通信的目的

  • 数据传输:一个进程需要将它的数据发送给另一个进程
  • 资源共享:多个进程之间共享同样的资源。
  • 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。
  • 进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。

进程间通信的本质

进程间通信的本质:让不同的进程先看到同一份资源 – 这份资源通常都是由OS提供

由于各个进程之间在数据层面具有独立性,要想实现通信,一定要借助第三方资源,这些进程向第三方资源读取或写入数据,进而实现进程间通信,这份资源通常是由操作系统提供的。
在这里插入图片描述

进程间通信的分类

管道

  • 匿名管道pipe
  • 命名管道

System V IPC

  • System V 消息队列
  • System V 共享内存
  • System V 信号量

POSIX IPC

  • 消息队列
  • 共享内存
  • 信号量
  • 互斥量
  • 条件变量
  • 读写锁

管道

什么是管道

管道是Unix中最古老的进程间通信的形式。
我们把从一个进程连接到另一个进程的一个数据流称为一个“管道”

在这里插入图片描述
who | wc -l 查看当前服务器上的登录用户个数,
其中,who 和 wc -l 是两个程序,运行起来后变成了两个进程,who进程通过标准输入将数据打入“管道”,wc进程再从“管道”中读取数据,完成了数据的传输。

匿名管道

匿名管道的原理

匿名管道用于进程间通信,且仅限于本地父子进程之间的通信。

进程间通信的本质就是,让不同的进程看到同一份资源,使用匿名管道实现父子进程间通信的原理就是,让两个父子进程先看到同一份被打开的文件资源,然后父子进程就可以对该文件进行写入或是读取操作,进而实现父子进程间通信。

在这里插入图片描述
注意:这里父子进程看到的同一份文件资源是由操作系统来维护的,所以当父子进程对该文件进行写入操作时,该文件缓冲区当中的数据并不会进行写时拷贝。

pipe函数

在这里插入图片描述

匿名管道使用步骤

在创建匿名管道实现父子进程间通信的过程中,需要pipe函数和fork函数搭配使用,具体步骤如下:

1、父进程调用pipe函数创建管道。
在这里插入图片描述

2、父进程创建子进程。
在这里插入图片描述

3、父进程关闭写端,子进程关闭读端。

在这里插入图片描述

注意:

  1. 管道只能够进行单向通信,因此当父进程创建完子进程后,需要确认父子进程谁读谁写,然后关闭相应的读写端。
  2. 从管道写端写入的数据会被内核缓冲,直到从管道的读端被读取。

例如,在以下代码当中,子进程向匿名管道当中写入10行数据,父进程从匿名管道当中将数据读出。

//child->write, father->read                                                                                                                                                                                                                                                                                                                                                                                                                                                        
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{int fd[2] = { 0 };if (pipe(fd) < 0){ //使用pipe创建匿名管道perror("pipe");return 1;}pid_t id = fork(); //使用fork创建子进程if (id == 0){//childclose(fd[0]); //子进程关闭读端//子进程向管道写入数据const char* msg = "hello father, I am child...";int count = 10;while (count--){write(fd[1], msg, strlen(msg));sleep(1);}close(fd[1]); //子进程写入完毕,关闭文件exit(0);}//fatherclose(fd[1]); //父进程关闭写端//父进程从管道读取数据char buff[64];while (1){ssize_t s = read(fd[0], buff, sizeof(buff));if (s > 0){buff[s] = '\0';printf("child send to father:%s\n", buff);}else if (s == 0){printf("read file end\n");break;}else{printf("read error\n");break;}}close(fd[0]); //父进程读取完毕,关闭文件waitpid(id, NULL, 0);return 0;
}

在这里插入图片描述

管道读写规则

pipe2函数与pipe函数类似,也是用于创建匿名管道,其函数原型如下:

int pipe2(int pipefd[2], int flags);

pipe2函数的第二个参数用于设置选项。
1、当没有数据可读时:

  • O_NONBLOCK disable:read调用阻塞,即进程暂停执行,一直等到有数据来为止。
  • O_NONBLOCK enable:read调用返回-1,errno值为EAGAIN。

2、当管道满的时候:

  • O_NONBLOCK disable:write调用阻塞,直到有进程读走数据。
  • O_NONBLOCK enable:write调用返回-1,errno值为EAGAIN

3、如果所有管道写端对应的文件描述符被关闭,则read返回0。
4、如果所有管道读端对应的文件描述符被关闭,则write操作会产生信号SIGPIPE,进而可能导致write进程退出。
5、当要写入的数据量不大于PIPE_BUF时,Linux将保证写入的原子性。
6、当要写入的数据量大于PIPE_BUF时,Linux将不再保证写入的原子性。

管道的特点

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

注意:
同步: 两个或两个以上的进程在运行过程中协同步调,按预定的先后次序运行。比如,A任务的运行依赖于B任务产生的数据。
互斥: 一个公共资源同一时刻只能被一个进程使用,多个进程不能同时使用公共资源。

在这里插入图片描述

命名管道

命名管道的原理

匿名管道只能用于具有共同祖先的进程(具有亲缘关系的进程)之间的通信,通常,一个管道由一个进程创建,然后该进程调用fork,此后父子进程之间就可应用该管道。
如果要实现两个毫不相关进程之间的通信,可以使用命名管道来做到。命名管道就是一种特殊类型的文件,两个进程通过命名管道的文件名打开同一个管道文件,此时这两个进程也就看到了同一份资源,进而就可以进行通信了。

注意:

  1. 普通文件是很难做到通信的,即便做到通信也无法解决一些安全问题。
  2. 命名管道和匿名管道一样,都是内存文件,只不过命名管道在磁盘有一个简单的映像,但这个映像的大小永远为0,因为命名管道和匿名管道都不会将通信数据刷新到磁盘当中。

创建一个命名管道

在这里插入图片描述

int mkfifo(const char *pathname, mode_t mode);

mkfifo函数的第一个参数是pathname,表示要创建的命名管道文件。

  • 若pathname以路径的方式给出,则将命名管道文件创建在pathname路径下。
  • 若pathname以文件名的方式给出,则将命名管道文件默认创建在当前路径下。(注意当前路径的含义)创建命名管道:

mkfifo函数的第二个参数是mode,表示创建命名管道文件的默认权限。

注意:若想创建出来命名管道文件的权限值不受umask的影响,则需要在创建文件前使用umask函数将文件默认掩码设置为0。

创建命名管道:

int main(int argc, char *argv[])
{mkfifo("p2", 0644);return 0;
}

命名管道的打开规则

1、如果当前打开操作是为读而打开FIFO时。

  • O_NONBLOCK disable:阻塞直到有相应进程为写而打开该FIFO。
  • O_NONBLOCK enable:立刻返回成功。

2、如果当前打开操作是为写而打开FIFO时。

  • O_NONBLOCK disable:阻塞直到有相应进程为读而打开该FIFO。
  • O_NONBLOCK enable:立刻返回失败,错误码为ENXIO。

用命名管道实现serve&client通信

实现服务端(server)和客户端(client)之间的通信之前,我们需要先让服务端运行起来,我们需要让服务端运行后创建一个命名管道文件,然后再以读的方式打开该命名管道文件,之后服务端就可以从该命名管道当中读取客户端发来的通信信息了。

服务端的代码如下:

#include "comm.h"int main()
{umask(0); //将文件默认掩码设置为0if (mkfifo(FILE_NAME, 0666) < 0){ //使用mkfifo创建命名管道文件perror("mkfifo");return 1;}int fd = open(FILE_NAME, O_RDONLY); //以读的方式打开命名管道文件if (fd < 0){perror("open");return 2;}char msg[128];while (1){msg[0] = '\0'; //每次读之前将msg清空//从命名管道当中读取信息ssize_t s = read(fd, msg, sizeof(msg)-1);if (s > 0){msg[s] = '\0'; //手动设置'\0',便于输出printf("client# %s\n", msg); //输出客户端发来的信息}else if (s == 0){printf("client quit!\n");break;}else{printf("read error!\n");break;}}close(fd); //通信完毕,关闭命名管道文件return 0;
}

客户端的代码如下:

#include "comm.h"int main()
{int fd = open(FILE_NAME, O_WRONLY); //以写的方式打开命名管道文件if (fd < 0){perror("open");return 1;}char msg[128];while (1){msg[0] = '\0'; //每次读之前将msg清空printf("Please Enter# "); //提示客户端输入fflush(stdout);//从客户端的标准输入流读取信息ssize_t s = read(0, msg, sizeof(msg)-1);if (s > 0){msg[s - 1] = '\0';//将信息写入命名管道write(fd, msg, strlen(msg));}}close(fd); //通信完毕,关闭命名管道文件return 0;
}

对于如何让客户端和服务端使用同一个命名管道文件,这里我们可以让客户端和服务端包含同一个头文件,该头文件当中提供这个共用的命名管道文件的文件名,这样客户端和服务端就可以通过这个文件名,打开同一个命名管道文件,进而进行通信了。

共用头文件的代码如下:

//comm.h
#pragma once#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>#define FILE_NAME "myfifo" //让客户端和服务端使用同一个命名管道

代码编写完毕后,先将服务端进程运行起来,之后我们就能在客户端看到这个已经被创建的命名管道文件。
在这里插入图片描述

命名管道和匿名管道的区别

  • 匿名管道由pipe函数创建并打开。
  • 命名管道由mkfifo函数创建,由open函数打开。
  • FIFO(命名管道)与pipe(匿名管道)之间唯一的区别在于它们创建与打开的方式不同,一旦这些工作完成之后,它们具有相同的语义。

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

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

相关文章

linux内核input子系统概述

目录 一、input子系统二、关键数据结构和api2.1 数据结构2.1.1 input_dev2.1.2 input_handler2.1.3 input_event2.1.4 input_handle 2.2 api接口2.2.1 input_device 相关接口input_device 注册流程事件上报 2.2.2 input handle 相关接口注册 handle指定 handle 2.2.3 input han…

基于springboot+vue的电影院购票系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

数码管的动态显示

1.共阴极数码管实现HELLO #include<reg51.h> char str[]{0x76,0x79,0x38,0x38,0x3F}; //HELLO char wei[]{0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80}; void delay(int n) {int i0,j0;for(i0;i<n;i){for(j0;j<120;j);} } void seg() {int i;for(i0;i<…

NC 现金流量查询 节点 多账簿联查时,根据所选择的列来判断明细和现金流量联查按钮是否可用,根据添加列选择监听事件处理。

NC 现金流量查询 节点 多账簿联查时&#xff0c;根据所选择的列来判断明细和现金流量联查按钮是否可用&#xff0c;如下图的情况&#xff1a; 在现金流量查询界面UI类的initTable(QueryConditionVO conVO)方法中添加列选择监听事件即可&#xff0c;如下&#xff1a; // 列监听…

LeetCode刷题【树状数组、并查集、二叉树】

目录 树状数组307. 区域和检索 - 数组可修改406. 根据身高重建队列673. 最长递增子序列的个数1409. 查询带键的排列 并查集128. 最长连续序列130. 被围绕的区域 二叉树94. 二叉树的中序遍历104. 二叉树的最大深度101. 对称二叉树543. 二叉树的直径108. 将有序数组转换为二叉搜索…

web性能检测工具lighthouse

About Automated auditing, performance metrics, and best practices for the web. Lighthouse 可以自动检查Web页面的性能。 你可以以多种方式使用它。 浏览器插件 作为浏览器插件&#xff0c;访问chrome网上商店 搜索Lighthouse 插件安装。以两种方式使用。 方式一 安装…

DP:路径规划模型

创作不易&#xff0c;感谢三连支持&#xff01; 路径规划主要是让目标对象在规定范围内的区域内找到一条从起点到终点的无碰撞安全路径。大多需要用二维dp数组去实现 一、不同路径 . - 力扣&#xff08;LeetCode&#xff09;不同路径 class Solution { public:int uniquePath…

重学SpringBoot3-MyBatis的三种分页方式

更多SpringBoot3内容请关注我的专栏&#xff1a;《SpringBoot3》 期待您的点赞&#x1f44d;收藏⭐评论✍ 重学SpringBoot3-MyBatis的三种分页方式 准备工作环境搭建数据准备未分页效果 1. 使用MyBatis自带的RowBounds进行分页演示 2. 使用物理分页插件演示 3. 手动编写分页SQL…

pcl 凸包ConvexHull

pcl 凸包ConvexHull 头文件等 #include <pcl/surface/convex_hull.h>typedef pcl::PointXYZ PointT; typedef pcl::PointCloud<PointT> CloudT; typedef CloudT::Ptr CP 代码 CP PSO::tubao(CP cloud) {pcl::ConvexHull<PointT> hull;hull.setInputCloud…

代码随想录算法训练营第十七天|110.平衡二叉树、257.二叉树的所有路径、404.左叶子之和

代码随想录算法训练营第十七天|110.平衡二叉树、257.二叉树的所有路径、404.左叶子之和 110.平衡二叉树 给定一个二叉树&#xff0c;判断它是否是 平衡二叉树 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;true题解&#xff1a;平衡…

查看Scala类的方法

文章目录 一、概述如何查看Scala类的方法二、使用Scala文档查看类的方法三、使用反射机制查看类的方法 一、概述如何查看Scala类的方法 本文介绍了在Scala中查看Int类方法的两种方法&#xff1a;使用Scala标准库文档和使用反射机制。通过Scala标准库文档&#xff0c;您可以方便…

【C++庖丁解牛】二叉搜索树(Binary Search Tree,BST)

&#x1f341;你好&#xff0c;我是 RO-BERRY &#x1f4d7; 致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 &#x1f384;感谢你的陪伴与支持 &#xff0c;故事既有了开头&#xff0c;就要画上一个完美的句号&#xff0c;让我们一起加油 目录 1. 二叉搜索树概念2. 二叉…

Etcd Raft 协议(进阶篇)

前言 在正式开始介绍 Raft 协议之间&#xff0c;我们有必要简单介绍一下其相关概念。在分布式系统中&#xff0c;一致性是比较常见的概念&#xff0c;所谓一致性指的是集群中的多个节点在状态上达成一致。在程序和操作系统不会崩溃、硬件不会损坏、服务器不会掉电、网络绝对可靠…

【Linux】环境变量常见指令操作&基本实验(入门必看!)

前言 大家好吖&#xff0c;欢迎来到 YY 滴Linux系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过Linux的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; YY的《C》专栏YY的《C11》专栏YY的《…

安卓实现翻转时间显示效果

效果 废话不多说上代码 自定义组件 import android.content.Context; import android.content.res.TypedArray; import android.graphics.Camera; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.…

BM83 字符串变形

import java.util.*;public class Solution {/*** 代码中的类名、方法名、参数名已经指定&#xff0c;请勿修改&#xff0c;直接返回方法规定的值即可** * param s string字符串 * param n int整型 * return string字符串*/public String trans (String s, int n) {// write co…

RK3568驱动指南|第十三篇 输入子系统-第143章 多对多的匹配关系分析

瑞芯微RK3568芯片是一款定位中高端的通用型SOC&#xff0c;采用22nm制程工艺&#xff0c;搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码&#xff0c;支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU&#xff0c;可用于轻量级人工…

ubuntu20.04_PX4_1.13

说在前面&#xff1a;&#xff08;最好找一个干净的Ubuntu系统&#xff09;如果配置环境的过程中出现很多编译的错误或者依赖冲突&#xff0c;还是建议新建一个虚拟机&#xff0c;或者重装Ubuntu系统&#xff0c;这样会避免很多麻烦&#x1f490; &#xff0c; 安装PX4 1.13.2 …

小红书扫码登录分析与python实现

文章目录 1. 写在前面2. 接口分析3. 代码实现 【&#x1f3e0;作者主页】&#xff1a;吴秋霖 【&#x1f4bc;作者介绍】&#xff1a;擅长爬虫与JS加密逆向分析&#xff01;Python领域优质创作者、CSDN博客专家、阿里云博客专家、华为云享专家。一路走来长期坚守并致力于Python…

代码随想录算法训练营第十七天(二叉树IV)| 110. 平衡二叉树、257. 二叉树的所有路径、404.左叶子之和(JAVA)

文章目录 110. 平衡二叉树解题思路源码 257. 二叉树的所有路径解题思路源码 404.左叶子之和解题思路源码 110. 平衡二叉树 给定一个二叉树&#xff0c;判断它是否是平衡二叉树 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7]输出&#xff1a;true 示例…