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.