进程间通信——管道通信

news2024/12/23 11:06:04

目录

 1 管道概念

 2 无名管道(pipe)只能给有亲缘关系进程通信

步骤

注意事项 

3 有名管道(fifo) 可以给任意单机进程通信

步骤

注意事项


 

1 管道概念

管道是UNIX 系统IPC 的最古老形式, 并且所有UNIX 系统都提供此种通信机制。管道有下
面两种局限性:
一、 历史上, 它们是半双工的( 即数据只能在一个方向上流动) 。现在, 某些系统提供全双工管道, 但是为了最佳的可移植性, 我们决不应预先假定系统使用此特性。
二、它们只能在具有公共祖先的进程之间使用。通常, 一个管道由一个进程创建, 然后该
进程调用fork, 此后父、子进程之间就可应用该管道。。

尽管有这两种局限性, 半双工管道仍是最常用的IPC 形式.。

管道的特性:
    1、管道是 半双工的工作模式
    2、所有的管道都是特殊的文件不支持定位操作。
    3、管道是特殊文件,读写使用文件IO。(open,read,write,close)

 2 无名管道(pipe)只能给有亲缘关系进程通信

无名管道: 大小 64k
函数接口
int pipe(int pipefd[2]);
功能
创建一个用来通信的无名管道(在内核中)
参数
pipefd:存放文件描述符数组空间首地址
pipefd[0]:读管道文件描述符
pipefd[1]:写管道文件描述符
返回值
成功返回0 
失败返回-1 

步骤

创建管道 == 》 读写管道 ==》关闭管道 

注意事项 

管道中至少有一个写端:
1.如果管道中有数据,直接读取 
2.如果管道中没有数据,阻塞等待,直到有数据写入,再读取数据
管道中没有写端:
1.如果管道中有数据,直接读取
2.如果管道中没有数据,不阻塞等待,直接返回
管道中至少有一个读端:
1.向管道中写入数据,如果没有写满,则直接写入
2.如果写满(64k),阻塞等待数据读出,才能继续写入
管道中没有读端:
1.向管道中写入数据会产生管道破裂的错误

 下面我通过一个代码示例来分析这个无名管道

                父子进程利用无名管道对文件进行传输,这里我对一个图片src.jpg利用管道传输生成拷贝出一个dst.jpg

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <semaphore.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>


int main(int argc, char const *argv[])
{
     pid_t pid;
     int pipefd[2],ret;
     ret = pipe(pipefd);
     if(ret < 0)
     {
          perror("fail to pipe");
          return -1;
     }
     pid = fork();

     if(pid > 0)//父进程写
     {
          close(pipefd[0]);
          int fd;
          ssize_t nret;
          fd = open("./src.jpg",O_RDONLY);
          char tmpbuf[1024] = {0};
          if(-1 == fd)
          {
               perror("fail open");
               return -1;
          } 
          while (1)
          {
               nret = read(fd,tmpbuf,sizeof(tmpbuf));
               if(0 >= nret)break;
               write(pipefd[1],tmpbuf,nret);
          }
          
          close(pipefd[1]);
          close(fd);
          wait(NULL); 
     }
     else if(0 == pid)//子进程读
     {
          close(pipefd[1]);
          int fd;
          ssize_t nret;
          fd = open("./dst.jpg",O_WRONLY | O_CREAT | O_TRUNC,0664);
          if(-1 == fd)
          {
               perror("fail open");
               return -1;
          }
          char tmp[1024] = {0};
          
          while (1)
          {
               nret = read(pipefd[0],tmp,sizeof(tmp));
               if(0 >= nret)break;
               write(fd,tmp,nret);
          }
          
          close(pipefd[0]);
          close(fd);
          exit(0);
     }
     else
     {    
          perror("fail to fork");
     }
     return 0;
}

可以看出这个和文件io的操作十分像,只是利用了管道的功能对两个父子进程进行了进程间的通信

代码运行后我们可以看一看src和dst的大小一模一样都为706103

 毋庸置疑而且图片也一样

 

 

 

3 有名管道(fifo) 可以给任意单机进程通信

