管道读写规则和Pipe Capacity、PIPE_BUF
一、当没有数据可读时
O_NONBLOCK disable:read调用阻塞,即进程暂停执行,一直等到有数据来到为止。
O_NONBLOCK enable:read调用返回-1,errno值为EAGAIN。
示例程序如下:
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[]) pid_t pid; if (pid == 0) close(pipefd[1]); 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:
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 ret; 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信号,默认终止当前进程
示例代码如下:
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) int main(int argc, char *argv[]) int pipefd[2]; pid_t pid; if (pid == 0) 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
示例程序如下:
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) int main(int argc, char *argv[]) int pipefd[2]; pid_t pid; if (pid == 0) close(pipefd[1]); 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)的情况 :
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 memset(a, 'A', sizeof(a)); int pipefd[2]; int pid = fork(); close(pipefd[0]); pid = fork(); if (pid == 0) close(pipefd[0]); close(pipefd[1]); sleep(1); int fd = open("test.txt", O_WRONLY | O_CREAT | O_TRUNC, 0664); 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中的最后一个字符,如果没有数据到达就阻塞等待,如果管道剩余数据不足4k,read
很可能返回 <
4k,但因为我们写入68k是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 Capacity 只有64k,不能一次性写入68k,如果此时管道是满的(64k),则只能返回-1并置错误码为EAGAIN,且一个字符也不写入,若不是满的,则写入的字节数是不确定的,需要检查write的返回值,而且这些字节很可能也与其他进程写入的数据穿插着。读端也不能设置为非阻塞,如果此时尚未有数据写入(管道为空)则返回-1并置错误码为EAGAIN,如果有部分数据已经写入,则读取的数据字节数也是不确定的,需要检查read的返回值。总之测试4种不同情形下的情况也应设置不同的条件。
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 imme‐
diately
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 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.
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.
管道的前4种读写规则具有普遍意义,Tcp socket 也具有管道的这些特性。
参考:《APUE》
管道读写规则和Pipe Capacity、PIPE_BUF的更多相关文章
- UNIX环境高级编程——管道读写规则和pipe Capacity、PIPE_BUF
一.当没有数据可读时O_NONBLOCK disable:read调用阻塞,即进程暂停执行,一直等到有数据来到为止. O_NONBLOCK enable:read调用返回-1,errno值为EAGAI ...
- linux系统编程之管道(二):管道读写规则
一,管道读写规则 当没有数据可读时 O_NONBLOCK disable:read调用阻塞,即进程暂停执行,一直等到有数据来到为止. O_NONBLOCK enable:read调用返回-1,errn ...
- 命名管道FIFO及其读写规则
一.匿名管道的一个限制就是只能在具有共同祖先的进程间通信命名管道(FIFO):如果我们想在不相关的进程之间切换数据,可以使用FIFO文件来做这项工作注意:命名管道是一种特殊类型文件.利用命令:$ mk ...
- [apue] 多进程管道读写的一些疑问
对于一对一的pipe: 1) 写进程关闭写管道后,读进程继续读管道会导致read返回0: 2) 读进程关闭读管道后,写进程继续写管道会激发SIGPIPE信号,若捕获,则write返回-1: 而对于多对 ...
- (转)epoll非阻塞读写规则
EPOLL技术 在linux的网络编程中,很长的时间都在使用select来做事件触发.在linux新的内核中,有了一种替换它的机制,就是epoll.相比于select,epoll最大的好处在于它不会随 ...
- Linux学习记录--命名管道通信
命名管道通信 什么是命名管道 一个主要的限制是,它是匿名管道的应用还没有名字,因此,只有它可以用于进程间通信的方式与亲缘关系.在命名管道(named pipe或FIFO)提出后,该限制得到了克服.FI ...
- Linux IPC实践(2) --匿名PIPE
管道概念 管道是Unix中最古老的进程间通信的形式,我们把从一个进程连接到另一个进程的一个数据流称为一个"管道", 管道的本质是固定大小的内核缓冲区; 如:ps aux | gre ...
- Linux进程间通信—管道
Linux下的进程通信手段基本上是从UNIX平台上的进程通信手段继承而来的.而对UNIX发展做出重大贡献的两大主力AT&T的贝尔实验室及BSD(加州大学伯克利分校的伯克利软件发布中心)在进程间 ...
- shell 匿名管道和命名管道
管道的特点:如果管道中没有数据,那么取管道数据的操作就会滞留,直到管道内进入数据,然后读出后才会终止这一操作:同理,写入管道的操作如果没有读取管道的操作,这一动作也会滞留. 1,匿名管道 匿名管道使用 ...
随机推荐
- iOS开发-UIWebView加载本地和网络数据
UIWebView是内置的浏览器控件,可以用它来浏览网页.打开文档,关于浏览网页榜样可以参考UC,手机必备浏览器,至于文档浏览的手机很多图书阅读软件,UIWebView是一个混合体,具体的功能控件内置 ...
- Gradle for Android 翻译 -1
英文版电子书下载 参考:Gradle for Android 一.从 Gradle 和 AS 开始 [Getting Started with Gradle and Android Studio] ...
- jquery中filter(fn)的使用研究
jquery中filter(fn)给出的官方说明是: 筛选出与指定函数返回值匹配的元素集合 这个函数内部将对每个对象计算一次 (正如 '$.each'). 如果调用的函数返回false则这个元素被删除 ...
- C#(64位系统) 解决"未能加载文件或程序集,或它的某一个依赖项..."
这个问题通常出在引用第三方DLL或者自己以前写的DLL. 在64位系统下则可能会出现这种问题. 今天下载MySQLDriverCS后引用遍出现了这个问题,参考了一些文档,下面给出解决方法: 将项目的生 ...
- 利用WebClient实现对Http协议的Post和Get对网站进行模拟登陆和浏览
我们在一些场合经常需要模拟浏览器进行一些操作,比如模拟投票,或者模拟点击,或者Web游戏外挂.而C#中封装好的WebClient可以在某些要求不算搞的场景实现Http的Post和Get.具体请见代码: ...
- PHP json_encode转换空数组为对象
问题描述: php返回json格式的数据,当返回数据的为数组,且key为字符串时,json化后将返回jsonObject,但是如果是空数组,有可能返回的就是jsonArray,数据结构不一致导致端解析 ...
- Linux command 系统快捷键
群里有人问"问个问题,Linux 命令行有没有快捷键一下从行末会到行头?经常敲了很多命令发现忘加 sudo 了,然后把命令删了重新敲一遍". 自己还真不知道怎么操作,只知道历史命令 ...
- 微信小程序scroll-view不动的处理
scroll-view原来好好的,最近突然不动了,捣鼓半天,居然是要手工设定为scroll属性!下面两样样式必不可少,一是设定滚动方向上的宽度或高度,二是设置超出的处理为scroll 1.设定宽度 2 ...
- 【ACM】杭电ACM题一直WA求高手看看代码
数据测试了好几个都没问题,可以就是WA不让过,检测了2个小时还是没发现有什么问题T_T!!求高手看看代码,小弟在此谢谢各位哦! #include <stdio.h> #include &l ...
- 算法(第四版)学习笔记之java实现希尔排序
希尔排序思想:使数组中随意间隔为h的元素都是有序的. 希尔排序是插入排序的优化.先对数组局部进行排序,最后再使用插入排序将部分有序的数组排序. 代码例如以下: /** * * @author seab ...