当前位置: 首页 > news >正文

Linux下的系统编程——进程间的通信(九)

 一、进程间通信常用方式

IPC方式:

        Linux环境下,进程地址空间相互独立,每个进程各自有不同的用户地址空间。任何一个进程的全局变量在另一个进程中都看不到,所以进程和进程之间不能相互访问,要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信(IPC,InterProcess Communication)。
 

        在进程间完成数据传递需要借助操作系统提供特殊的方法,如:文件、管道、信号、共享内存、消息队列、套接字、命名管道等。随着计算机的蓬勃发展,一些方法由于自身设计缺陷被淘汰或者弃用。现今常用的进程间通信方式有:


1.管道(使用最简单)

2.信号(开销最小)

3.共享映射区(无血缘关系)

4.本地套接字(最稳定)

二、管道

1.概念:

      

         管道是一种最基本的IPC机制,作用于有血缘关系的进程之间,完成数据传递。调用pipe系统函数即可创建一个管道。有如下特质:


(1).其本质是一个伪文件(实为内核缓冲区)

(2).由两个文件描述符引用,一个表示读端,一个表示写端,只能一次读取

(3).规定数据从管道的写端流入管道,从读端流出,单向流动


管道的原理:  管道实为内核使用环形队列机制,借助内核缓冲区(4k)实现

管道的:

        1)数据不能进程自己写,自己读。·

        2)管道中数据不可反复读取。一旦读走,管道中不再存在。

        3)采用半双工通信方式,数据只能在单方向上流动

        4)只能在有公共祖先的进程间使用管道

常用的通信方式: 单工通信、半双工通信、全双工通信

创建管道文件:

(不占用磁盘空间)

*2.pipe函数:

函数功能:创建,并打开管道。

    int pipe(int fd[2]);

    参数:    

        fd[0]: 读端。

        fd[1]: 写端。

    返回值:

         成功: 0

         失败: -1 errno

管道通信:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>void sys_err(const char *str)
{perror(str);exit(1);
}int main(int argc, char *argv[])
{int ret,re;int fd[2];pid_t pid;char *str = "hello pipe\n";char buf[1024];ret = pipe(fd);if(ret == -1)sys_err("pipe error");pid = fork();if(pid > 0){            //父进程close(fd[0]);       //关闭读段write(fd[1],str,strlen(str));//写入数据sleep(1);close(fd[1]);            //关闭写段}else if(pid == 0){        //子进程close(fd[1]);          //关闭写段re = read(fd[0],buf,sizeof(buf)); //读取数据write(STDOUT_FILENO,buf,re);      //写到屏幕上close(fd[0]);        //关闭读段}return 0;}

*3.管道的读写行为:

读管道:
        1. 管道有数据,read返回实际读到的字节数。

        2. 管道无数据:    

                        1)无写端,read返回0 (类似读到文件尾)

                        2)有写端,read阻塞等待。

