进程间通信---匿名管道

简介: 进程间通信---匿名管道

文章目录


为什么要通信

我们需要不同进程之间相互协同,进程之间的协同本质上是程序员之间的协同,有可能为要从数据库中拿数据,你要从数据库中将数据格式化,写成特定的格式,我要根据特定的格式做数据统计,这一件事情,有一个无法实现这个事情就无法完成,


我们可以将这一件事情拆分成3个事情:一个进程专门从数据库中拿数据,一个进程专门做数据格式化,还有一个进程专门做数据统计

第一个处理完之后交给第二个,第二个处理完之后交给第三个

这样我们就可以实现业务上的通信,这样就增加了代码的可维护性,这样我们就找专门的进程处理特定的问题

如:

管道


通信的宏观理解

进程之间可能会存在特定的协同工作的场景------> 一个进程要把自己的数据交付给了一个进程,让其处理---------->这就叫做进程间通信------> 但是进程具有独立性,交互数据,成本一定很高,

(所以这一定需要操作系统进行协同,参与通信)

因为一个进程是看不到另一个进程的资源

所以两个进程要相互通信------->必须要先看到一份公共的资源(一方往里面放数据,另一方往里面取数据 )(这里的资源,就是一段内存)----> (这段公共进程属于谁,只能属于操作系统,)!!,因为进程具有独立性

这段内存:可能以文件的方式存在,也可能以队列的方式存在,也可能提供的就是原始的内存块

(这也是通信方式有很多的原因)

进程间通信本质上:其实是有OS参与,提供一封所有通信进程都能看到的公共资源,

结论

  1. 我们要想办法让我们看到一份公共的资源,
  2. 通信的时候操作系统的方式不同,所以通信的方式也就不同


进程间通信分类

管道

  • 匿名管道
  • 命名管道

System V IPC

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


管道

匿名管道

父子进程是独立的进程,所以父子通信也属于进程通信的一种



让双方进程看到了同一份进程

接下来我们还想要让他钔通信

我们可以使用系统调用

PIPE(2)                                                                               Linux Programmer's Manual                                                                              PIPE(2)
NAME
       pipe, pipe2 - create pipe
SYNOPSIS
       #include <unistd.h>
       int pipe(int pipefd[2]);
       #define _GNU_SOURCE             /* See feature_test_macros(7) */
       #include <fcntl.h>              /* Obtain O_* constant definitions */
       #include <unistd.h>
       int pipe2(int pipefd[2], int flags);
DESCRIPTION
       pipe()  creates  a pipe, a unidirectional data channel that can be used for interprocess communication.  The array pipefd is used to return two file descriptors referring to the ends of the
       pipe.  pipefd[0] refers to the read end of the pipe.  pipefd[1] refers to the write end of the pipe.  Data written to the write end of the pipe is buffered by the kernel until  it  is  read
       from the read end of the pipe.  For further details, see pipe(7).
       If flags is 0, then pipe2() is the same as pipe().  The following values can be bitwise ORed in flags to obtain different behavior:
       O_NONBLOCK  Set the O_NONBLOCK file status flag on the two new open file descriptions.  Using this flag saves extra calls to fcntl(2) to achieve the same result.
       O_CLOEXEC   Set the close-on-exec (FD_CLOEXEC) flag on the two new file descriptors.  See the description of the same flag in open(2) for reasons why this may be useful.
RETURN VALUE
       On success, zero is returned.  On error, -1 is returned, and errno is set appropriately.
ERRORS
       EFAULT pipefd is not valid.
       EINVAL (pipe2()) Invalid value in flags.
       EMFILE Too many file descriptors are in use by the process.
       ENFILE The system limit on the total number of open files has been reached.
  1. pipefd[2]是一个输出型参数;意味着我们向通过这个参数读取到打开的两个fd,就是把这个值带出去,
  2. pipe(pipefd[2]),数组名就是首元素地址,到时候通过指针把文件描述符拷贝到我们的数组之中,