函数接口:
int mkfifo(const char *pathname, mode_t mode);
功能
创建一个有名管道
参数
pathname:有名管道的路径
mode:有名管道的权限
返回值
成功返回0 
失败返回-1 

步骤

1、创建:mkfifo()

2、打开有名管道 open()

3、管道的读写: 文件IO

    读: read(fd-read,buff,sizeof(buff));
    写: write(fd-write,buff,sizeof(buff));

4、关闭管道:close(fd);

5、卸载管道:remove();

注意事项

有名管道必须读写两端同时加入后才能继续向下执行,否则以只读或只写方式
打开,会发生阻塞(等待另一端的加入) 。

以O_RDONLR打开时 ,管道在open处阻塞
以O_WRONLY打开时 ,管道在open处阻塞

当两端同时打开时,才解除阻塞。

然而我用两个不同的.c文件来利用有名管道,进行终端的聊天(两个c文件,A_B.c由进程知识书写,B_A.c由线程知识写的) .

A_B.c

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <semaphore.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>

int main(int argc, char const *argv[])
{
     pid_t pid;
     int ret1 = mkfifo("./A_B", 0664); // 创建有名管道
     int ret2 = mkfifo("./B_A", 0664); // 创建有名管道
     if ((-1 == ret1 || -1 == ret2) && errno != EEXIST)
     {
          perror("fail to mkfifo");
          return -2;
     }
     pid = fork();
     if (pid > 0) // 父进程a->b(A发送)
     {
          char tmpbuf[1024] = {0};
          int fa_b = open("./A_B", O_WRONLY); // 打开管道文件(写)
          while (1)
          {
               fgets(tmpbuf, sizeof(tmpbuf), stdin);
               if (!strcmp("quit\n", tmpbuf))break;
               write(fa_b, tmpbuf, strlen(tmpbuf) + 1);
          }
          close(fa_b);
          remove("A_B");
          return 0;
     }
     else if (0 == pid) // 子进程b->a(A接收)
     {
          char tmpbuf[1024] = {0};
          int fb_a = open("./B_A", O_RDONLY); // 打开管道文件(读)
          while (1)
          {
               int nret = read(fb_a, tmpbuf, sizeof(tmpbuf));
               if (!strcmp("quit\n", tmpbuf) || nret <= 0)break;
               printf("\33[32mB->A\33[0m: %s", tmpbuf);
          }
          close(fb_a);
          remove("./B_A");
          return 0;
     }
     else
     {
          perror("fail to fork");
          return 0;
     }
}

 B_A.c

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <semaphore.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
void *A_B(void *arg) // a->b(B接收)
{
     char tmpbuf[1024] = {0};
     int fa_b = open("./A_B", O_RDONLY); // 打开管道文件(读)
     while (1)
     {
          int nret = read(fa_b, tmpbuf, sizeof(tmpbuf));
          if (!strcmp("quit\n", tmpbuf))break;
          printf("\33[33mA->B\33[0m: %s", tmpbuf);
     }
     close(fa_b);
     remove("./A_B");
     exit(0);
}
void *B_A(void *arg) // b->a(B发送)
{
     char tmpbuf[1024] = {0};
     int fb_a = open("./B_A", O_WRONLY); // 打开管道文件(写)
     while (1)
     {
          fgets(tmpbuf, sizeof(tmpbuf), stdin);
          if (!strcmp("quit\n", tmpbuf))break;
          write(fb_a, tmpbuf, strlen(tmpbuf) + 1);
     }
     close(fb_a);
     remove("B_A");
     exit(0);
}
int main(int argc, char const *argv[])
{
     pthread_t tid1, tid2;
     int ret1 = mkfifo("./A_B", 0664); // 创建有名管道
     int ret2 = mkfifo("./B_A", 0664); // 创建有名管道
     if ((-1 == ret1 || -1 == ret2) && errno != EEXIST)
     {
          perror("fail to mkfifo");
          return -2;
     }
     pthread_create(&tid1, NULL, A_B, NULL);
     pthread_create(&tid2, NULL, B_A, NULL);

     pthread_join(tid1, NULL);
     pthread_join(tid2, NULL);
     return 0;
}

