概述
http://tech.ddvip.com/2013-05/1369683339196204_2.html
一、当没有数据可读时
O_NONBLOCK disable:read调用阻塞,即进程暂停执行,一直等到有数据来到为止。
O_NONBLOCK enable:read调用返回-1,errno值为EAGAIN。
示例程序如下:
/*************************************************************************
> 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=%sn", 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:
/*************************************************************************
> 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=%sn", strerror(errno));
break;
}
count++;
}
printf("count=%dn", 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信号,默认终止当前进程
示例代码如下:
/*************************************************************************
> 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=%dn", 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=%sn", 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
示例程序如下:
/*************************************************************************
> 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=%dn", 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 = %dn", 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)的情况 :
/*************************************************************************
> 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 pipen", 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 pipen", 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]=%cn",
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 也具有管道的这些特性。
最后
以上就是认真雨为你收集整理的linux系统编程之管道(二):管道读写规则和Pipe Capacity、PIPE_BUF的全部内容,希望文章能够帮你解决linux系统编程之管道(二):管道读写规则和Pipe Capacity、PIPE_BUF所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复