#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdlib.h>
int main()
{
  int fd[2];
  if(pipe(fd)!=0)
  {
    perror("pipe");
    exit(-1);
  }
  printf("pipe[0]=%d\n",fd[0]);
  printf("pipe[1]=%d\n",fd[1]);
  return 0;
}



我们发现一个是3,一个是4

匿名管道的特点


管道是一个单向的流通信号,

管道是面向字节流的,tcp FILE,fstream,我们读的时候是只有字节的概念,并不是说我想读多少,就读多少

仅限于父子特性

管道回自带同步机制,管道里面没有数据就不会读到管道里面的废弃数据,原子性写入

管道的生命周期是随进程的

我们测试了一下,写段一直不写,读端一直不读


写满64KB 的时候,write就不再写入了,为什么呢?因为管道有大小!当write写满的时候,要让reader来读,同样的读端也要等写端,我们不会出现读取新老数据的覆盖事件,

#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdlib.h>
#include <string.h>
int main()
int main()
{
  int fd[2];
  if(pipe(fd)!=0)//创建管道
  {
    perror("pipe");
    exit(-1);
  }
  //打印文件描述符
  printf("pipe[0]=%d\n",fd[0]);
  printf("pipe[1]=%d\n",fd[1]);
  //实现进程键通信
  pid_t id =fork();
  if(id<0)
  {
    perror("fork");
    exit(1);
  }
  //我们向让父进程执行读取,子进程执行写入
  //0----->就是读取端,1----->就是写入端
  else if(id==0)//创建子进程
  {
    //child
    //关闭读写端,建立一个单向的管道
    //子进程把文件描述符也都给继承下来了
    close(fd[0]);
    const char* msg="hello world";
    int cout=0;
    //我们还可以使用read 和write进行读写操作
    while(1)
    {
    //write(fd[1],msg,strlen(msg));//我们不需要+1,因为\0本身只是c语言的标准,
    write(fd[1],"a",1);//子进程不断往管道里面写入,但是父进程不读
    cout++;
    printf("cout=%d\n",cout);
    //write的动作就是pipe里面只要有缓冲区区域就一直写入
    //当我们一直些的时候,写满64KB的时候,写段就不再写入了,因为管道有大小,
    //当写端写满的时候,为什么不写了,
    //因为要让读端来读,
    }
    exit(0);
    //dup2(fd[1],1);
  }
  else 
  {
    //father
    close(fd[1]);
    //父进程就一直在那里读取
    //while(1)
    //{
    //  char buf[32]={0};
    //  ssize_t ret=read(fd[0],buf,sizeof(buf));//如果此时ret=0说明子进程关闭文件描述符了,相当于读到文件的结尾了
    //  //read是只要有数据就可以一直读取
    //  //这就叫字节流
    //  if(ret==0)
    //    break;
    //  else 
    //  {
    //  buf[ret]=0;//把\0位置就设置为0
    //  printf("child say : %s\n",buf);
    //  //我们没有让父进程去sleep
    //  }
    dup2(fd[0],0);//绑定打印到文件里面
    //}
    sleep(1);
    //dup2(fd[0],0);
  }
  return 0;
}

写段被关闭了,读端继续读,就会都到文件的最后


当我们的读端关闭,写端还在写入,此时站在操作系统的角度,合理吗??

严重不合理,因为已经没有人读了,你还在写入,本质就是在浪费操作系统的资源,所以OS会直接终止写入进程,OS给目标进程发送信号,SIGPIPE


管道的四种情况:


读端不读或者读的满,那么写端要等读端读完

读端关闭,没人读了,写端就没有了存在的必要,写端直接受到SIGPIPE 的信号,直接终止

写端不写或者写的慢,读端就要等写端

写端关闭,读端就读完全部pipe内部的数据,然后再读就会读到0,表面文件的结尾!

管道实在具有血缘关系的进程之间可以进行进程间通信,但是常用于父子间通信的


管道是文件吗?

如果一个文件只能被当前进程打开,相关进程退出了,被打开的文件呢?会被OS自动关闭,


可以看我的github仓库详细情况

匿名管道代码详细


