linux中的PIPE_SIZE與PIPE_BUF,管道最大寫入值問題

來源:互聯網
上載者:User

標籤:pipe_size   pipe_buf   管道最大值   

現在開發的項目是從solaris到linux的應用移植。經常用到popen函數,使用8192位元組的數組讀取popen輸出,但沒有進行溢出判斷。

剛開始認為是一個簡單的記憶體越界,但對popen和PIPE調查以後,疑惑越來越多了。

1)問題的引出

popen使用管道來記錄被調用命令的輸出,那麼popen的最大寫入位元組數必然是管道的最大值。

使用linux的ulimit -a來查看系統限制:

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

查看solaris的系統限制:

bash-3.00$ ulimit -acore file size        (blocks, -c) unlimiteddata seg size         (kbytes, -d) unlimitedfile size             (blocks, -f) unlimitedopen files                    (-n) 256pipe size          (512 bytes, -p) 10stack size            (kbytes, -s) 8192cpu time             (seconds, -t) unlimitedmax user processes            (-u) 29995virtual 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

在Google上搜尋核心源碼,在3.10的核心中有如下:

133 /* Differs from PIPE_BUF in that PIPE_SIZE is the length of the actual134    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特性的程式並不是好的設計,非常容易出亂子(目前還未發生),最好還是老老實實地做記憶體越界判斷,降低與系統的耦合性。


linux中的PIPE_SIZE與PIPE_BUF,管道最大寫入值問題

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.