就这样,当两个代码同时运行,就会创建出两个管道在进行半双工的收发通信,如图(缺少的文字是因为,半角全角中文在终端删除的bug)。

 

 

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

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

相关文章

JavaWeb项目 -- 博客系统

JavaWeb项目 -- 博客系统前言&#xff1a;页面展示一、创建 Maven 项目二、设计数据库三、封装数据库的操作3.1 创建 DBUtil 类3.2 创建 Blog 类3.3 创建 User 类3.4 创建类 BlogDao3.5 创建类 UserDao四、导入准备好的前端代码五、实现博客列表界面5.1 约定好前后端交互接口5.…

excel函数应用:如何用数位函数分段提取身份证信息 上篇

用Excel处理身份证号&#xff0c;在我们日常工作中是相当普遍的&#xff0c;尤其是对于做人事行政工作、财务工作的同学来说&#xff0c;更显得十分重要。那么一个身份证号&#xff0c;能给予我们多少信息量呢&#xff1f;无论我们需要用Excel处理何种数据&#xff0c;首先都应…

python 使用矢量化替换循环

介绍 &#x1f3b5;&#x1f57a;&#x1f5e3;&#x1f3c0; 循环自然而然地出现在我们身边&#xff0c;我们了解几乎所有编程语言中的循环。因此&#xff0c;默认情况下&#xff0c;只要有重复操作&#xff0c;我们就会开始执行循环。但是当我们处理大量迭代&#xff08;数百…

5G NR标准 第14章 调度

第14章 调度 NR 本质上是一个调度系统&#xff0c;这意味着调度器决定何时以及向哪些设备分配时间、频率和空间资源&#xff0c;以及使用什么传输参数&#xff0c;包括数据速率。 调度可以是动态的或半静态的。 动态调度是基本的操作模式&#xff0c;其中调度程序针对每个时间…

【JVM 从入门到精通系列】 JVM 字节码指令篇 之 Class文件结构

一、概述 字节码文件的跨平台性 Java语言&#xff1a;跨平台的语言 当Java源代码成功编译成字节码后&#xff0c;如果想在不同平台上运行&#xff0c;则无需再次编译。这个优势已经不再那么吸引人了&#xff0c;Python、PHP、Perl、Ruby、Lisp等有强大的编译器。跨平台似乎已…

uniprot蛋白序列数据库,蛋白质结构数据库PDB;pymol pse格式

https://www.bilibili.com/video/BV1p34y1D77Z https://www.bilibili.com/video/BV1Xa4y1W7Dx 蛋白质结构数据库PDB 注意点&#xff1a;很多数据含有共晶配体的结构 很多时候&#xff0c;蛋白晶体结构中不只是蛋白&#xff0c;还可能有核酸、多肽、辅酶、小分子化合物&#…

振动力学——2.单自由度系统无阻尼自由振动能量法

对于不计阻尼即认为没有能量损失的单自由度系统&#xff0c;可利用能量守恒原理建立自由振动微分方程&#xff0c;或直接求出固有频率无阻尼系统为保守系统&#xff0c;其机械能守恒&#xff0c;即动能T和势V之和保持不变 &#xff0c;即&#xff1a; 或 (1-9) 图1-7弹簧质量…

Clickhouse 三节点三分片六实例双副本部署,用户密码权限配置,cpu内存资源优化

文章目录1. rpm安装ck2. 集群规划3. config.xml文件配置&#xff08;1&#xff09;分片副本信息配置&#xff08;2&#xff09;zookeeper信息配置&#xff08;3&#xff09;macros 信息配置&#xff08;4&#xff09;注释掉映射信息&#xff08;5&#xff09;修改实例中的日志路…

深入理解MySQL——master thread分析

1. master thread的线程分析 master thread的线程优先级别最高。其内部由几个循环&#xff08;loop&#xff09;组成&#xff1a;主循环&#xff08;loop&#xff09;、后台循环&#xff08;background loop&#xff09;、刷新循环&#xff08;flush loop&#xff09;、暂停循…

基于springcloud的学习笔记1

