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