linux中的PIPE_SIZE与PIPE_BUF,管道最大写入值问题

现在开发的项目是从solaris到linux的应用移植。经常用到popen函数,使用8192字节的数组读取popen输出,但没有进行溢出判断。

刚开始认为是一个简单的内存越界,但对popen和PIPE调查以后,疑惑越来越多了。

1)问题的引出

popen使用管道来记录被调用命令的输出,那么popen的最大写入字节数必然是管道的最大值。

使用linux的ulimit -a来查看系统限制:

[[email protected] linux]$ ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 16204
max locked memory       (kbytes, -l) 64
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) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 1024
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

查看solaris的系统限制:

bash-3.00$ ulimit -a
core file size        (blocks, -c) unlimited
data seg size         (kbytes, -d) unlimited
file size             (blocks, -f) unlimited
open files                    (-n) 256
pipe size          (512 bytes, -p) 10
stack size            (kbytes, -s) 8192
cpu time             (seconds, -t) unlimited
max user processes            (-u) 29995
virtual memory        (kbytes, -v) unlimited

可以看到在linux系统上pipe size 为512bytes * 8= 4096bytes。solaris系统上pipe size为512bytes * 10= 5120bytes。

无论是4096还是5120都是远远小于8912的。因此使用8912字节的buf来读取popen的输出时绝对不会出现内存越界问题了。

2)问题的深入

通过ulimit看似得到了正确的结果,但在实际测试中却让人大跌眼镜!

测试程序:

test_popen.c

#include<stdio.h>
int main()
{
    FILE        *fp;
    int i;
    char        *p;
    char        buf[128];
    fp = popen("./test_print", "r");
    if(fp ==NULL) {
        printf("NG\n");
        return -1;
    }
    fgets(buf, 128, fp);
    pclose(fp);
    return 0;
}

test_print.c

#include <stdio.h>
int main()
{
    unsigned int i;
    for(i=0; i<0xffffffff;i++)
        printf("a");
    return 0;
}

将test_popen.c 和test_print.c分别编译为test_popen和test_print,运行test_popen,程序竟然正常运行!test_print明明输出了4G的字符啊

3)探究原理。

通过man 7 pipe来理解PIPE(我的man版本是1.6f)

 PIPE_BUF
       POSIX.1-2001 says that write(2)s of less than PIPE_BUF  bytes  must  be
       atomic:  the  output  data  is  written  to  the  pipe  as a contiguous
       sequence.  Writes of more than PIPE_BUF bytes may  be  non-atomic:  the
       kernel  may  interleave  the data with data written by other processes.
       POSIX.1-2001 requires PIPE_BUF to be at least 512  bytes.   (On  Linux,
       PIPE_BUF  is  4096 bytes.)  The precise semantics depend on whether the
       file descriptor is non-blocking (O_NONBLOCK), whether there are  multi-
       ple writers to the pipe, and on n, the number of bytes to be written:

PIPE_BUF确实为4096,但PIPE_BUF是指原子写的最大值,4kb刚好是1page size,并不是指PIPE SIZE

在谷歌上搜索内核源码,在3.10的内核中有如下:

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

明确说明了PIPE_BUF和PIPE_SIZE的不同

进一步调查,发现在2.6.11之前,PIPE_SIZE为4k,之后内核中PIPE_SIZE为64k,那为何能写入多达4G的字符呢?

这时我想到了popen的源码,查看了popen在BSD的实现,除了使用pipe函数外,与system调用并无区别。

点我查看popen源代码

4)结论

依赖linux的pipe特性的程序并不是好的设计,非常容易出乱子(目前还未发生),最好还是老老实实地做内存越界判断,降低与系统的耦合性。

时间: 2024-12-28 21:15:03

linux中的PIPE_SIZE与PIPE_BUF,管道最大写入值问题的相关文章

Linux中的变量及IO管道使用

变量 io重定向 管道pipe var-变量数学的方程式:设x=2,求y=x^2+2x+1 ?,x和y我们称为未知数,在shell脚本中称为变量,变量用来存储数据,指向内存空间的某个地址,当脚本中重复执行一长串相同代码时,可以使用变量替换,简化代码量,方便开发. 变量赋值var_name=value 变量名命名规则:1.变量名只能包含数字.字母和下划线,而且不能以数字开头:2.变量名最好见名知义,命名机制遵循某种法则:不能够使用程序的保留字,例如if, else, for, while等等,不同

Linux中可用于管道操作的命令总结