概述springcloud的微服务分布式架构对于springboot的服务集成开发最大的优点就是解决了&#xff0c;springboot中模块之间的高耦合度&#xff0c;springcloud进行高粒度的拆分服务之后就可以降低在高并发下会出现的所有模块服务不可用。同理springcloud就是拆分出不同的模块成为…

Window 环境 安装 mycli

Window 环境 安装 Mycli 平时都用 图形化界面操作MySQL 如 navicat, workbench. 为了更专业一点也锻炼一下动手能力&#xff0c;现在打算换成命令行的方式操作。了解到 myclli这个工具。方便体验&#xff0c;就先在window环境装一个玩玩。 mycli 是一个 MySQL 命令行客户端工具…

【LeetCode每日一题】——50.Pow(x, n)

文章目录一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【解题思路】七【题目提示】八【时间频度】九【代码实现】十【提交结果】一【题目类别】 数学 二【题目难度】 中等 三【题目编号】 50.Pow(x, n) 四【题目描述】 实现 pow(x,n)pow(x…

Exchange漏洞分析:SSRF RCE

0x00 前言 在今年3月份&#xff0c;微软公布了多个Microsoft Exchange的高危漏洞。ProxyLogon是Exchange历史上最具影响力的漏洞之一&#xff0c;有上千台Exchange服务器被植入了webshell后门。 0x01 漏洞描述 CVE-2021-26855是一个SSRF漏洞&#xff0c;利用该漏洞可以绕过E…

一文搞定Nginx的压缩、黑白名单、防盗链、零拷贝、跨域、双机热备等知识

引言早期的业务都是基于单体节点部署&#xff0c;由于前期访问流量不大&#xff0c;因此单体结构也可满足需求&#xff0c;但随着业务增长&#xff0c;流量也越来越大&#xff0c;那么最终单台服务器受到的访问压力也会逐步增高。时间一长&#xff0c;单台服务器性能无法跟上业…

2020网络安全投融资趋势报告

声明 本文是学习2020网络安全投融资趋势报告. 下载地址 http://github5.com/view/55012而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 物联网安全&#xff1a;5G的商业化推动物联网安全加速落地 在应用安全领域&#xff0c;本文共收录投融资事件13起…

IOT云平台 simple(6)springboot netty实现IOT云平台基本的架构(mqtt、Rabbitmq)

本系列教程包括&#xff1a; IOT云平台 simple&#xff08;0&#xff09;IOT云平台简介 IOT云平台 simple&#xff08;1&#xff09;netty入门 IOT云平台 simple&#xff08;2&#xff09;springboot入门 IOT云平台 simple&#xff08;3&#xff09;springboot netty实现TCP Se…

告别Whitelabel Error Page!

相信在JavaWeb开发中不少小伙伴会遇到这个页面吧&#xff0c;特别是初学者基础不扎实不牢固然后网上说的一大堆莫名其妙的解法&#xff0c;千万不要盲目跟着改&#xff0c;建议多读几篇博客&#xff0c;再根据自己的知识分析一下开发流程。首先status404&#xff0c;肯定是我访…

Unity联网多人游戏技术方案调研

关于联网方案 Listen Server (Host) 和 Relay转发服务器游戏包同时包含客户端和服务端逻辑&#xff0c;联网时一个客户端开主&#xff0c;称为Host&#xff0c;其他客户端连入。局域网和互联网都支持。互联网需要有一个匹配服务器帮助找到不同人建立的主机。如果不使用Relay服…

校招前端二面常考react面试题(边面边更)

高阶组件 高阶函数&#xff1a;如果一个函数接受一个或多个函数作为参数或者返回一个函数就可称之为高阶函数。 高阶组件&#xff1a;如果一个函数 接受一个或多个组件作为参数并且返回一个组件 就可称之为 高阶组件。 react 中的高阶组件 React 中的高阶组件主要有两种形式…

verilog学习笔记- 6)verilog基础知识

目录 Verilog 的逻辑值: Verilog 的标识符&#xff08;类似C中的变量名&#xff09;: 1) 定义: 2) 规范建议: Verilog 的数字进制格式: Verilog 的数据类型: 1) 寄存器类型&#xff1a; 2) 线网类型&#xff1a; 3) 参数类型&#xff1a; Verilog 的运算符&#xff1a…