前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >【Linux】进程间通信——匿名管道

【Linux】进程间通信——匿名管道

作者头像
大耳朵土土垚
发布2024-11-21 12:56:19
发布2024-11-21 12:56:19
11500
代码可运行
举报
文章被收录于专栏:c/c++c/c++
运行总次数:0
代码可运行

1.进程间通信目的

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

💥进程具有独立性,所以不同进程间通信该如何通信呢?   进程间进行通信,首先需要让不同的进程看到同一份资源(某种形式的内存空间),并且因为进程具有独立性的特性,一个进程不能在另一个进程中操作某种资源,所以该资源的提供者不能是进程,只能是操作系统,这也是进程间通信的核心。

  进程间通信有两类通信方案——本地通信和网络通信;本地通信就是指在同一台主机同一个操作系统,不同进程间进行通信;后文讲的都是本地通信,网络通信等后续再学习。

2. 进程间通信分类

Linux进程间通信可以分为以下几种分类:

  1. 匿名管道(Pipe):管道是一种半双工的通信机制,可以在同一个进程中的两个文件描述符之间传递数据。
  2. 命名管道(Named Pipe):命名管道也是一种半双工的通信机制,但是可以在不同进程之间进行通信。
  3. 信号(Signal):信号是一种在进程之间传递简单消息的机制,主要用于进程之间的通知和同步。
  4. 消息队列(Message Queue):消息队列是一种通过消息传递进行通信的机制,可以在不同进程之间传递消息。
  5. 共享内存(Shared Memory):共享内存是一种进程间通信的高效机制,多个进程可以通过映射同一块内存区域来实现数据的共享。
  6. 信号量(Semaphore):信号量是一种用于进程之间同步和互斥的机制,可以用来控制对共享资源的访问。
  7. 套接字(Socket):套接字是一种可以在不同主机之间进行进程间通信的机制,可以实现分布式系统间的通信。
  8. 文件锁(File Lock):文件锁是一种用于进程之间同步和互斥的机制,可以通过锁定文件来控制对共享资源的访问。

需要根据具体的业务需求选择适合的进程间通信方式,每种通信方式都有其特点和适用场景。

3. 管道

  管道是一种最简单的进程间通信方式,可以通过创建一个管道文件来实现两个进程之间的通信。读进程从管道中读取数据,写进程向管道中写入数据。管道只能在具有父子关系的进程之间使用。

  父进程创建子进程后,子进程会拷贝父进程的task_struct,同样父进程在创建子进程之前打开了一个文件,子进程也会拷贝该文件的struct file,此时两个进程就指向同一个操作系统提供给该文件的内核缓冲区,所以就可以通过的该文件的内核缓冲区进行通信了:

  管道分为匿名管道pipe命名管道

4. 匿名管道pipe

  在Linux中,匿名管道(anonymous pipe)是一种常用的进程间通信机制。它的原理与上图类似,是通过pipe系统调用来创建一个管道文件,该文件与上图文件不同,没有inode,也不需要写入磁盘,仅仅用来给父子进程间进行通信,可以说是一次性使用的,没有名字,所以称为匿名管道。

  创建匿名管道的思路如下:

  1. 父进程调用系统调用创建管道:

父进程使用读和写两种方式打开管道文件,虽然是一个管道文件,但是文件描述符有两个并且struct file也有两个,一个指向读,一个指向写,但是都指向同一个文件内核缓冲区

  1. 父进程创建子进程:

子进程拷贝父进程的task_struct,这也就是为什么父进程要先将管道创建出来,再创建子进程并且要将读写文件同时打开,是为了给子进程继承

  1. 如果父进程写入,子进程读取,那么就将父进程读取端关闭,子进程写入端关闭;如果父进程读取,子进程写入,那么就反之:

如果不关文件描述符,会造成文件描述符泄露并且可能会误操作,所以建议关闭

管道的通信是单向的,即父进程或子进程不能对一个管道同时读和写,只能选择一个,父进程读,子进程就写;子进程读,父进程就写。

在Linux系统中,可以使用以下系统调用接口创建匿名管道:

代码语言:javascript
代码运行次数:0
复制
#include <unistd.h>

int pipe(int pipefd[2]);

该函数会创建一个管道,并将相应的读取和写入文件描述符存储在pipefd数组中。pipefd[0]表示读取端文件描述符,pipefd[1]表示写入端文件描述符。

使用示例:

代码语言:javascript
代码运行次数:0
复制
#include <unistd.h>
#include <stdio.h>

int main() {
    int pipefd[2];
    int ret = pipe(pipefd);
    
    if (ret == -1) {
        perror("pipe");
        return 1;
    }
    
    printf("读取端文件描述符:%d\n", pipefd[0]);
    printf("写入端文件描述符:%d\n", pipefd[1]);
    
    return 0;
}

运行以上程序,会输出管道的读取和写入端文件描述符。

需要注意的是,创建管道时需要保证父子进程之间的通信,因为管道只能在具有亲缘关系的进程之间使用。通常情况下,我们会调用pipe之后创建子进程,将管道传递给子进程。

以下是一个通过匿名管道简单实现进程间通信的代码:

代码语言:javascript
代码运行次数:0
复制
#include<iostream>
#include<string>
#include<unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>


//子进程写,父进程读
int main()
{
    //1.创建管道
    int pipefd[2];
    int n = pipe(pipefd);
    if(n == -1)
    {
        std::cerr<<"pipe error"<<std::endl;
        return 1;
    }

    //2.创建子进程
    pid_t id = fork();

    //3.关闭不需要的文件描述符fd
    if(id < 0)
    {
        std::cerr<<"fork error"<<std::endl;
        return 1;
    }
    else if(id == 0)//子进程
    {
        ::close(pipefd[0]);//关闭读端
        //向管道写入数据
        std::string message = "hello, I am child process.";
        ::write(pipefd[1],message.c_str(),message.size());
        ::exit(0);
    }
    else//父进程
    {
        ::close(pipefd[1]);//关闭写端
        char buffer[1024];
        ssize_t count = ::read(pipefd[0],buffer,1024);
        if(count > 0)
        {
            buffer[count] = 0;
            std::cout<<"child->father,message:"<<buffer<<std::endl;
        }

        pid_t rid = waitpid(id,nullptr,0);
        std::cout<<"father wait child success:"<<rid<<std::endl;
    }
    return 0;
}

将该代码编译为可执行文件后运行结果如下:

在进程间通信时,可能会出现以下场景:

  1. 管道为空,read会阻塞
  2. 管道为满,write会阻塞

管道是有上限,在某些系统下它只能写入64kb的内容,所以当管道写满了,write就会阻塞

  1. 管道写端关闭,读端最终会读到0,表示已经读到文件结尾
  2. 管道读端关闭,操作系统会直接杀掉写入的进程

匿名管道特性:

  • 面向字节流
  • 只能通过具有血缘关系比如父子/兄弟的进程使用,常用于父子进程间通信
  • 文件的生命周期随进程,管道也是

当父子进程都关闭了,管道就会被操作系统回收,和文件类似

  • 管道通信是单向的

只能有一方写入一方读取,不能同时读取和写入

  • 管道自带同步互斥等机制,也就是说管道在写的时候不能读取,读的时候不能写入
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-11-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.进程间通信目的
  • 2. 进程间通信分类
  • 3. 管道
  • 4. 匿名管道pipe
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档