管道(上) 为什么需要管道what is 管道(pipe)几种特殊情况总结
每一个进程在正常情况下都有自己的地址空间(address speace),这样的好处是让进程运行具有独立性,且凡是由进程编写的程序,该进程的稳定性好,(一个进程在自己的地址空间内运行,当他不小心崩溃了他也不会影响到其他进程),但这也是他的缺点,当我们想让一个进程给另一个进程发送数据时,我们就需要进程间通信的知识了,本文就来讲讲进程通信的管道。
进程间通信:让不同进程看到同一份公共资源,这里的公共资源不属于某一个特定的进程,他一般是由独立的第三方提供(内核)。不明白?看看下面的图
进程1将自己的有效数据写入到操作系统的缓冲区里,在通过进程2从系统的缓冲区里读出来。
管道是一种最基本的IPC(InterProcess Communication-进程间通信)机制,由pipe函数创建
#include <unistd.h> int pipe(int pipefd[2]); #define _GNU_SOURCE #include <unistd.h> int pipe2(int pipefd[2], int flags);pipefd[2]:一个具有两个整形元素的一位数组,当调用函数时,该一维数组会退化成一个指向一位数组的指针,是一个输出型参数。为什么会有两个元素呢而且还有关键字fd
当一个进程调用pipe时,它的本质是当前进程分别以读和写打开一个文件(pipe)此时这个文件打开了两次,打开一个文件就会有一个fd(文件描述符),这里分别以读和写打开pipe文件就会得到两个fd,现在明白pipefd[2]了吧。还不明白?好吧!
一个进程调用pipe就会得到一个以读方式一个以写方式打开pipe的文件文件描述符,而pipefd恰好是一个二维数组他恰好可以放两个元素,这个数组希望用0号和1号返回打开的文件,所以调用完pipe我们会得到两个文件描述符,其中0号内容表示以读方式打开,1号表示以写方式打开。
fork()后父子进程发生写时拷贝,都同时以读写方式打开了一个文件,现在终于让两个独立的进程看到一块儿公共的资源。
现在我们如果想让子进程写而让父进程读因该怎么样做? 回答正确:只要让父进程fclose(pipefd[1]),子进程fclose(pipefd[0]),这时我们就建立单向通信
#include <stdio.h>#include <unistd.h>#include <string.h>int main(){ int fds[2]; if (pipe(fds) < 0){//创建管道失败 perror("pipe"); return 1; } pid_t id = fork(); if (id == 0){//child ->write close(fds[0]); const char* msg = "hello father, I am child, hello pipe!"; while (1){ write(fds[1], msg, strlen(msg)); sleep(1); } }else{//father ->read close(fds[1]); char buf[1024]; while(1){ ssize_t s = read(fds[0], buf, sizeof(buf)-1); if (s > 0){ buf[s] = 0; printf("client->father : %s\n",buf); } } } return 0;}
运行结果:你会发现每隔一秒就打印一条”msg”。
管道的五个特点
管道只能进行单向通信。管道只能让有血缘关系的进程实现进程间通信(因为要发生写时拷贝),常用于父子进程,兄弟进程,爷孙进程。管道在读写过程自带同步机制。管道提供面向字节流的通信方式。管道随与之相关进程的退出而随即被释放,管道生命周期岁进程。