相关文章
|
24天前
|
存储 Unix Linux
进程间通信方式-----管道通信
【10月更文挑战第29天】管道通信是一种重要的进程间通信机制,它为进程间的数据传输和同步提供了一种简单有效的方法。通过合理地使用管道通信,可以实现不同进程之间的协作,提高系统的整体性能和效率。
|
7月前
|
存储 负载均衡 Linux
【Linux 系统】进程间通信(匿名管道 & 命名管道)-- 详解(下)
【Linux 系统】进程间通信(匿名管道 & 命名管道)-- 详解(下)
|
7月前
|
消息中间件 Unix Linux
【Linux 系统】进程间通信(匿名管道 & 命名管道)-- 详解(上)
【Linux 系统】进程间通信(匿名管道 & 命名管道)-- 详解(上)
|
3月前
|
消息中间件 Unix Linux
C语言 多进程编程(二)管道
本文详细介绍了Linux下的进程间通信(IPC),重点讨论了管道通信机制。首先,文章概述了进程间通信的基本概念及重要性,并列举了几种常见的IPC方式。接着深入探讨了管道通信,包括无名管道(匿名管道)和有名管道(命名管道)。无名管道主要用于父子进程间的单向通信,有名管道则可用于任意进程间的通信。文中提供了丰富的示例代码,展示了如何使用`pipe()`和`mkfifo()`函数创建管道,并通过实例演示了如何利用管道进行进程间的消息传递。此外,还分析了管道的特点、优缺点以及如何通过`errno`判断管道是否存在,帮助读者更好地理解和应用管道通信技术。
|
3月前
|
SQL 网络协议 数据库连接
已解决:连接SqlServer出现 provider: Shared Memory Provider, error: 0 - 管道的另一端上无任何进程【C#连接SqlServer踩坑记录】
本文介绍了解决连接SqlServer时出现“provider: Shared Memory Provider, error: 0 - 管道的另一端上无任何进程”错误的步骤,包括更改服务器验证模式、修改sa用户设置、启用TCP/IP协议,以及检查数据库连接语句中的实例名是否正确。此外,还解释了实例名mssqlserver和sqlserver之间的区别,包括它们在默认设置、功能和用途上的差异。
|
4月前
|
消息中间件 Linux 开发者
Linux进程间通信秘籍:管道、消息队列、信号量,一文让你彻底解锁!
【8月更文挑战第25天】本文概述了Linux系统中常用的五种进程间通信(IPC)模式:管道、消息队列、信号量、共享内存与套接字。通过示例代码展示了每种模式的应用场景。了解这些IPC机制及其特点有助于开发者根据具体需求选择合适的通信方式,促进多进程间的高效协作。
175 3
|
4月前
|
开发者 API Windows
从怀旧到革新:看WinForms如何在保持向后兼容性的前提下,借助.NET新平台的力量实现自我进化与应用现代化,让经典桌面应用焕发第二春——我们的WinForms应用转型之路深度剖析
【8月更文挑战第31天】在Windows桌面应用开发中,Windows Forms(WinForms)依然是许多开发者的首选。尽管.NET Framework已演进至.NET 5 及更高版本,WinForms 仍作为核心组件保留,支持现有代码库的同时引入新特性。开发者可将项目迁移至.NET Core,享受性能提升和跨平台能力。迁移时需注意API变更,确保应用平稳过渡。通过自定义样式或第三方控件库,还可增强视觉效果。结合.NET新功能,WinForms 应用不仅能延续既有投资,还能焕发新生。 示例代码展示了如何在.NET Core中创建包含按钮和标签的基本窗口,实现简单的用户交互。
72 0
|
4月前
|
Linux C语言
【C语言】进程间通信之命名管道fifo
【C语言】进程间通信之命名管道fifo
46 0
|
4月前
|
C语言
【C语言】进程间通信之管道pipe
【C语言】进程间通信之管道pipe
93 0
|
4月前
|
Python
Python IPC深度探索:解锁跨进程通信的无限可能,以管道与队列为翼,让你的应用跨越边界,无缝协作,震撼登场
【8月更文挑战第3天】Python IPC大揭秘:解锁进程间通信新姿势,让你的应用无界连接
27 0