在Linux中药进行稍复杂的操作,通常需要借助管道命令"|"多个命令的组合,形式如下: command 1 |  command 2 |  command 3 -- 在linux中有些命令可以直接用于管道命令中,有些则不可以,对于不可以直接用于管道的命令需要借助xargs命令: find ./ -type f | xargs md5sum >> md5_rc1.txt find ./ -type f  -print| xargs md5sum >> md5_rc

Linux中重定向、管道和grep命令总结

今天我们来讲下Linux中的重定向.管道和grep命令.由于重定向和管道知识点比较少,但是又比较重要所以和grep命令一起讲. 在将重定向我们先讲下系统的标准输入和输出.在Linux中标准输入(STDIN 0)对应设备:键盘:标准输出(STDOUT 1)和标准错误输出(STDERR 2)对应设备:显示器.重定向就是将标准输入输出重新定位到指定位置或者设备中(比如:文件). I/O重定向 set –C 禁止对已存在的文件进行覆盖重定向 强制覆盖>| set +C 关闭上述功能 输出重定向: >:

Linux中的管道与重定向

前言: 这篇博文主要参考了鸟哥的<鸟哥的Linux私房菜>和杨鹏(NetSnake)的<Linux服务器架设>这两本书,以及在网络上查阅的资料.此博文作为我阅读<鸟哥的Linux私房菜>和<Linux服务器架设>这本书的读书笔记.那么接下来就让我们依次了解它们. 概述: 熟悉操作系统的朋友,常常会听到.用到管道和重定向这两个词,那么究竟什么是管道,什么是重定向?他们对我有用吗?可以说,管道和重定向是操作系统的精髓之一,在操作系统的进程.程序和管理等各方面,都

Linux中IO重定向和管道

IO重定向和管道 根据冯诺依曼原理的知识,计算机运行有数据流的输入和输出,称之为IO. Linux中一切皆文件思想,表现为具体的文件. 在linux中打开的文件都有一个fd(File Descriptor):文件描述符 程序:指令+数据 读入数据:Input 输出数据:Output Linux给程序提供三种I/O设备: 1. 标准输入(STDIN): -0 默认为接受键盘输入2. 标准输出(STDOUT):-1 默认为输出到终端窗口3. 标准错误(STDERR):-2 默认为输出到终端窗口注:标

linux中的命名管道(FIFO)

管道只能用于亲缘关系之间的通信,而FIFO中,只要可以访问路径,就可以进行通信. FIFO按照先进先出的原则进行通信,第一个被写入的数据首先从管道中读出. 创建命名管道的系统函数有两个: mknod和mkfifo.两个函数均定义在头文件sys/stat.h,函数原型如下: #include <sys/types.h>#include <sys/stat.h>int mknod(const char *path,mode_t mod,dev_t dev);int mkfifo(con

linux中的signal机制(转)

信号是Linux编程中非常重要的部分,本文将详细介绍信号机制的基本概念.Linux对信号机制的大致实现方法.如何使用信号,以及有关信号的几个系统调用. 信号机制是进程之间相互传递消息的一种方法,信号全称为软中断信号,也有人称作软中断.从它的命名可以看出,它的实质和使用很象中断.所以,信号可以说是进程控制的一部分. 一.信号的基本概念 本节先介绍信号的一些基本概念,然后给出一些基本的信号类型和信号对应的事件.基本概念对于理解和使用信号,对于理解信号机制都特别重要.下面就来看看什么是信号. 1.基本

Linux中find常见用法示例

·find   path   -option   [   -print ]   [ -exec   -ok   command ]   {} \; find命令的参数: pathname: find命令所查找的目录路径.例如用.来表示当前目录,用/来表示系统根目录.-print: find命令将匹配的文件输出到标准输出.-exec: find命令对匹配的文件执行该参数所给出的shell命令.相应命令的形式为'command' { } \;,注意{ }和\:之间的空格.-ok: 和-exec的作用

linux中用户和组的管理

一.用户的帐号管理   ①添加帐号:useradd 选项: -c, --comment 'COMMENT':在创建用户时为用户添加注释信息,一般为全名. -d, --home/PATH/TO/HOME_DIR:在创建用户的时候为用户指定家目录的绝对路径,被指定的目录应该是事先不存在的目录: -g, --gid GROUPNAME:在创建用户时,为用户指定主组: -G, --groups GROUP1[,GROUP2,...[,GROUPN]]]:在创建用户时,为用户添加附加组: -m, --cr