深入理解PIPE

来源:互联网 发布:智能排班软件 编辑:程序博客网 时间:2024/06/05 15:52

原文: http://blog.ddup.us/?p=285


在linux中要进行进程间通信有多种方法:pipe、fifo、共享内存,信号量,消息队列,共享文件等等。其中pipe和fifo使用最广泛,二者的区别为pipe为匿名管道,只能用在有父子关系的进程间通信,而fifo可以通过文件系统中的一个文件取得,所以不受上述限制。作为父子进程间通信的通道,pipe同样可以看作是一个先进先出的队列。

PIPE基本用法

pipe的使用很简单,原型如下:

#include <unistd.h> int pipe(int pipefd[2]);

典型的使用方法如下:

#include <stdlib.h>#include <stdio.h>#include <unistd.h> int main(){    int pfd[2];    int ret = pipe(pfd);    if (ret != 0) {        perror("pipe");        exit(-1);    }     pid_t pid = fork();    if (pid == 0) {        close(pfd[1]);        char rdbuf[1024];        read(pfd[0], rdbuf, sizeof(rdbuf));        return 0;    } else if (pid > 0) {        close(pfd[0]);        char wrbuf[] = "abcd";        write(pfd[1], wrbuf, sizeof(wrbuf));        pid_t cpid = waitpid(-1, NULL, 0);    } else {        perror("fork");    }    return 0;}


从代码中可以看出,调用pipe之后,pfd即为一对相关联的管道,其中pfd[0]对应的是数据输入端,pfd[1]对应数据输出端。当父进程想要给子进程发送数据时,直接将数据write到pfd[1]中即可,对应的子进程从pfd[0]中read。

PIPE缓冲区限制

从上边的父子进程间关系可以看到,这是操作系统中典型的1:1生产者消费者模型。管道作为联系生产者与消费者的缓冲区同样会涉及到两个问题:1)缓冲区的大小问题。 2)缓冲区读写的互斥问题。

将上述代码中父进程的write一行去掉,即可测试出在管道为空的情况下,子进程读管道会阻塞(默认是阻塞的,亦可以将其设置为非阻塞,这样返回的将是-1,同时errno设为EAGAIN)。

为了测试缓冲区的大小,再次修改上述代码,使得子进程不再读取数据,同时父进程一直向其中写入数据,代码如下:

pid_t pid = fork();    if (pid == 0) {        close(pfd[1]);        pause();        return 0;    } else if (pid > 0) {        close(pfd[0]);        pause();        char buf[] = "a";        for (int i = 0; ; i++) {            write(pfd[1], buf, sizeof(buf[0]));            printf("write pipe %d, total %d\n", i+1, (i+1)*sizeof(buf[0]));        }        pid_t cpid = waitpid(-1, NULL, 0);    } else {        perror("fork");    }


修改之后的父进程每次向管道中写入一个字符,并将已经写入的字节数打印出来。当程序停住时,在Linux2.6.27上显示的结果为:

write pipe 65536, total 65536

由此可以看出,此时pipe的缓冲区大小为64KB。相同的程序在MacOSX中显示为:

write pipe 16384, total 16384

可以看出MacOSX的pipe默认缓冲区大小16KB。

已知了pipe默认缓冲区的大小了,那么自然就会想这个缓冲区大小是不是可以人工设定呢?搜索一番之后,发现好多人都说这个值是限定死了的,在内核代码中固定就是64K。后来下了一份linux 3.0代码发现这个默认值已经是可以改的了。相关代码在include/linux/pipe_fs_i.h中:

#define PIPE_DEF_BUFFERS    16……/* for F_SETPIPE_SZ and F_GETPIPE_SZ */long pipe_fcntl(struct file *, unsigned int, unsigned long arg);

看到PIPE_DEF_BUFFERS就是默认的pipe缓冲区大小,不过是以页为单位的,默认的页大小为4K,所以默认的pipe缓冲区大小即为64KB。但是在最下边又有个函数pipe_fcntl,同时有两个常量F_SETPIPE_SZ,F_GETPIPE_SZ。看来应该是用来修改默认缓冲区大小的。

果然这个特性是在2.6.35的内核中加入的。发行说明可以见这里,相应的commit log中看到有相应的Commit。这样就可以通过使用fcntl配合上边两个常量指令更改pipe缓冲区大小。同时在/proc/sys/fs/pipe-max-size中可以方便查看pipe缓冲区最大值。

至此已经知道了linux默认的pipe缓冲区为64KB,同时在2.6.35之后的内核可以使用fcntl更改缓冲区大小。还剩另一个问题,就是原子读写问题。

PIPE的原子访问

同样在include/linux/pipe_fs_i.h中,有如下代码:

/* Differs from PIPE_BUF in that PIPE_SIZE is the length of the actual   memory allocation, whereas PIPE_BUF makes atomicity guarantees.  */#define PIPE_SIZE       PAGE_SIZE

看到定义了PIPE_SIZE这个常量,大小为一个页面大小(默认是4K)。从注释中可以看到这个PIPE_SIZE值即为PIPE最大可保证的原子操作字节数。同样在Write Man Page中写到:

Write requests of {PIPE_BUF} bytes or less shall not be interleaved with data from other processes doing writes on the same pipe. Writes of greater than {PIPE_BUF} bytes may have data interleaved, on arbitrary boundaries, with writes by other processes, whether or not the O_NONBLOCK flag of the file status flags is set.

这个PIPE_BUF常量在/usr/include/linux/limits.h中定义:

#define PIPE_BUF        4096    /* # bytes in atomic write to a pipe */

另外也可以通过ulimit -p查看这个限制,不过这是个只读值,不能修改的。ulimit -a输出如下,其中的pipe size 即是:

core file size          (blocks, -c) 0data seg size           (kbytes, -d) unlimitedscheduling priority             (-e) 0file size               (blocks, -f) unlimitedpending signals                 (-i) 81920max locked memory       (kbytes, -l) 32max memory size         (kbytes, -m) unlimitedopen files                      (-n) 1024pipe size            (512 bytes, -p) 8POSIX message queues     (bytes, -q) 819200real-time priority              (-r) 0stack size              (kbytes, -s) 10240cpu time               (seconds, -t) unlimitedmax user processes              (-u) 1024virtual memory          (kbytes, -v) unlimitedfile locks                      (-x) unlimited

参考

http://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html
http://stackoverflow.com/questions/4624071/pipe-buffer-size-is-4k-or-64k
http://home.gna.org/pysfst/tests/pipe-limit.html
http://unix.stackexchange.com/questions/11946/how-big-is-the-pipe-buffer

http://stackoverflow.com/questions/4739348/is-it-possible-to-change-the-size-of-a-named-pipe-on-linux


0 0
原创粉丝点击