如何使用管道是进程间通信的关键
博主先声明一下,关于处理进程创建以及销毁的方法。
“子进程究竟何时终止????调用waitpid函数后还要无休止的等待子进程终止吗???”,这显然会是一个问题。因为父进程往往与子进程一样繁忙,因此我们不能只调用waitpid函数来等待子进程终止。那么我们应该怎么办呢???
这就要涉及到我们后面要了解的内容了,信号的概念,我们可以制作信号捕捉器,来完美解决这个问题,由于信号的知识点较多也比较复杂所以博主将往后推移了。
那我们就开始吧!!
如何理解进程间通信
进程 通信意味着两个不同进程间可以交换数据,为了完成这一点,操作系统中应提供两个进程可以同时访问的内存空间(我们也叫缓冲区。)
我们先看一下比较专业的解答吧。
Linux环境下,进程地址空间相互独立,每个进程各自有不同的用户地址空间。任何一个进程的全局变量在另一个进程中都看不到,所以进程和进程之间不能相互访问,要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信。
其实就是进程一旦创建完成它就拥有完全独立的内存结构(0到4G的空间pcb控制块等等),就连通过 fork()创建的子进程也不会与父进程共享内存空间。因此我们就必须想想其他的办法完成进程间的通信了。只要有两个进程由可以同时访问的内存空间,那么进程间就可以通过此空间进行数据交换了。----------->这就是进程间通信。
那么如何实现我们上面所说的呢??这似乎也是一件麻烦事啊????
博主在这里唠叨一句下面的方法都能实现进程间通信,但是展开说实在太费篇幅,博主就简单提一提,如果有感兴趣的朋友可以去查一下相关的资料。
在进程间完成数据传递需要借助操作系统提供特殊的方法,如:文件、管道、信号、共享内存、消息队列、套接字、命名管道等。随着计算机的蓬勃发展,一些方法由于自身设计缺陷被淘汰或者弃用。现今常用的进程间通信方式有:
① 管道 (使用最简单)
② 信号 (开销最小)
③ 共享映射区 (无血缘关系)
④ 本地套接字 (最稳定)
管道是什么?
管道是什么?
既然我们要用到管道的话,那我们就先来聊一聊什么是管道吧。
IPC:下面给出百度百科的解释
管道是一种最基本的IPC机制,作用于有血缘关系的进程之间,完成数据传递。调用pipe系统函数即可创建一个管道。有如下特质:
1. 其本质是一个伪文件(实为内核缓冲区)
2. 由两个文件描述符引用,一个表示读端,一个表示写端。
3. 规定数据从管道的写端流入管道,从读端流出。
管道的原理: 管道实为内核使用环形队列机制,借助内核缓冲区(4k)实现。
管道的局限性:
① 数据自己读不能自己写。
② 数据一旦被读走,便不在管道中存在,不可反复读取。
③ 由于管道采用半双工通信方式。因此,数据只能在一个方向上流动。(这就是半双工通信。)
④ 只能在有公共祖先的进程间使用管道。
(希望大家能记住上面的,还是比较重要的。)
给出管道的模型:
通过上图我们可以看到,为了完成进程间通信(因为进程间的通信不能直接完成,所以我们不得不使用管道),我们需要创建管道。管道不属于进程的资源。而是和套接字一样,属于操作系统(也就不是fork()锁复制的对象)。所以,两个进程之间可以通过操作系统提供的内存空间进行通信。
下面给出创建管道的函数:
#include<unistd.h> int pipe(int pipefd[2]); -->成功:返回 0 失败:返回 -1
函数调用成功返回read()/write()两个文件描述符。无需open(),但需手动close()。
规定:fd[0] → read(); fd[1] → write(),就像0对应标准输入,1对应标准输出一样。向管道文件读写数据其实是在读写内核缓冲区
其实当你要使用管道时,博主在这里可以给你一些使用的小方法来帮助你去理解一些代码.
#include<stdio.h> #include<unistd.h> #include<stdlib.h> #define BUF_SIZE 30 int main(void) { int fds[2];//因为你要调用 int pipe(int pipefd[2]) 这个函数所以你需要创建一个 int型的数组 char str[] = "Who are you?"; //这个是你要写出去的字符串 char buf[BUF_SIZE]; //用来接收的数组 pid_t pid;//用来接收 fork()的返回值,来判断 父子进程 pipe(fds); //创建管道 pid = fork(); //创建进程 if (pid == 0) { //子进程 write(fds[1], str, sizeof(str)); //将字符串 "Who are you?"写出去 } else { //父进程 read(fds[0], buf, BUF_SIZE)); //将独到的字符串写到 buf数组里面 该数组大小为 BUF_SIZE puts(buf); //输出 } return 0; }
大家对于管道的理解到这里就行了,鉴于书中还有单管道同时进行读写,这是不推荐使用的。
不推荐这种
博主推荐的是下面这种方式使用管道
为了完成这个模式我们需要使用以下代码:
有了博主上面的讲解大奖看懂这个代码应该是很轻松地了。