深入理解PIPE

184 篇文章 4 订阅
86 篇文章 1 订阅

转载: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) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 81920
max locked memory       (kbytes, -l) 32
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 10240
cpu time               (seconds, -t) unlimited
max user processes              (-u) 1024
virtual memory          (kbytes, -v) unlimited
file 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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值