一张图带你彻底弄懂linux管道通信-匿名管道

目录

1. 进程间通信介绍

1-1 进程间通信目的

1-2 进程间通信发展

1-3 进程间通信分类

SystemVIPC

POSIXIPC

2. 管道

什么是管道?

3. 匿名管道

3.1站在文件描述符角度-深度理解管道

3.2站在内核角度-管道本质

4匿名管道代码实例

一张图带你彻底弄清楚

5.总结管道通信原理


1. 进程间通信介绍

1-1 进程间通信目的

• 数据传输:⼀个进程需要将它的数据发送给另⼀个进程

• 资源共享:多个进程之间共享同样的资源。

• 通知事件:⼀个进程需要向另⼀个或⼀组进程发送消息,通知它(它们)发⽣了某种事件(如进

程终⽌时要通知⽗进程)。

• 进程控制:有些进程希望完全控制另⼀个进程的执⾏(如Debug进程),此时控制进程希望能够

拦截另⼀个进程的所有陷⼊和异常,并能够及时知道它的状态改变。

1-2 进程间通信发展

• 管道

• SystemV进程间通信

• POSIX进程间通信

1-3 进程间通信分类

管道

• 匿名管道pipe

• 命名管道

SystemVIPC

• SystemV消息队列

• SystemV共享内存

• SystemV信号量

POSIXIPC

• 消息队列

• 共享内存

• 信号量

• 互斥量

• 条件变量

• 读写锁

2. 管道

什么是管道?

管道是Unix中最古⽼的进程间通信的形式。

我们把从⼀个进程连接到另⼀个进程的⼀个数据流称为⼀个“管道”

3. 匿名管道

#include <unistd.h>
功能:
创建⼀⽆名管道
原型
int pipe(int fd[2]);
参数
fd:⽂件描述符数组,其中fd[0]表⽰读端, fd[1]表⽰写端
返回值:0成功返回,失败返回错误代码

3.1站在文件描述符角度-深度理解管道

3.2站在内核角度-管道本质

所以,看待管道,就如同看待文件⼀样!管道的使⽤和⽂件⼀致,迎合了“Linux⼀切皆⽂件思

想”。

4匿名管道代码实例

#include <iostream>
#include <unistd.h>
#include <cstdio>
#include <cstring>
#include <sys/types.h>
#include <sys/wait.h>

ssize_t FatherWrite(int wfd)
{
    char buffer[1024];
    int cnt = 0;
    while (true)
    {
        snprintf(buffer, sizeof(buffer), "I'm Father,cnt=%d\n", cnt++);
        write(wfd, buffer, strlen(buffer));
        sleep(1);
    }
}

ssize_t ChildRead(int rfd)
{
    char buffer[1024];
    while(true)
    {
        buffer[0]=0;
        ssize_t n=read(rfd,buffer,sizeof(buffer)-1);
       if(n>0)
       {
             buffer[n]=0;
             std::cout<<"Child read that Father Say:"<<buffer<<std::endl;
       }
    }
}

int main()
{
    int fds[2] = {0};
    int n = pipe(fds);
    if (n < 0)
    {
        std::cerr << "pipe error" << std::endl;
        exit(1);
    }
    std::cout << "fds[0]:" << fds[0] << std::endl;
    std::cout << "fds[1]:" << fds[1] << std::endl;

    pid_t id = fork();
    if (id == 0) // 子进程
    {
        // c->r  f->w
        close(fds[1]);
        ChildRead(fds[0]);
        close(fds[0]);
        exit(0);
    }
    close(fds[0]);
    FatherWrite(fds[1]);
    close(fds[0]);
    waitpid(id, nullptr, 0);

    return 0;
}

你可能会有疑惑----linux 匿名管道通信中为什么父子进程没发生写时拷贝?

一张图带你彻底弄清楚

首先理解什么是写时拷贝

写时拷贝的核心思想是:

1在资源复制时,不立即执行实际的数据复制

2多个对象共享相同的原始数据

3当任一对象尝试修改数据时

4系统在此时才创建该数据的专用副本

5修改操作在副本上执行,保持原始数据不变

当fork创建新进程时,struct_file是父子共享的文件

当父子进程对该文件进行修改时才会发生写时拷贝(修改用户空间)

所以为什么管道通信不触发写时拷贝?

管道数据在内核空间:当进程通过管道写入数据时,实际上是调用了系统调用(如write(fd[1], buffer, size)),将数据从用户空间的内存复制到内核的管道缓冲区。这个复制过程不涉及修改父子进程共享的用户空间内存,因此不会触发写时拷贝。

结论

在管道通信中,父子进程通过管道传递数据并不会触发写时拷贝,因为传递数据是通过系统调用将数据复制到内核的管道缓冲区,不涉及对共享用户内存页的修改。但是,在fork之后,父子进程各自拥有独立的进程空间,当它们修改自己的用户空间内存(例如,修改变量)时,写时拷贝就会发生。管道通信本身并不依赖共享内存(用户空间),而是通过内核缓冲区进行数据传递。

5.总结管道通信原理

1.管道创建​:当父进程调用pipe()系统调用时,会创建一个管道,这是一个单向通信通道,返回两个文件描述符:fd[0]用于读取,fd[1]用于写入。
2. ​创建子进程​:父进程调用fork()创建子进程。此时,父子进程拥有相同的地址空间(包括文件描述符表),因此它们都拥有指向同一个管道(即同一对文件描述符)的能力。
3. ​关闭未使用的文件描述符​:通常,父进程关闭读端(fd[0]),子进程关闭写端(fd[1]),或者反之,这样形成了单向通信。

评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值