1、管道(pipe)特点
a>.通过 pipe 建立通信管道,fork 创建子进程(传递文件描述符),且这种关系只能由父进程建立, 局限于有血缘关系的进程间的通讯 ;
b>.管道是文件,并且只存于内存中,当进程终结时,管道也消失 ;
c>.管道只能是单向通信,一端输入,另一端输出 ;
d>.管道满时,写阻塞。管道空时,读阻塞。
2、创建管道
a>.父进程 pipe 管道,得到文件描述符指向管道;
b>.父进程 fork 子进程,子进程也有了文件描述符指向管道;
c>.关闭多余的文件描述符,完成单向通信。
代码如下:
#include<stdio.h> #include<unistd.h> #include<errno.h> #include<string.h> int main() { int _pipe[2]; int ret = pipe(_pipe); if(ret == -1) { perrno("pipe()"); return -1; } pid_t id = fork(); if(id < 0) {//创建进程失败 perrno("fork()"); return -1; } else if(id == 0) {//子进程 close(_pipe[0]); int i = 0; char *_mesg_c = NULL; while(i++ < 10) { _mesg_c = "i am child"; write(_pipe[1], _mesg_c, strlen(_mesg_c)+1); sleep(2); } close(_pipe[1]); } else {//父进程 close[1]; char _mesg[100]; int j = 0; while(j++ < 10) { memset(_mesg, ‘\0‘, sizeof(_mesg)); read(_pipe[0], _mesg, sizeof(_mesg)); printf("%s\n", _mesg); } close(_pipe[0]); } return 0; }
3、使用管道还要注意以下四种情况(阻塞I/O操作)
a>.指向管道写端的文件描述符关闭了,但还有进程从管道端读数据,那么当管道中数据读取完后,再次 read 会返回;
模拟代码如下:
#include<stdio.h> #include<unistd.h> #include<errno.h> #include<string.h> #include<sys/wait.h> int main() { int _pipe[2]; int ret = pipe(_pipe); if(ret < 0) { perrno("pipe()"); return -1; } pid_t id = fork(); if(fork < 0) { perrno("fork()"); return -1; } if(fork == 0) {//子进程 close(_pipe[0]); char *_mesg_c = NULL; int i = 0; while(i++ < 10) { char *_mesg_c = "i am child"; write(_pipe[1], _mesg_c, strlen(_mesg_c)+1); sleep(2); } close(_pipe[1]); } else {//父进程 close(_pipe[1]); char _mesg[100]; int j = 0; while(j++ < 100) { memset(_mesg, ‘\0‘, sizeof(_mesg)); read(_pipe[0], _mesg, sizeof(_mesg)); printf("%s", _mesg); } if(waitpid(id, NULL, 0) < 0) { return 1; } } return 0; }
b>.指向管道的写端文件描述符没关闭,但是又没有向管道写数据,当管道中的数据读取完成后,再次 read 会阻塞,直到管道中有数据可读了,才读取数据并返回;
模拟代码如下:
#include<stdio.h> #include<unistd.h> #include<errno.h> #include<string.h> #include<sys/wait.h> int main() { int _pipe[2]; int ret = pipe(_pipe); if(ret < 0) { perrno("pipe()"); return -1; } pid_t id = fork(); if(id < 0) { perrno("fork()"); return -1; } else if(id == 0) {//子进程 close(_pipe[0]); char *_mesg_c = NULL; int i = 0; while(i++ < 20) { if(i < 10) { _mesg_c = "i am child"; write(_pipe[1], _mesg_c, strlen(_mesg_c)+1); } sleep(1); } close(_pipe[1]); } else {//父进程 close(_pipe[1]); char _mesg[100]; int j = 0; while(j++ < 20) { memset(_mesg, ‘\0‘, sizeof(_mesg)); read(_pipe[0], _mesg, 0); printf("%s", _mesg); } if(waitpid(id, NULL, 0) < 0) { return 1; } } return 0; }
c>.指向管道的读端文件描述符关闭了,这时还有进程向管道的写端写,那么进程就会收到信号 SIDPIPE ,导致进程异常终止;
模拟代码如下:
#include<stdio.h> #include<unistd.h> #include<errno.h> #include<string.h> #include<sys/wait.h> int main() { int _pipe[2]; int ret = pipe(_pipe); if(ret < 0) { perrno("pipe()"); return -1; } pid_t id = fork(); if(id < 0) { perrno("fork()"); return -1; } else if(id == 0) {//子进程 close(_pipe[0]); char *_mesg_c = NULL; int i = 0; while(i++ < 20) { _mesg_c = "i am child"; write(_pipe[1], _mesg_c, strlen(_mesg_c)+1); sleep(1); } close(_pipe[1]); } else {//父进程 close(_pipe[1]); char _mesg[100]; int j = 0; while(j++ < 10) { memset(_mesg, ‘\0‘, sizeof(_mesg)); read(_pipe[0], _mesg, 0); printf("%s", _mesg); } close(_pipe[0]); if(waitpid(id, NULL, 0) < 0) { return 1; } } return 0; }
d>.指向管道的读端文件描述符没关闭,但是读端的进程也没有读数据,那么有进程向管道写数据,当写满了再次 write 会阻塞,直到管道中有空位置才写入数据并返回。
模拟代码如下:
#include<stdio.h> #include<unistd.h> #include<errno.h> #include<string.h> #include<sys/wait.h> int main() { int _pipe[2]; int ret = pipe(_pipe); if(ret < 0) { perrno("pipe()"); return -1; } pid_t id = fork(); if(id < 0) { perrno("fork()"); return -1; } else if(id == 0) {//子进程 close(_pipe[0]); char *_mesg_c = NULL; int i = 0; while(i++ < 20) { _mesg_c = "i am child"; write(_pipe[1], _mesg_c, strlen(_mesg_c)+1); sleep(1); } close(_pipe[1]); } else {//父进程 close(_pipe[1]); char _mesg[100]; int j = 0; while(j++ < 20) { if(j < 5) { memset(_mesg, ‘\0‘, sizeof(_mesg)); read(_pipe[0], _mesg, 0); printf("%s", _mesg); } sleep(1); } close(_pipe[0]); if(waitpid(id, NULL, 0) < 0) { return 1; } } return 0; }
原文地址:http://green906.blog.51cto.com/10697569/1763093