写管道:
        1. 无读端, 异常终止。 (SIGPIPE导致的)

        2. 有读端:    

                        1) 管道已满, 阻塞等待

                        2) 管道未满, 返回写出的字节个数。

 1)读管道,管道无数据(无写端

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>void sys_err(const char *str)
{perror(str);exit(1);
}int main(int argc, char *argv[])
{int ret,re;int fd[2];pid_t pid;char *str = "hello pipe\n";char buf[1024];ret = pipe(fd);if(ret == -1)sys_err("pipe error");pid = fork();if(pid > 0){            //父进程close(fd[0]);       //关闭读段//	write(fd[1],str,strlen(str));//写入数据close(fd[1]);            //关闭写段}else if(pid == 0){        //子进程close(fd[1]);          //关闭写段re = read(fd[0],buf,sizeof(buf)); //读取数据printf("child read ret =%d\n",ret);write(STDOUT_FILENO,buf,re);      //写到屏幕上close(fd[0]);        //关闭读段}return 0;}

read返回0

4.父子间进程 :

        使用管道实现父子进程间通信,完成:ls | wc -l。假定父进程实现ls,子进程实现wc

ls | wc -l命令:

 实现流程:

(1)父进程创建管道 pipe()

(2)父进程创建子进程 fork()

(3)设置父进程执行ls命令,子进程执行wc命令 execlp()

(4)设置父子进程通过管道的单项流动(设置指向标准输出的指向管道dup2()

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <pthread.h>void sys_err(const char *str)
{perror(str);exit(1);
}int main(int argc,char *argv[])
{/***************dup2();fork();pipe();execlp();****************/int fd[2]; int ret;pid_t pid;//父进程创建管道ret = pipe(fd);if(ret == -1){sys_err("pipe error");}//父进程创建子进程 pid = fork();if(pid == -1){sys_err("fork error");}else if(pid > 0){close(fd[1]);                  //关闭写,设置单项流动dup2(fd[0],STDIN_FILENO);      //设置读管道信息execlp("wc","wc","-l",NULL);        //设置子进程wc命令sys_err("execlp wc error");}else if(pid == 0){close(fd[0]);                  //关闭读,设置单项流动dup2(fd[1],STDOUT_FILENO);     //设置写操作指向管道execlp("ls","ls",NULL);        //设置父进程执行ls命令sys_err("execlp ls error");}return 0;
}

 

 5.兄弟间进程通信:

使用管道实现兄弟进程间通信,完成:ls | wc -l。假定父进程实现ls,子进程实现wc

 实现流程:

(1)父进程创建管道 pipe()

(2)父进程创建俩个(兄弟)子进程 fork()

(3)设置兄进程执行ls命令,第进程执行wc命令 execlp() 

(4)设置兄弟进程通过管道的单项流动(设置指向标准输出的指向管道dup2()

(5)回收父进程残余文件  wait()

刚创建出的兄弟进程:

 设置兄弟进程通过管道的单项流动后

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/wait.h>void sys_err(const char *str)
{perror(str);exit(1);
}int main(int argc,char *argv[])
{/***************dup2();fork();pipe();execlp();wait();****************/int fd[2]; int ret;int i;pid_t pid;//父进程创建管道ret = pipe(fd);if(ret == -1){sys_err("pipe error");}for(i = 0;i < 2;i++){       //表达式2 出口,仅限父进程使用pid = fork();if(pid == -1){sys_err("fork error");}if(pid == 0)           //子进程出口break;}if(i == 2){                //父进程 //不需要父进程所以需要关闭他的读写并且回收掉父进程close(fd[0]);          close(fd[1]);wait(NULL);wait(NULL);}else if(i == 0){          //兄进程close(fd[0]);dup2(fd[1],STDOUT_FILENO);execlp("ls","ls",NULL);    //兄进程执行ls命令sys_err("ececlp ls error");}else if(i == 1){		   //弟进程close(fd[1]);dup2(fd[0],STDIN_FILENO);execlp("wc","wc","-l",NULL);//弟进程执行wc命令sys_err("ececlp wc error");}return 0;
}

6.多个读写端操作管道

实现一个pipe有一个写端,多个读端

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/wait.h>void sys_err(const char *str)
{perror(str);exit(1);
}int main(int argc,char *argv[])
{/***************dup2();fork();pipe();execlp();****************/int fd[2],i,n; int ret;char buf[1024];pid_t pid;//父进程创建管道ret = pipe(fd);if(ret == -1){sys_err("pipe error");exit(1);}for(i = 0;i < 2;i++){pid = fork();if(pid == -1){sys_err("fork error");exit(1);}if(pid == 0)break;}if(i == 2){            //父进程close(fd[1]);       //父进程关闭写端,留读端读取数据sleep(1);n = read(fd[0],buf,1024);    //从管道中读取数据write(STDOUT_FILENO,buf,n); for(i == 0;i < 2;i++)        //两个儿子wait两次wait(NULL);}else if(i == 0){        //兄进程close(fd[0]);write(fd[1],"1.hello\n",strlen("1.hello\n"));}else if(i == 1){		 //弟进程close(fd[0]);write(fd[1],"2.world\n",strlen("2.world\n"));}return 0;
}

7.管道缓冲区大小:

可以使用 ulimIt -a 命令来查看当前系统中创建管道文件所对应的内核缓冲区大小。通常为:
        pipe size               ......(512 bytes,-p)  8 

也可以使用fpathconf函数,借助参数―选项来查看。使用该宏应引入头文件<unistd.h>
        long fpathconf(int fd, int name);成功:返回管道的大小―失败:-1,设置errno

.8.管道的优劣

优点:简单,相比信号,套接字实现进程间通信,简单很多。

缺点:

                1.只能单向通信,双向通信需建立两个管道。
                2.只能用父子、兄弟进程(有共同祖先)间通信。该问题后来使用fifo有名管道解决)

 三、FIFO:   

fifo管道:可以用于无血缘关系的进程间通信。

    命名管道:  mkfifo 

    无血缘关系进程间通信:

                        读端,open fifo O_RDONLY

                        写端,open fifo O_WRONLY

     

1.命名管道fifo的创建和原理:

使用命令:myfifo myfifo

 使用myfifo创建

#include<stdio.h>
#include<sys/stat.h>
#include<errno.h>
#include<pthread.h>
#include<stdlib.h>void sys_err(const char *str){perror(str);exit(1);
}int main(int argc,char *str)
{int ret = mkfifo("mytestfifo",0664);if(ret == -1)sys_err("mkfifo error");return 0;}

相关文章:

Linux下的系统编程——进程间的通信(九)

一、进程间通信常用方式 IPC方式&#xff1a; Linux环境下&#xff0c;进程地址空间相互独立&#xff0c;每个进程各自有不同的用户地址空间。任何一个进程的全局变量在另一个进程中都看不到&#xff0c;所以进程和进程之间不能相互访问&#xff0c;要交换数据必须通过内核&am…...

Qt QtableWidget、QtableView表格删除选中行、删除单行、删除多行

文章目录 Qt QtableWidget表格删除选中行只能选择一行&#xff0c;点击按钮后&#xff0c;删除一行可以选择中多行&#xff0c;点击按钮后&#xff0c;删除多行选中某一列中的不同行&#xff0c;点击按钮后&#xff0c;删除多行 QTableWidgetSelectionRange介绍QTableWidget的选…...

【代码随想录day24】不同的二叉搜索树

题目 给你一个整数 n &#xff0c;求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种&#xff1f;返回满足题意的二叉搜索树的种数。 示例 1&#xff1a; 输入&#xff1a;n 3 输出&#xff1a;5示例 2&#xff1a; 输入&#xff1a;n 1 输出&#xf…...

数学建模--Subplot绘图的Python实现

目录 1.Subplot函数简介 2.Subplot绘图范例1:绘制规则子图 3.Subplot绘图范例2:绘制不规则子图 4.Subplot绘图范例3:gridspec辅助实战1 5.Subplot绘图范例4:gridspec辅助实战2 1.Subplot函数简介 """ 最近在数学建模种需要绘制多张子图,发现对于subplot函…...

JMeter(三十九):selenium怪异的UI自动化测试组合

文章目录 一、背景二、JMeter+selenium使用过程三、总结一、背景 题主多年前在某社区看到有人使用jmeter+selenium做UI自动化测试的时候,感觉很是诧异、怪异,为啥?众所周知在python/java+selenium+testng/pytest这样的组合框架下,为啥要选择jmeter这个东西[本身定位是接口测…...

c++ 移动构造方法为什么要加noexcept

背景: 最近看了候捷老师的c的教程, 他说移动构造方法要加noexcept, 在vector扩容的时候, 如果有移动构造方法没有加noexcept,是不会调用的. 个人感觉有些神奇, 这就去查下一探究竟. 过程: 测试代码如下: #include <iostream> #include <vector> struct A {A(){s…...

鸿鹄工程项目管理系统 Spring Cloud+Spring Boot+前后端分离构建工程项目管理系统

工程项目管理软件&#xff08;工程项目管理系统&#xff09;对建设工程项目管理组织建设、项目策划决策、规划设计、施工建设到竣工交付、总结评估、运维运营&#xff0c;全过程、全方位的对项目进行综合管理 工程项目各模块及其功能点清单 一、系统管理 1、数据字典&am…...

手把手教你搭建园林园艺小程序商城

现如今&#xff0c;随着互联网的快速发展&#xff0c;小程序成为了企业和个人展示产品和服务的新方式。在园林园艺行业&#xff0c;构建一个园林园艺小程序能够更好地推广和销售自己的产品和服务。那么&#xff0c;如何构建一个园林园艺小程序呢&#xff1f;下面我们来详细介绍…...

Java Iterator(迭代器)

Java迭代器&#xff08;Iterator&#xff09;是 Java 集合框架中的一种机制&#xff0c;是一种用于遍历集合&#xff08;如列表、集合和映射等&#xff09;的接口。 它提供了一种统一的方式来访问集合中的元素&#xff0c;而不需要了解底层集合的具体实现细节。 Iterator 是 …...

Logstash同步MySQL数据到ElasticSearch

当MySQL数据到一定的数量级&#xff0c;而且索引不能实现时&#xff0c;查询就会变得非常缓慢&#xff0c;所以使用ElasticSearch来查询数据。本篇博客介绍使用Logstash同步MySQL数据到ElasticSearch&#xff0c;再进行查询。 测试环境 Windows系统MySQL 5.7Logstash 7.0.1El…...

【C++】运算符重载的示例实现和应用

C运算符重载的格式&#xff1a; operator 运算符 比如要重载 ! 运算符 &#xff1a; operator ! 下面是一个例子&#xff1a; class DemoText{DemoText(string str, int num){m_text str; m_number num;}string m_text;int m_number; }这里来定义两个对象&#xff1a;…...

Kubernetes禁止调度

在Kubernetes中&#xff0c;您可以通过几种方式来禁止某个Pod调度到节点上。以下是一些方法&#xff1a; Node Selector&#xff1a;您可以使用Node Selector来限制Pod只能调度到带有特定标签的节点上。如果您希望完全禁止Pod调度到某些节点上&#xff0c;可以确保这些节点不拥…...

CocosCreator3.8研究笔记(七)CocosCreator 节点和组件的介绍

相信很多新手朋友&#xff0c;肯定会问&#xff0c;CocosCreator 中什么是节点&#xff1f;什么是组件&#xff1f; 一、什么是组件&#xff08;Component&#xff09;&#xff1f; Cocos Creator 3.8 的工作流程是以组件式开发为核心&#xff0c;即以组合而非继承的方式进行游…...

Ceph入门到精通-C++入门知识点

C中的双冒号(::)是作用域分解运算符&#xff08;scope resolution operator&#xff09;。 它主要有以下两种用法&#xff1a; 用于区分同名的不同成员&#xff0c;例如在不同类中声明了同名的成员函数或成员变量&#xff0c;可以使用A::B的方式来特指A类的B成员。当全局变量…...

Ansible之playbook详解和应用实例

目录 一、playbook简介 1.什么是playbook 2.playbook组成 二、应用实例 1.使用playbook安装启用httpd服务 2.使用playbook安装启用nginx服务 三、ansible-playbook其他用法 1.检查yaml文件的语法是否正确 2.检查tasks任务 3.检查指定的主机 4.指定从某个task开始运行…...

经验萃取方法

【经验萃取】 经验萃取不是简单的总结提炼归纳&#xff01; 经验萃取需经过还原、复盘分析、萃取重构 一.经验萃取前三个准备 1.定主题&#xff1a; 萃取主题选择&#xff08;阐述原因、确定级别、差距/问题是源头&#xff09;->多维评分&#xff1a;普遍性、重要性、迫切…...

手写apply方法

<script>/** 手写apply方法 * */Function.prototype.myApply function (context, args) {console.log(this, sss)//fnconst key Symbol()context[key] thiscontext[key](...args)delete context[key]return context[key]}const obj {name: zs,age: 18}function fn …...

Jenkins实现基础CD操作

操作截图 在Jenkins里面设置通过标签进行构建 在Jenkins中进入项目&#xff0c;配置以下 将execute shell换到invoke top-level maven targets之前 在gitlab中配置标签 代码迭代新的版本 项目代码迭代 修改docker-compose.yml 提交新版本的代码 在Jenkins中追加新…...

开源软件合集(Docker)

Docker安装 1.安装命令&#xff1a;curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun2.启动&#xff1a;systemctl start docker3.停止&#xff1a;systemctl stop docker4.重启&#xff1a;systemctl restart docker5.开机启动&#xff1a;systemctl enab…...

Ceph入门到精通-生产日志级别设置

Ceph 子系统及其日志记录级别的信息。 了解 Ceph 子系统及其日志记录级别 Ceph 由多个子系统组成&#xff1a; 每个子系统都有其日志记录级别&#xff1a; 默认情况下存储在 /var/log/ceph/ 目录中的输出日志&#xff08;日志级别&#xff09;存储在内存缓存中的日志&#…...

16-MyCat

一 Mycat概述 1 什么是Mycat 什么是Mycat Mycat是数据库中间件&#xff0c;所谓数据库中间件是连接Java应用程序和数据库中间的软件。 为什么要用Mycat 遇到问题&#xff1a; Java与数据库的紧耦合高访问量高并发对数据库的压力读写请求数据不一致 2 Mycat与其他中间件区别 目…...

RKNPU2通用API和零拷贝API

RKNPU2通用API 通用API接口按照异构编程规范&#xff0c;需要将数据拷贝到NPU运行时的内存空间。 通用API部署流程 初始化上下文&#xff0c;需要先创建上下文对象和读取模型文件 rknn_context ctx; model load_model(model_path, &model_len); ret rknn_init(&ctx…...

LeetCode 1123. 最深叶节点的最近公共祖先:DFS

【LetMeFly】1123.最深叶节点的最近公共祖先 力扣题目链接&#xff1a;https://leetcode.cn/problems/lowest-common-ancestor-of-deepest-leaves/ 给你一个有根节点 root 的二叉树&#xff0c;返回它 最深的叶节点的最近公共祖先 。 回想一下&#xff1a; 叶节点 是二叉树…...

多线程应用——线程池

线程池 文章目录 线程池1.什么是线程池2.为什么要用线程池3.怎么使用线程池4.工厂模式5.自己实现一个线程池6.创建系统自带的线程池6.1 拒绝策略6.2 线程池的工作流程 1.什么是线程池 字面意思&#xff0c;一次创建多个线程&#xff0c;放在一个池子(集合类)&#xff0c;用的时…...

OPENCV+QT环境配置

【qtopencv开发入门&#xff1a;4步搞定opencv环境配置2】https://www.bilibili.com/video/BV1f34y1v7t8?vd_source0aeb782d0b9c2e6b0e0cdea3e2121eba 第一步&#xff1a; 安装QT Qt 5.15 第二步&#xff1a; 安装OPENCV VS2022 Opencv4.5.5 C 配置_愿飞翔的鱼儿的博客…...

Kafka3.0.0版本——文件清理策略

目录 一、文件清理策略1.1、文件清理策略的概述1.2、文件清理策略的官方文档1.3、日志超过了设置的时间如何处理1.3.1、delete日志删除&#xff08;将过期数据删除&#xff09;1.3.2、compact日志压缩 一、文件清理策略 1.1、文件清理策略的概述 Kafka 中默认的日志保存时间为…...

SRT参数说明

1.超时选项 connect_timeout 连接超时时间&#xff0c;单位毫秒&#xff0c;默认值为3秒。 当RTT > 1500毫秒(2次握手交换)时&#xff0c;SRT无法连接。此选项适用于caller和rendezvous模式。 listen_timeout 监听超时时间&#xff0c;单位毫秒 timeout 为读、写和连接操作…...

vue响应式原理

vue响应式原理 vue响应式原理vue2响应式原理目标对象为数组时 vue3响应式原理Vue3和Vue2在响应式系统方面的对比数据劫持的方式支持数据劫持的数据类型Vue3响应式系统显著优点是&#xff1a; vue响应式原理 无论vue2和vue3响应式都是通过观察者模式&#xff08;发布订阅模式&a…...

elk安装篇之 Kibana安装

Kibana是一个开源的分析与可视化平台&#xff0c;设计出来用于和Elasticsearch一起使用的。你可以用kibana搜索、查看存放在Elasticsearch中的数据。是es的可视化客户端之一。 一&#xff1a;下载 https://www.elastic.co/cn/kibana 我的es是elasticsearch-7.10.2版本&#x…...

MySQL 用户授权管理及白名单

1.创建用户 在 MySQL 中&#xff0c;你可以通过以下步骤创建用户并设置白名单&#xff1a; 使用管理员账号连接到 MySQL 服务器。 创建新用户&#xff1a; CREATE USER usernamehostname IDENTIFIED BY password;其中&#xff0c; username 是你要创建的用户名&#xff1b;ho…...