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

一张图带你彻底弄懂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

功能:

创建⼀⽆名管道

原型

int pipe(int fd[2]);

参数

fd:⽂件描述符数组,其中fd[0]表⽰读端, fd[1]表⽰写端

返回值:0成功返回,失败返回错误代码

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

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

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

想”。

4匿名管道代码实例

#include

#include

#include

#include

#include

#include

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:"<

}

}

}

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]),或者反之,这样形成了单向通信。

尊享推荐

365娱乐app官方版下载106平台 贴合贴合更贴合,oneup G6游戏鼠标评测
365bet资讯网 关于世界杯的5个小故事

关于世界杯的5个小故事

📅 09-01 👑 587
365bet资讯网 含有两对反义词的成语汇总
365娱乐app官方版下载106平台 电脑怎样剪切图片?剪切图片大小方法大全