进程返回linux系统编程之管道(二):管道读写规则和Pipe Capacity、PIPE_BUF
題記:寫這篇博客要主是加深自己對進程返回的認識和總結實現算法時的一些驗經和訓教,如果有錯誤請指出,萬分感謝。
????一、
????當沒有數據可讀時
O_NONBLOCK disable:read調用阻塞,即進程暫停執行,始終等到有數據離開為止。
????O_NONBLOCK enable:read調用返回-1,errno值為EAGAIN。
????示例程序如下:
????
????
C++ Code?| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | ? | /************************************************************************* ????>?File?Name:?process_.c ????>?Author:?Simba ????>?Mail:?dameng34@163.com ????>?Created?Time:?Sat?23?Feb?2013?02:34:02?PM?CST ?************************************************************************/ #include<sys/types.h> #include<sys/stat.h> #include<unistd.h> #include<fcntl.h> #include<stdio.h> #include<stdlib.h> #include<errno.h> #include<string.h> #include<signal.h> #define?ERR_EXIT(m)?\ ???? do?{?\ ????????perror(m);?\ ????????exit(EXIT_FAILURE);?\ ????}? while( 0) int?main( int?argc,? char?*argv[]) { ???? int?pipefd[ 2]; ???? if?(pipe(pipefd)?==?- 1) ????????ERR_EXIT( "pipe?error"); ????pid_t?pid; ????pid?=?fork(); ???? if?(pid?==?- 1) ????????ERR_EXIT( "fork?error"); ???? if?(pid?==? 0) ????{ ????????sleep( 3); ????????close(pipefd[ 0]); ????????write(pipefd[ 1],? "hello",? 5); ????????close(pipefd[ 1]); ????????exit(EXIT_SUCCESS); ????} ????close(pipefd[ 1]); ???? char?buf[ 10]?=?{ 0}; ???? int?flags?=?fcntl(pipefd[ 0],?F_GETFL); ????fcntl(pipefd[ 0],?F_SETFL,?flags?|?O_NONBLOCK);? //enable?fd的O_NONBLOCK ???? int?ret?=?read(pipefd[ 0],?buf,? 10);? //默認是disable?fd的O_NONBLOCK ???? if?(ret?==?- 1)? //?父進程不會阻塞,犯錯返回 ????????ERR_EXIT( "read?error"); ????printf( "buf=%s\n",?buf); ???? return? 0; } |
????特意在子進程中sleep了3s,讓父進程先被調度運行,而且讀端文件描述符標志設置為非阻塞,即立刻犯錯返回,如下。
????simba@ubuntu:~/Documents/code/linux_programming/APUE/pipe$ ./pipe_block?
read error: Resource temporarily unavailable
????
????二、當管道滿的時候
O_NONBLOCK disable: write調用阻塞,直到有進程讀走數據
O_NONBLOCK enable:調用返回-1,errno值為EAGAIN
????管道是一塊內存緩沖區,可以寫個小程序測試一下管道的容量Pipe Capacity:
????
????
C++ Code?| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | ? | /************************************************************************* ????>?File?Name:?process_.c ????>?Author:?Simba ????>?Mail:?dameng34@163.com ????>?Created?Time:?Sat?23?Feb?2013?02:34:02?PM?CST ?************************************************************************/ #include<sys/types.h> #include<sys/stat.h> #include<unistd.h> #include<fcntl.h> #include<stdio.h> #include<stdlib.h> #include<errno.h> #include<string.h> #include<signal.h> #define?ERR_EXIT(m)?\ ???? do?{?\ ????????perror(m);?\ ????????exit(EXIT_FAILURE);?\ ????}? while( 0) int?main( int?argc,? char?*argv[]) { ???? int?pipefd[ 2]; ???? if?(pipe(pipefd)?==?- 1) ????????ERR_EXIT( "pipe?error"); ???? int?ret; ???? int?count?=? 0; ???? int?flags?=?fcntl(pipefd[ 1],?F_GETFL); ????fcntl(pipefd[ 1],?F_SETFL,?flags?|?O_NONBLOCK);? //?設置為非阻塞 ???? while?( 1) ????{ ????????ret?=?write(pipefd[ 1],? "A",? 1); ???????? if?(ret?==?- 1) ????????{ ????????????printf( "err=%s\n",?strerror(errno)); ???????????? break; ????????} ????????count++; ????} ????printf( "count=%d\n",?count);? //管道容量 ???? return? 0; } |
????程序中將寫端文件描述符標志設置為非阻塞,當管道被寫滿時不會等待其他進程讀取數據,而是直接返回-1并置errno,輸出如下:
simba@ubuntu:~/Documents/code/linux_programming/APUE/pipe$ ./pipe_capacity?
err=Resource temporarily unavailable
count=65536
????打印了錯誤碼,可以看到管道的容量是64kB,man 7 pipe中也有提到在2.6.11內核以前是4096,現在是65536。
????
????三、如果全部管道讀端對應的文件描述符被關閉(管道讀端的引用計數等于0),則write操縱會產生SIGPIPE信號,默認終止以后進程
????示例代碼如下:
????
????
C++ Code?| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | ? | /************************************************************************* ????>?File?Name:?process_.c ????>?Author:?Simba ????>?Mail:?dameng34@163.com ????>?Created?Time:?Sat?23?Feb?2013?02:34:02?PM?CST ?************************************************************************/ #include<sys/types.h> #include<sys/stat.h> #include<unistd.h> #include<fcntl.h> #include<stdio.h> #include<stdlib.h> #include<errno.h> #include<string.h> #include<signal.h> #define?ERR_EXIT(m)?\ ???? do?{?\ ????????perror(m);?\ ????????exit(EXIT_FAILURE);?\ ????}? while( 0) void?handler( int?sig) { ????printf( "recv?sig=%d\n",?sig); } int?main( int?argc,? char?*argv[]) { ????signal(SIGPIPE,?handler); ???? int?pipefd[ 2]; ???? if?(pipe(pipefd)?==?- 1) ????????ERR_EXIT( "pipe?error"); ????pid_t?pid; ????pid?=?fork(); ???? if?(pid?==?- 1) ????????ERR_EXIT( "fork?error"); ???? if?(pid?==? 0) ????{ ????????close(pipefd[ 0]); ????????exit(EXIT_SUCCESS); ????} ????close(pipefd[ 0]); ????sleep( 1); ???? int?ret?=?write(pipefd[ 1],? "hello",? 5); ???? if?(ret?==?- 1) ????{ ????????printf( "err=%s\n",?strerror(errno)); ????} ???? return? 0; } |
????輸出測試:
????simba@ubuntu:~/Documents/code/linux_programming/APUE/pipe$ ./close_fd_read?
recv sig=13
err=Broken pipe
因為自信,在呀呀學語時,我靠著纖嫩的雙腿,邁出人生的第一步;因為自信,我一次次將第一名的獎狀高高舉起;因為自信,我毫不吝惜地剪掉飄逸的長發,在運動場上展現風采……感謝自信,它給了我一雙翅膀,讓我在電閃雷鳴中去飛翔,在風雨中去搏擊人生!
????父進程睡眠1s確保全部讀端文件描述符都已經關閉,如果沒有安裝SIGPIPE信號的處理函數,則默認終止以后進程,即write函數不會返回,現在write錯誤返回-1,并置errno=EPIPE,對應的犯錯信息是Broken pipe。
????
????四、如果全部管道寫端對應的文件描述符被關閉(管道寫端的引用計數等于0),那么管道中殘余的數據都被讀取后,再次read會返回0
????示例程序如下:
????
????
C++ Code?| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | ? | /************************************************************************* ????>?File?Name:?process_.c ????>?Author:?Simba ????>?Mail:?dameng34@163.com ????>?Created?Time:?Sat?23?Feb?2013?02:34:02?PM?CST ?************************************************************************/ #include<sys/types.h> #include<sys/stat.h> #include<unistd.h> #include<fcntl.h> #include<stdio.h> #include<stdlib.h> #include<errno.h> #include<string.h> #include<signal.h> #define?ERR_EXIT(m)?\ ???? do?{?\ ????????perror(m);?\ ????????exit(EXIT_FAILURE);?\ ????}? while( 0) void?handler( int?sig) { ????printf( "recv?sig=%d\n",?sig); } int?main( int?argc,? char?*argv[]) { ????signal(SIGPIPE,?handler); ???? int?pipefd[ 2]; ???? if?(pipe(pipefd)?==?- 1) ????????ERR_EXIT( "pipe?error"); ????pid_t?pid; ????pid?=?fork(); ???? if?(pid?==?- 1) ????????ERR_EXIT( "fork?error"); ???? if?(pid?==? 0) ????{ ????????close(pipefd[ 1]); ????????exit(EXIT_SUCCESS); ????} ????close(pipefd[ 1]); ????sleep( 1); ???? char?buf[ 10]?=?{ 0}; ???? int?ret?=?read(pipefd[ 0],?buf,? 10); ????printf( "ret?=?%d\n",?ret); ???? return? 0; } |
????輸出測試如下:
????simba@ubuntu:~/Documents/code/linux_programming/APUE/pipe$ ./close_fd_write?
ret = 0
????同樣地父進程睡眠1s確保全部的寫端文件描述符都已經關閉,read返回0。
????
????五、當要寫入的數據量不大于PIPE_BUF時,linux將保證寫入的原子性;當要寫入的數據量大于PIPE_BUF時,linux將不再保證寫入的原子性。
????On ?Linux,?PIPE_BUF is 4096 bytes。
?????The precise semantics depend on whether the file descriptor is nonblocking (O_NONBLOCK),?whether there are multiple writers to the pipe, and on n, the number of bytes to be written。即由文件描述符的標志,是否有多個進程向管道寫入以及寫入的字節數所決定準確的語義,總共分4種情況,具體可man一下。
????
????下面的程序演示O_NONBLOCK disabled ,size > PIPE_BUF(4K)的情況 :
????
????
C++ Code?| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | ? | /************************************************************************* ????>?File?Name:?process_.c ????>?Author:?Simba ????>?Mail:?dameng34@163.com ????>?Created?Time:?Sat?23?Feb?2013?02:34:02?PM?CST ?************************************************************************/ #include<sys/types.h> #include<sys/stat.h> #include<unistd.h> #include<fcntl.h> #include<stdio.h> #include<stdlib.h> #include<errno.h> #include<string.h> #include<signal.h> #define?ERR_EXIT(m)?\ ???? do?{?\ ????????perror(m);?\ ????????exit(EXIT_FAILURE);?\ ????}? while( 0) #define?TEST_SIZE? 68* 1024? //?68KB /*?默認O_NONBLOCK?disabled?,這里驗證?size?>?PIPE_BUF(4K)的情況?*/ int?main( int?argc,? char?*argv[]) { ???? char?a[TEST_SIZE]; ???? char?b[TEST_SIZE]; ????memset(a,? 'A',? sizeof(a)); ????memset(b,? 'B',? sizeof(b)); ???? int?pipefd[ 2]; ???? int?ret?=?pipe(pipefd); ???? if?(ret?==?- 1) ????????ERR_EXIT( "pipe?error"); ???? int?pid?=?fork(); ???? if?(pid?==? 0) ????{ ????????close(pipefd[ 0]); ????????ret?=?write(pipefd[ 1],?a,? sizeof(a));? //?全部寫完才返回 ????????printf( "apid=%d?write?%d?bytes?to?pipe\n",?getpid(),?ret); ????????exit( 0); ????} ????pid?=?fork(); ???? if?(pid?==? 0) ????{ ????????close(pipefd[ 0]); ????????ret?=?write(pipefd[ 1],?b,? sizeof(b)); ????????printf( "bpid=%d?write?%d?bytes?to?pipe\n",?getpid(),?ret); ????????exit( 0); ????} ????close(pipefd[ 1]); ????sleep( 1); ???? int?fd?=?open( "test.txt",?O_WRONLY?|?O_CREAT?|?O_TRUNC,? 0664); ???? char?buf[ 1024?*? 4]?=?{ 0}; ???? int?n?=? 1; ???? while?( 1) ????{ ????????ret?=?read(pipefd[ 0],?buf,? sizeof(buf));? //當管道被寫入數據,就已經可以開始讀了,每次讀取4k ???????? if?(ret?==? 0)? //?管道寫端全部關閉,即讀到了結尾 ???????????? break; ????????printf( "n=%02d?pid=%d?read?%d?bytes?from?pipe?buf[4095]=%c\n", ???????????????n++,?getpid(),?ret,?buf[ 4095]); ????????write(fd,?buf,?ret); ????} ???? return? 0; } |
????輸出測試如下:
????simba@ubuntu:~/Documents/code/linux_programming/APUE/pipe$ ./pipe_buf?
n=01 pid=7137 read 4096 bytes from pipe buf[4095]=B
n=02 pid=7137 read 4096 bytes from pipe buf[4095]=B
n=03 pid=7137 read 4096 bytes from pipe buf[4095]=B
n=04 pid=7137 read 4096 bytes from pipe buf[4095]=B
n=05 pid=7137 read 4096 bytes from pipe buf[4095]=B
n=06 pid=7137 read 4096 bytes from pipe buf[4095]=B
n=07 pid=7137 read 4096 bytes from pipe buf[4095]=B
n=08 pid=7137 read 4096 bytes from pipe buf[4095]=B
n=09 pid=7137 read 4096 bytes from pipe buf[4095]=B
n=10 pid=7137 read 4096 bytes from pipe buf[4095]=B
n=11 pid=7137 read 4096 bytes from pipe buf[4095]=B
n=12 pid=7137 read 4096 bytes from pipe buf[4095]=B
n=13 pid=7137 read 4096 bytes from pipe buf[4095]=B
n=14 pid=7137 read 4096 bytes from pipe buf[4095]=B
n=15 pid=7137 read 4096 bytes from pipe buf[4095]=B
n=16 pid=7137 read 4096 bytes from pipe buf[4095]=B
n=17 pid=7137 read 4096 bytes from pipe buf[4095]=A
n=18 pid=7137 read 4096 bytes from pipe buf[4095]=A
n=19 pid=7137 read 4096 bytes from pipe buf[4095]=A
n=20 pid=7137 read 4096 bytes from pipe buf[4095]=A
n=21 pid=7137 read 4096 bytes from pipe buf[4095]=A
n=22 pid=7137 read 4096 bytes from pipe buf[4095]=A
n=23 pid=7137 read 4096 bytes from pipe buf[4095]=A
n=24 pid=7137 read 4096 bytes from pipe buf[4095]=A
n=25 pid=7137 read 4096 bytes from pipe buf[4095]=A
n=26 pid=7137 read 4096 bytes from pipe buf[4095]=A
apid=7138 write 69632 bytes to pipe
n=27 pid=7137 read 4096 bytes from pipe buf[4095]=A
n=28 pid=7137 read 4096 bytes from pipe buf[4095]=A
n=29 pid=7137 read 4096 bytes from pipe buf[4095]=A
n=30 pid=7137 read 4096 bytes from pipe buf[4095]=A
n=31 pid=7137 read 4096 bytes from pipe buf[4095]=A
n=32 pid=7137 read 4096 bytes from pipe buf[4095]=A
n=33 pid=7137 read 4096 bytes from pipe buf[4095]=A
n=34 pid=7137 read 4096 bytes from pipe buf[4095]=B
bpid=7139 write 69632 bytes to pipe
????
????分析一下:現在的情況是有兩個子進程在對管道進行阻塞寫入各68k,即每個子進程完全寫入68k才返回,而父進程對管道進行阻塞讀取,每次讀取4k,打印每4k中的最后一個字符。需要注意的是是邊寫邊讀,因為前面說過管道的容量只有64k,當管道被寫滿時子進程就阻塞等待父進程讀取后再寫入。由上面輸出可以看出B進程先寫入64k的B,然后A進程寫入68k的A之后B進程接著寫完最后4K的B,然后write返回。由A進程write完畢輸出的提示可知此時A進程已經寫完成了,但父進程還沒讀取A完畢,當兩個子進程全部寫完退出時關閉寫端文件描述符,則父進程read就會返回0,退出while循環。可以得出結論:當多個進程對管道進行寫入,且一次性寫入數據量大于PIPE_BUF時,則不能保證寫入的原子性,即可能數據是穿插著的。man 手冊的解釋如下:
????? ? ? ?O_NONBLOCK disabled, n > PIPE_BUF
?The write is nonatomic: the data given to write(2) may be interleaved with write(2)s by other process; ?the?write(2) blocks until n bytes have been written.
????
????注意我們這里設定了size=68k,則寫端不能設置成非阻塞,因為PIPE_BUF只有64k,不能一次性寫入68k,故只能返回-1,且一個字符也不寫入,讀端也不能設置為非阻塞,因為有可能此時管道還沒被寫入數據或者不夠一次性read的數據量的時候去讀,則直接返回-1,不會阻塞等待足夠數據量后進行讀取。總之測試4種不同情形下的情況也應設置不同的條件。
????
????管道的前4種讀寫規則具有普遍意義,Tcp socket 也具有管道的這些特性。
文章結束給大家分享下程序員的一些笑話語錄: IBM和波音777
波音777是有史以來第一架完全在電腦虛擬現實中設計制造的飛機,所用的設備完全由IBM公司所提供。試飛前,波音公司的總裁非常熱情的邀請IBM的技術主管去參加試飛,可那位主管卻說道:“啊,非常榮幸,可惜那天是我妻子的生日,So..”..
波音公司的總載一聽就生氣了:“膽小鬼,我還沒告訴你試飛的日期呢!”
轉載于:https://www.cnblogs.com/jiangu66/archive/2013/05/21/3091503.html
總結
以上是生活随笔為你收集整理的进程返回linux系统编程之管道(二):管道读写规则和Pipe Capacity、PIPE_BUF的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: protobuf生成as文件
- 下一篇: linux 应用层时间和随机数,zigb