O_NONBLOCK enable:read調用返回-1,errno值爲EAGAIN。linux
示例程序以下:編程
#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); } // sleep(3); 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,讓父進程先被調度運行,並且讀端文件描述符標誌設置爲非阻塞,即馬上出錯返回,以下:
huangcheng@ubuntu:~$ ./a.out read error: Resource temporarily unavailable
假設開啓35行,註釋29行,讓父進程先sleep,子進程先運行,則運行結果:ubuntu
huangcheng@ubuntu:~$ ./a.out buf=hello
2、當管道滿的時候
O_NONBLOCK disable: write調用阻塞,直到有進程讀走數據
O_NONBLOCK enable:調用返回-1,errno值爲EAGAIN小程序
管道是一塊內存緩衝區,能夠寫個小程序測試一下管道的容量Pipe Capacity:api
#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,輸出以下:
huangcheng@ubuntu:~$ ./a.out err=Resource temporarily unavailable count=65536打印了錯誤碼,能夠看到管道的容量是64kB,man 7 pipe中也有提到在2.6.11內核之前是4096,如今是65536。
3、若是全部管道讀端對應的文件描述符被關閉(管道讀端的引用計數等於0),則write操做會產生SIGPIPE信號,默認終止當前進程socket
示例代碼以下:函數
#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; }輸出測試:
huangcheng@ubuntu:~$ ./a.out recv sig=13 err=Broken pipe
父進程睡眠1s確保全部讀端文件描述符都已經關閉,若是沒有安裝SIGPIPE信號的處理函數,則默認終止當前進程,即write函數不會返回,如今write錯誤返回-1,並置errno=EPIPE,對應的出錯信息是Broken pipe。測試
4、若是全部管道寫端對應的文件描述符被關閉(管道寫端的引用計數等於0),那麼管道中剩餘的數據都被讀取後,再次read會返回0atom
示例程序以下:spa
#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; }輸出測試以下:
huangcheng@ubuntu:~$ ./a.out ret = 0一樣地父進程睡眠1s確保全部的寫端文件描述符都已經關閉,read返回0。
5、當要寫入的數據量不大於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)的狀況 :
#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; }輸出測試以下:
huangcheng@ubuntu:~$ ./a.out n=01 pid=2598 read 4096 bytes from pipe buf[4095]=B n=02 pid=2598 read 4096 bytes from pipe buf[4095]=B n=03 pid=2598 read 4096 bytes from pipe buf[4095]=B n=04 pid=2598 read 4096 bytes from pipe buf[4095]=B n=05 pid=2598 read 4096 bytes from pipe buf[4095]=B n=06 pid=2598 read 4096 bytes from pipe buf[4095]=B n=07 pid=2598 read 4096 bytes from pipe buf[4095]=B n=08 pid=2598 read 4096 bytes from pipe buf[4095]=B n=09 pid=2598 read 4096 bytes from pipe buf[4095]=B n=10 pid=2598 read 4096 bytes from pipe buf[4095]=B n=11 pid=2598 read 4096 bytes from pipe buf[4095]=B n=12 pid=2598 read 4096 bytes from pipe buf[4095]=B n=13 pid=2598 read 4096 bytes from pipe buf[4095]=B n=14 pid=2598 read 4096 bytes from pipe buf[4095]=B n=15 pid=2598 read 4096 bytes from pipe buf[4095]=B n=16 pid=2598 read 4096 bytes from pipe buf[4095]=B bpid=2600 write 69632 bytes to pipe n=17 pid=2598 read 4096 bytes from pipe buf[4095]=B n=18 pid=2598 read 4096 bytes from pipe buf[4095]=A n=19 pid=2598 read 4096 bytes from pipe buf[4095]=A n=20 pid=2598 read 4096 bytes from pipe buf[4095]=A n=21 pid=2598 read 4096 bytes from pipe buf[4095]=A n=22 pid=2598 read 4096 bytes from pipe buf[4095]=A n=23 pid=2598 read 4096 bytes from pipe buf[4095]=A n=24 pid=2598 read 4096 bytes from pipe buf[4095]=A n=25 pid=2598 read 4096 bytes from pipe buf[4095]=A n=26 pid=2598 read 4096 bytes from pipe buf[4095]=A n=27 pid=2598 read 4096 bytes from pipe buf[4095]=A n=28 pid=2598 read 4096 bytes from pipe buf[4095]=A n=29 pid=2598 read 4096 bytes from pipe buf[4095]=A n=30 pid=2598 read 4096 bytes from pipe buf[4095]=A n=31 pid=2598 read 4096 bytes from pipe buf[4095]=A n=32 pid=2598 read 4096 bytes from pipe buf[4095]=A n=33 pid=2598 read 4096 bytes from pipe buf[4095]=A apid=2599 write 69632 bytes to pipe n=34 pid=2598 read 4096 bytes from pipe buf[4095]=A
分析一下:如今的狀況是有兩個子進程在對管道進行阻塞寫入各68k,即每一個子進程徹底寫入68k才返回,而父進程對管道進行阻塞讀取,每次讀取4k,打印每4k中的最後一個字符,若是沒有數據到達就阻塞等待,若是管道剩餘數據不足4k,read 極可能返回 < 4k,但由於咱們寫入68k是4k整數倍,故不存在這種狀況。須要注意的是是邊寫邊讀,由於前面說過管道的容量只有64k,當管道被寫滿時子進程就阻塞等待父進程讀取後再寫入。由上面輸出能夠看出B進程先寫入64k的B,而後寫入剩下的4k的B,接着A進程先寫入64k的A以後接着寫完最後的4k的A,而後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只有4k,不能一次性寫入68k,若是此時管道是滿的(64k),則只能返回-1並置錯誤碼爲EAGAIN,且一個字符也不寫入,若不是滿的,則寫入的字節數是不肯定的,須要檢查write的返回值,並且這些字節極可能也與其餘進程寫入的數據穿插着。讀端也不能設置爲非阻塞,若是此時還沒有有數據寫入(管道爲空)則返回-1並置錯誤碼爲EAGAIN,若是有部分數據已經寫入,則讀取的數據字節數也是不肯定的,須要檢查read的返回值。總之測試4種不一樣情形下的狀況也應設置不一樣的條件。
詳細說明見:《UNIX環境高級編程——管道和FIFO的額外屬性》
O_NONBLOCK disabled, n <= PIPE_BUF All n bytes are written atomically; write(2) may block if there is not room for n bytes to be written immediately O_NONBLOCK enabled, n <= PIPE_BUF If there is room to write n bytes to the pipe, then write(2) succeeds immediately, writing all n bytes; otherwise write(2) fails, with errno set to EAGAIN. O_NONBLOCK disabled, n > PIPE_BUF The write is non-atomic: 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. O_NONBLOCK enabled, n > PIPE_BUF If the pipe is full, then write(2) fails, with errno set to EAGAIN. Otherwise, from 1 to n bytes may be written (i.e., a "partial write" may occur; the caller should check the return value from write(2) to see how many bytes were actually written), and these bytes may be interleaved with writes by other processes.