原帖:http://blog.csdn.net/nodeathphoenix/article/details/23284157html
當沒有數據可讀時node
當管道滿的時候linux
若是全部管道寫端對應的文件描述符被關閉,則read返回0api
若是全部管道讀端對應的文件描述符被關閉,則write操做會產生信號SIGPIPEapp
當要寫入的數據量<=PIPE_BUF時,linux將保證寫入的原子性。less
當要寫入的數據量>PIPE_BUF時,linux將再也不保證寫入的原子性。ui
Detail 參見:atom
http://man7.org/linux/man-pages/man7/pipe.7.htmlspa
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <fcntl.h> int main(void) { int fds[2]; if(pipe(fds) == -1){ perror("pipe error"); exit(EXIT_FAILURE); } pid_t pid; pid = fork(); if(pid == -1){ perror("fork error"); exit(EXIT_FAILURE); } if(pid == 0){ close(fds[0]);//子進程關閉讀端 sleep(10); write(fds[1],"hello",5); exit(EXIT_SUCCESS); } close(fds[1]);//父進程關閉寫端 char buf[10] = {0}; read(fds[0],buf,10); printf("receive datas = %s\n",buf); return 0; }
結果:.net
說明:管道建立時默認打開了文件描述符,且默認是阻塞(block)模式打開
因此這裏,咱們讓子進程先睡眠10s,父進程由於沒有數據從管道中讀出,被阻塞了,直到子進程睡眠結束,向管道中寫入數據後,父進程纔讀到數據
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <fcntl.h> int main(void) { int fds[2]; if(pipe(fds) == -1){ perror("pipe error"); exit(EXIT_FAILURE); } pid_t pid; pid = fork(); if(pid == -1){ perror("fork error"); exit(EXIT_FAILURE); } if(pid == 0){ close(fds[0]);//子進程關閉讀端 sleep(10); write(fds[1],"hello",5); exit(EXIT_SUCCESS); } close(fds[1]);//父進程關閉寫端 char buf[10] = {0}; int flags = fcntl(fds[0], F_GETFL);//先獲取原先的flags fcntl(fds[0],F_SETFL,flags | O_NONBLOCK);//設置fd爲阻塞模式 int ret; ret = read(fds[0],buf,10); if(ret == -1){ perror("read error"); exit(EXIT_FAILURE); } printf("receive datas = %s\n",buf); return 0; }
結果:
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <fcntl.h> int main(void) { int fds[2]; if(pipe(fds) == -1){ perror("pipe error"); exit(EXIT_FAILURE); } pid_t pid; pid = fork(); if(pid == -1){ perror("fork error"); exit(EXIT_FAILURE); } if(pid == 0){ close(fds[1]);//子進程關閉寫端 exit(EXIT_SUCCESS); } close(fds[1]);//父進程關閉寫端 char buf[10] = {0}; int ret; ret = read(fds[0],buf,10); printf("ret = %d\n", ret); return 0; }
結果:
可知確實返回0,表示讀到了文件末尾,並不表示出錯
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <fcntl.h> #include <signal.h> void sighandler(int signo); int main(void) { int fds[2]; if(signal(SIGPIPE,sighandler) == SIG_ERR) { perror("signal error"); exit(EXIT_FAILURE); } if(pipe(fds) == -1){ perror("pipe error"); exit(EXIT_FAILURE); } pid_t pid; pid = fork(); if(pid == -1){ perror("fork error"); exit(EXIT_FAILURE); } if(pid == 0){ close(fds[0]);//子進程關閉讀端 exit(EXIT_SUCCESS); } close(fds[0]);//父進程關閉讀端 sleep(1);//確保子進程也將讀端關閉 int ret; ret = write(fds[1],"hello",5); if(ret == -1){ printf("write error\n"); } return 0; } void sighandler(int signo) { printf("catch a SIGPIPE signal and signum = %d\n",signo); }
結果:
可知當全部讀端都關閉時,write時確實產生SIGPIPE信號
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <fcntl.h> int main(void) { int fds[2]; if(pipe(fds) == -1){ perror("pipe error"); exit(EXIT_FAILURE); } int ret; int count = 0; while(1){ ret = write(fds[1],"A",1);//fds[1]默認是阻塞模式 if(ret == -1){ perror("write error"); break; } count++; } return 0; }
結果:
說明:fd打開時默認是阻塞模式,當pipe緩衝區滿時,write操做確實阻塞了,等待其餘進程將數據從管道中取走
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <fcntl.h> int main(void) { int fds[2]; if(pipe(fds) == -1){ perror("pipe error"); exit(EXIT_FAILURE); } int ret; int count = 0; int flags = fcntl(fds[1],F_GETFL); fcntl(fds[1],F_SETFL,flags|O_NONBLOCK); while(1){ ret = write(fds[1],"A",1);//fds[1]默認是阻塞模式 if(ret == -1){ perror("write error"); break; } count++; } printf("the pipe capcity is = %d\n",count); return 0; }
結果:
可知也出現EGIN錯誤,管道容量是65536字節
man 7 pipe說明:
A pipe has a limited capacity. If the pipe is full, then a write(2) will block or fail, depending on whether the O_NONBLOCK flag is set (see below). Different implementations have different limits for the pipe capacity. Applications should not rely on a particular capacity: an application should be designed so that a reading process consumes data as soon as it is available, so that a writing process does not remain blocked. In Linux versions before 2.6.11, the capacity of a pipe was the same as the system page size (e.g., 4096 bytes on i386). Since Linux 2.6.11, the pipe capacity is 65536 bytes.
man幫助說明:
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 nonatomic: 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 nonblocking (O_NONBLOCK), whether there are multiple writers to the pipe, and on n, the number of bytes to be written: 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 阻塞模式時且n<PIPE_BUF:寫入具備原子性,若是沒有足夠的空間供n個字節所有寫入,則阻塞直到有足夠空間將n個字節所有寫入管道 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. 非阻塞模式時且n<PIPE_BUF:寫入具備原子性,當即所有成功寫入,不然一個都不寫入,返回錯誤 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. 阻塞模式時且n>PIPE_BUF:不具備原子性,可能中間有其餘進程穿插寫入,直到將n字節所有寫入才返回,不然阻塞等待寫入 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.
非阻塞模式時且N>PIPE_BUF:若是管道滿的,則當即失敗,一個都不寫入,返回錯誤,若是不滿,則返回寫入的字節數爲1~n,即部分寫入,寫入時可能有其餘進程穿插寫入
注:管道容量不必定等於PIPE_BUF
示例:當寫入數據大於PIPE_BUF時
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <errno.h> #include <fcntl.h> #define ERR_EXIT(m) \ do \ { \ perror(m); \ exit(EXIT_FAILURE); \ } while(0) #define TEST_SIZE 68*1024 int main(void) { char a[TEST_SIZE]; char b[TEST_SIZE]; char c[TEST_SIZE]; memset(a, 'A', sizeof(a)); memset(b, 'B', sizeof(b)); memset(c, 'C', sizeof(c)); int pipefd[2]; int ret = pipe(pipefd); if (ret == -1) ERR_EXIT("pipe error"); pid_t pid; 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); } pid = fork(); if (pid == 0)//第三個子進程 { close(pipefd[0]); ret = write(pipefd[1], c, sizeof(c)); 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, 0644); char buf[1024*4] = {0}; int n = 1; while (1) { ret = read(pipefd[0], buf, sizeof(buf)); 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; }
結果:
可見各子進程間出現穿插寫入,並沒保證原子性寫入,且父進程在子進程編寫時邊讀。