官方参考手册
https://manpages.courier-mta.org/htmlman7/pipe.7.html
示例代码
// O_NOATIME 宏定义的扩展要求
#define _GNU_SOURCE 1
#include <fcntl.h>
#undef _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/unistd.h>
#include <assert.h>
#include <limits.h>
static int pip[2];
void
sleep_select(int sec)
{
struct timeval timeout;
timeout.tv_usec = 0;
timeout.tv_sec = sec;
select(0, NULL, NULL, NULL, &timeout);
}
static void
*thread_read(void *userdata)
{
char data[5] = {0};
int ret;
printf("p0: %d\n", pip[0]);
printf("p1: %d\n", pip[1]);
while (1)
{
fd_set fds;
FD_ZERO(&fds);
FD_SET(pip[0], &fds);
struct timeval timeout = {10, 0};
ret = select(pip[0] + 1, &fds, NULL, NULL, &timeout);
if (ret < 0)
{
printf("select error\n");
break;
}
else if (ret == 0)
{
// printf("no fd ready\n");
continue;
}
else
{
if (FD_ISSET(pip[0], &fds) > 0)
{
read(pip[0], data, 5);
printf("data: %s\n", data);
}
}
}
}
static void *
thread_write(void)
{
printf("thread_write\n");
char data[] = "pipe-data";
while (1)
{
sleep_select(1);
write(pip[1], data, sizeof(data));
/* 单次写入的数据长度超过 PIPE_BUF 时, 内核不保证写入的原子性*/
if (sizeof(data) > PIPE_BUF)
{
printf("warn data is too long\n");
}
}
}
int main(int argc, char *argv[])
{
char data[] = "data";
pthread_t id;
if (pipe(pip))
{
printf("pipe error\n");
exit(-1);
}
printf("p0: %d\n", pip[0]);
printf("p1: %d\n", pip[1]);
fcntl(pip[0], F_SETFL, O_NOATIME); /* 提高性能 */
fcntl(pip[1], F_SETFL, O_NOATIME);
if (pthread_create(&id, NULL, thread_read, NULL))
{
printf("create thread error\n");
exit(-1);
}
printf("pthread id = %x\n", id);
pthread_create(&id, NULL, thread_write, NULL);
// signal(SIGUSR1, mysignal);
while (1)
{
sleep_select(2);
}
pthread_join(id, NULL);
}
基本介绍
无名管道(4):通过管道同步进程
通过管道同步进程
管道其实就是一个文件
PIPE通信应用
通过管道同步进程
管道自带同步互斥机制:
管道的内核实现:fs/pipe.h
通过内核的锁、等待队列等机制实现
Write操作可能会阻塞进程
当内存缓冲区已满被读进程锁定
直到所有数据被写入到管道为止
Read操作进程可能会阻塞进程
读进程可以休眠在等待队列
直到所有子进程都关闭了管道的写入端描述符为止
父进程的写入端描述符也要关闭,否则父进程读管道时也会被阻塞
只有当所有的写端描述符都已关闭,且管道中的数据都被读出,对读端描述符调用read函数才会返回0(即读到EOF标志)
当所有的读取端和写入端都关闭,管道才能被销毁
管道缓冲区设置
管道缓冲区
管道对应的内存缓冲区大小
PIPE_BUF的容量是有限的:默认是65536字节
在不同OS下PIPE_BUF大小设置不同:在limits.h头文件中定义
写入管道的数据超过PIPE_BUF大小,内核会分割几块传输
最大值/proc/sys/fs/pipe-maxsize
查看打开的管道文件:cat /proc/PID/fd
设置缓冲区大小
特权用户:可以修改上限值
设置缓冲区大小:fcntl(fd, F_SETPIPE, size)
管道的原子性
一个管道的容量是有限的。POSIX规定,少于 PIPE_BUF 的写操作必须原子完成:要写的数据应被连续的写到管道;大于 PIPE_BUF 的写操作可能是非原子的: 内核可能会把此数据与其它进程的对此管道的写操作交替起来。POSIX规定PIPE_BUF至少为512B(linux中为 4096B),具体的语义如下: 其中n为要写的字节数
n <= PIPE_BUF, O_NONBLOCK无效:原子的写入n个字节。如果管道当前的剩余空间不足以立即写入n个字节,就阻塞直到有足够的空间。
n <= PIPE_BUF, O_NONBLOCK有效:写入具有原子性,如果有足够的空间写入n个字节,write立即成功返回。否则一个都不写入,返回错误,并设置errno为EAGAIN。
n > PIPE_BUF, O_NONBLOCK无效:非原子写。可能会和其它的写进程交替写。write阻塞直到将n个字节写入管道。
n > PIPE_BUF, O_NONBLOCK有效:如果管道满,则write失败,返回错误,并将errno设置为 EAGIN。如果不满,则返回写入的字节数为1~n,即部分写入,写入时可能有其他进程穿插写入。
结论:
1、当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。
2、当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。