標籤: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,管道最大寫入值問題