您的位置:首页 > 运维架构 > Linux

linux系统学习之管道

2016-05-15 10:20 344 查看
首先理解管道其实是一个二进制字节流,它是内核为维持两个或多个进程互相通信的一种手段(一种IPC)。如下图所示:

管道是一个字节流,这意味着读可以从管道中读取一部分字节流,然后剩下一部分等待下一次读取,这是允许的。如下测试:

#include<stdio.h>
#include <sys/wait.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#define BUF_SIZ 10
int main(int argc,char *argv[])
{
int field[2];
pipe(field);
char buf[BUF_SIZ];
switch(fork())
{
case -1:
exit(1);
case 0:
close(field[1]);
for(;;)
{
int numread=read(field[0],buf,BUF_SIZ);
if(numread==-1)
exit(1);
if(numread==0)
break;
if(write(1,buf,numread)!=numread)
exit(1);
sleep(3);
}
write(1,"\n",1);
close(field[0]);
default:
close(field[0]);
write(field[1],argv[1],strlen(argv[1]));
close(field[1]);
wait(NULL);
exit(0);
}

}
此时我将每次读取的内容设置为10个字节,一次写入管道中的字节超过10个时,可以在终端看到,10个字节后,一部分内容是在延迟3s才在终端显示的。
值得一提的是,当读取管道时,如果管道中没有数据,那么read会阻塞,直到管道中有新的字节流。如果管道的写入端关闭了,那么在read完管道中剩下的字节流之后,将会看到文件结尾(read会返回0),当write进管道时,一次写入太大的字节流(其实管道可以看做一个内核缓存,缓存的大小是固定的,不同的系统大小不一样,linux是465536),管道容纳不了一次写这么多,那么write也会阻塞。

创建和使用管道:

int pipe(int field[2])

即可创建管道,其中field[0]是管道读取端,field[1]是管道写入端。一般在一个进程中创建管道也没有什么意义。一般管道是用于父进程与其子进程之类的进行通讯。

尽管管道支持多个进程写入和读取,但是一般不那么做。一般设定为父进程写入,子进程读取或者子进程写入,父进程读取,使得它们之间开始通讯。

注意在创建管道过程中,及时关闭不使用的文件描述符是很有必要的。

从管道中读取数据的进程(如上图b中所示的子进程)应该关闭其写入端的文件描述符(图44-3 b)中子进程的field[1].因为如果在别的进程都已经写入数据完成,并且已经关闭了写入文件描述符,按照原本的思想,该次通讯已经完成,此时已经没有数据从父进程传给子进程了。但是由于子进程的写入管道端文件描述符未关闭,让子进程读取端误认为至少还有一个进程没有完成写入,那么子进程的读取端会一直阻塞地read下去。而写入端关闭读取文件操作符是处于不同的原因。当一个进程试图向管道中写入数据但没有任何进程拥有该管道打开的读描述符,内核会向写入进程发送一个SIGPIPE信号。默认下,该信号会杀死一个进程。当然,进程也可以捕捉或者忽略该信号。收到SIGPIPE信号对于表示管道的状态很有用。

关闭未使用文件描述符的最后一个原因是当所有进程的所有引用一个管道的文件描述符被关闭后,才会销毁该管道,释放管道资源。

还有另外一种管道,它叫FIFO管道,但是它在文件系统中有一个名称,打开它和打开一个普通文件是一样的。不同的是管道只能用于有“血缘”关系的进程间通讯,FIFO可以对在任意进程之间通讯(如客户端和服务器)。

文章参考Linux/Unix系统编程手册一书。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: