结论:
一、当管道内没有数据可读时
O_NONBLOCK disable:read 调用阻塞,直到有可读数据
O_NONBLOCK enable: read 调用返回 -1,errno 值为 EAGAIN
二、当管道已满或者剩余空间不够时
O_NONBLOCK disable:write 调用阻塞,直到有进程读走数据,管道中有足够大的可用空间
O_NONBLOCK enable: write 调用返回 -1,errno 值为 EAGAIN
匿名管道默认是阻塞模式,可通过以下示例修改为非阻塞模式
int flags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
命名管道的阻塞模式和非阻塞模式设定参见以下示例
open("fifo", flags | O_NONBLOCK); //非阻塞
open("fifo", flags); //阻塞
三、
如果所有管道写端对应的文件描述符被关闭,则 read 调用返回 0
如果所有管道读端对应的文件描述符被关闭,则 write 调用会产生信号 SIGPIPE,进而可能导致写进程退出
四、man 7 pipe
1、当要写入的数据量 n <= PIPE_BUF 时,linux 将保证写入的原子性
O_NONBLOCK disable:如果管道中有足够大的可用空间便写入;否则 write 调用阻塞,直到管道中有足够大的可用空间
O_NONBLOCK enable: 如果管道中有足够大的可用空间便写入;否则 write 调用返回 -1,errno 值为 EAGAIN
2、当要写入的数据量 n > PIPE_BUF 时,linux 将不再保证写入的原子性
O_NONBLOCK disable:write 直到将所有数据写入管道后返回,期间可能有其他进程穿插写入
O_NONBLOCK enable: 如果管道已满,write 调用返回 -1,errno 值为 EAGAIN;否则实际写入的数据量为 1 ~ n,即部分写入,期间可能有其他进程穿插写入
3、读数据不会因为读取数据量 n 是否大于 PIPE_BUF 失去原子性
五、综合以上一、二和四1、3,内核会对管道(匿名/命名管道)进行同步与互斥
验证如下:
1、O_NONBLOCK disable:read 调用阻塞
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int fds[2];
pid_t pid;
time_t t;
char buf[10] = {0};
if(0 != pipe(fds))
{
perror("pipe error");
exit(EXIT_FAILURE);
}
pid = fork();
if (-1 == pid)
{
perror("fork error");
exit(EXIT_FAILURE);
}
if (0 == pid)
{
close(fds[0]); //关闭读端
sleep(10);
write(fds[1], "hello", 5);
exit(EXIT_SUCCESS);
}
close(fds[1]); //关闭写端
t = time(NULL);
read(fds[0], buf, sizeof(buf));
t = time(NULL) - t;
printf("receive data: %s, interval time: %ds\n", buf, t);
waitpid(pid, NULL, 0);
return 0;
}
/*
* receive data: hello, interval time: 10s
*/
2、O_NONBLOCK enable:read 调用返回 -1,errno 值为 EAGAIN(11)
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
int main(void)
{
int fds[2], flags;
pid_t pid;
char buf[10] = {0};
if(0 != pipe(fds))
{
perror("pipe error");
exit(EXIT_FAILURE);
}
pid = fork();
if (-1 == pid)
{
perror("fork error");
exit(EXIT_FAILURE);
}
if (0 == pid)
{
close(fds[0]);
sleep(10);
write(fds[1], "hello", 5);
exit(EXIT_SUCCESS);
}
close(fds[1]);
flags = fcntl(fds[0], F_GETFL);
fcntl(fds[0], F_SETFL, flags | O_NONBLOCK);
if (-1 == read(fds[0], buf, sizeof(buf)))
{
perror("read error");
printf("errno: %d\n", errno);
waitpid(pid, NULL, 0);
exit(EXIT_FAILURE);
}
printf("receive data: %s\n", buf);
waitpid(pid, NULL, 0);
return 0;
}
/*
* read error: Resource temporarily unavailable
* errno: 11
*/
3、O_NONBLOCK disable:write 调用阻塞
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
int main(void)
{
int fds[2];
char buf[4096];
int count = 0;
if(0 != pipe(fds))
{
perror("pipe error");
exit(EXIT_FAILURE);
}
memset(buf, 'A', sizeof(buf));
while(1)
{
count += write(fds[1], buf, sizeof(buf));
printf("writed data length: %d\n", count);
}
return 0;
}
/*
writed data length: 4096
writed data length: 8192
writed data length: 12288
writed data length: 16384
writed data length: 20480
writed data length: 24576
writed data length: 28672
writed data length: 32768
writed data length: 36864
writed data length: 40960
writed data length: 45056
writed data length: 49152
writed data length: 53248
writed data length: 57344
writed data length: 61440
writed data length: 65536
阻塞卡住
*/
4、O_NONBLOCK enable: write 调用返回 -1,errno 值为 EAGAIN
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
int main(void)
{
int fds[2], flags;
char buf[4096];
int ret, count = 0;
if(0 != pipe(fds))
{
perror("pipe error");
exit(EXIT_FAILURE);
}
flags = fcntl(fds[1], F_GETFL);
fcntl(fds[1], F_SETFL, flags | O_NONBLOCK);
memset(buf, 'A', sizeof(buf));
while(1)
{
ret = write(fds[1], buf, sizeof(buf));
if (-1 == ret)
{
perror("write error");
printf("errno: %d\n", errno);
exit(EXIT_FAILURE);
}
count += ret;
printf("writed data length: %d\n", count);
}
return 0;
}
/*
writed data length: 4096
writed data length: 8192
writed data length: 12288
writed data length: 16384
writed data length: 20480
writed data length: 24576
writed data length: 28672
writed data length: 32768
writed data length: 36864
writed data length: 40960
writed data length: 45056
writed data length: 49152
writed data length: 53248
writed data length: 57344
writed data length: 61440
writed data length: 65536
write error: Resource temporarily unavailable
errno: 11
*/
5、所有管道写端对应的文件描述符被关闭,read 调用返回 0
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int fds[2];
char buf[10] = {0};
int ret;
if(0 != pipe(fds))
{
perror("pipe error");
exit(EXIT_FAILURE);
}
close(fds[1]); //此处用单进程验证
ret = read(fds[0], buf, sizeof(buf));
printf("receive data len: %d\n", ret);
return 0;
}
/*
* receive data len: 0
*/
6、所有管道读端对应的文件描述符被关闭,write 调用会产生信号 SIGPIPE(13,kill -l)
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
void sighandler(int signo)
{
printf("catch a signal, signum: %d\n", signo);
}
int main(void)
{
int fds[2];
char buf[10] = {0};
int ret;
if(SIG_ERR == signal(SIGPIPE, sighandler))
{
perror("signal error");
exit(EXIT_FAILURE);
}
if(0 != pipe(fds))
{
perror("pipe error");
exit(EXIT_FAILURE);
}
close(fds[0]);
ret = write(fds[1], "hello", 5);
printf("write ret val: %d\n", ret);
return 0;
}
/*
* catch a signal, signum: 13
* write ret val: -1
*/
7、当要写入的数据量 n > PIPE_BUF 时,linux 将不再保证写入的原子性
O_NONBLOCK disable:write 直到将所有数据写入管道后返回,期间可能有其他进程穿插写入
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#define PIPE_SIZE 4096
#if 0
// error
#define WRITE_SIZE PIPE_SIZE + 1
#else
// ok
#define WRITE_SIZE PIPE_SIZE
#endif
#define READ_SIZE PIPE_SIZE + 1
int main(void)
{
int fds[2], ofd;
pid_t pid;
char obuf[WRITE_SIZE], c;
int len;
char ibuf[READ_SIZE] = {0};
if (0 != pipe(fds))
{
perror("pipe error");
exit(EXIT_FAILURE);
}
pid = fork();
if (-1 == pid)
{
perror("fork error");
exit(EXIT_FAILURE);
}
else if (0 == pid)
{
close(fds[0]);
for (c = 'A'; c <= 'M'; c++)
{
memset(obuf, c, WRITE_SIZE);
len = write(fds[1], obuf, WRITE_SIZE);
printf("child1 write %d bytes to pipe\n", len);
}
exit(0);
}
pid = fork();
if (-1 == pid)
{
perror("fork error");
exit(EXIT_FAILURE);
}
else if (0 == pid)
{
close(fds[0]);
for (c = 'N'; c <= 'Z'; c++)
{
memset(obuf, c, WRITE_SIZE);
len = write(fds[1], obuf, WRITE_SIZE);
printf("child2 write %d bytes to pipe\n", len);
}
exit(0);
}
close(fds[1]);
ofd = open("test.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
ibuf[4096] = '\n';
while (1)
{
sleep(1);
if (0 == (len = read(fds[0], ibuf, PIPE_SIZE)))
break;
printf("father read %d bytes from pipe\n", len);
if (PIPE_SIZE == len)
len = READ_SIZE;
if (len != write(ofd, ibuf, len))
{
printf("write error, quit\n");
exit(EXIT_FAILURE);
}
printf("father write %d bytes to file 'test.txt'\n", len);
}
return 0;
}
异常结果:
8、读数据不会因为读取数据量 n 是否大于 PIPE_BUF 失去原子性
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#define PIPE_SIZE 4096
#define WRITE_SIZE PIPE_SIZE
#if 0
#define READ_SIZE PIPE_SIZE
#else
#define READ_SIZE PIPE_SIZE + 1
#endif
int main(void)
{
int fds[2], ofd;
char obuf[WRITE_SIZE], c;
int len = 0;
pid_t pid;
char ibuf[READ_SIZE + 1] = {0};
if (0 != pipe(fds))
{
perror("pipe error");
exit(EXIT_FAILURE);
}
pid = fork();
if (-1 == pid)
{
perror("fork error");
exit(EXIT_FAILURE);
}
else if (0 == pid)
{
close(fds[1]);
ofd = open("child1.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
ibuf[READ_SIZE] = '\n';
while (1)
{
if (0 == (len = read(fds[0], ibuf, READ_SIZE)))
break;
printf("child1 read %d bytes from pipe\n", len);
if (READ_SIZE == len)
len += 1;
if (len != write(ofd, ibuf, len))
{
printf("write error, quit\n");
exit(EXIT_FAILURE);
}
printf("child1 write %d bytes to file 'child1.txt'\n", len);
}
exit(0);
}
pid = fork();
if (-1 == pid)
{
perror("fork error");
exit(EXIT_FAILURE);
}
else if (0 == pid)
{
close(fds[1]);
ofd = open("child2.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
ibuf[READ_SIZE] = '\n';
while (1)
{
if (0 == (len = read(fds[0], ibuf, READ_SIZE)))
break;
printf("child2 read %d bytes from pipe\n", len);
if (READ_SIZE == len)
len += 1;
if (len != write(ofd, ibuf, len))
{
printf("write error, quit\n");
exit(EXIT_FAILURE);
}
printf("child2 write %d bytes to file 'child2.txt'\n", len);
}
exit(0);
}
for (c = 'A'; c <= 'Z'; c++)
{
memset(obuf, c, WRITE_SIZE);
len += write(fds[1], obuf, WRITE_SIZE);
printf("father write %d bytes to pipe\n", len);
}
close(fds[1]);
waitpid(-1, NULL, 0);
waitpid(-1, NULL, 0);
return 0;
}