- 专栏内容:linux下并发编程
- 个人主页:我的主页
- 座右铭:天行健,君子以自强不息;地势坤,君子以厚德载物.
目录
前言
概述
原理介绍
接口说明
pipe 与 fifo的区别
代码演示
结尾
前言
本专栏主要分享linux下并发编程相关知识,包括多进程,多线程,进程/线程间通信,并发同步控制,以及高并发下性能提升,请大家多多留言。
概述
管道是在进程间进行通信的一种方式。在不同进程和程序间如何传递少量数据,且能阻塞形式使用,这就用到了管道。管道分为两种,无名管道和命名管道,因为子进程可以复制父进程的进程上下文,所以无名管道用在父子进程间,而命名管道在不同进程间通过路径名进行共享,更加通用。
原理介绍
- 基本原理
无名管道是一种在内存中创建的管道,只能在相关进程间使用。它提供一种字节流的通信方式,内容没有边界,所以调用者来控制读写频次。
一个无名管道是由两个文件描述符表示的,它们都是整数值。一个文件描述符用于读取管道的内容,另一个用于写入管道的内容。如果一个进程向管道写入数据,那么数据就会被暂存到管道中,等待另一个进程来读取。
使用无名管道进行进程间通信非常方便,使用简单的 read 和 write 系统调用就可以完成,不过需要注意管道大小的限制,一般情况下我们要先创建一个缓存区来存储管道中的内容以及控制读写操作的并发性, 在使用时需要注意管道的关闭和错误处理问题。
总的来说,无名管道是一种非常实用的进程间通信方式,能够帮助我们完成简单且高效的进程间信息交换。
- Pipe大小限制
Linux的管道实际上是由缓冲区组成的,管道缓冲区的大小是由内核参数决定的,并没有确定的限制。在内核版本2.6.35以前,管道的缓冲区大小默认是4KB;从2.6.35开始,缓冲区大小被改为了16KB,这是因为在对大型文件进行复制时,较大的缓冲区可以减少CPU使用率,提高复制速度。
pipe大小可以通过proc下的参数进行查询:
/proc/sys/fs/pipe-max-pages (only in Linux 2.6.34)
/proc/sys/fs/pipe-max-size (since Linux 2.6.35)
/proc/sys/fs/pipe-user-pages-hard (since Linux 4.5)
/proc/sys/fs/pipe-user-pages-soft (since Linux 4.5)
设置pipe大小可以用 fcntl的F_GETPIPE_SZ 和F_SETPIPE_SZ来获取和设置。
- 非阻塞模式
pipe默认是阻塞模式,写端写入数据,要等读端来读。当然也可以设置为非阻塞模式,通过fcntl的F_SETFL 操作设置参数为 O_NONBLOCK。此时写端直到写满缓冲区时就会失败,其它时候都可以立即返回,读端每次都会立即返回。
接口说明
(1)创建管道
#include <unistd.h>
int pipe(int pipefd[2]);
这里会创建一个pipefd的数组,其中pipefd[0]是读端,只能读数据,pipefd[1]是写端,只能用于写数据。
在子进程中,创建后会关闭一端,一个进程只保留一个端(写或读)。
还可以用下面的接口
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <fcntl.h> /* Obtain O_* constant definitions */
#include <unistd.h>
int pipe2(int pipefd[2], int flags);
flag可以为:
O_CLOEXEC ,这个参数非常有用,当进程调用exec系列接口时,句柄就会自动关闭回收。否则就有资源泄漏。
O_NONBLOCK,非阻塞模式;
(2)管道的读/写
读/写调用 read/write即可
(3)管道的关闭
close(fd)
- 注意事项
(1)当所有进程关闭了写端时,读端读时就会返回0;当所有进程都关闭了读端时,写端写时就会返回错误,错误码为EPIPE;
(2)关联进程,需要关闭不用的写端或读端,这样才能确保有正确的 SIGPIPE/EPIPE,或是读返回0,收到对端关闭的信息;
pipe 与 fifo的区别
它们的创建和打开方式不同,读写操作是相似的。它们都是通过共享缓冲区,虽然fifo有创建管道文件,并不会与磁盘交互;这两种方式仅是提供缓冲区的读写端。
代码演示
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main()
{
int fd[2];
pid_t pid1, pid2;
if (pipe(fd) == -1)
{
fprintf(stderr, "Pipe failed");
return 1;
}
pid1 = fork();
if (pid1 < 0)
{
fprintf(stderr, "Fork failed");
return 1;
}
if (pid1 == 0)
{
// Child process 1
char message[] = "Hello from child process 1!\n";
close(fd[0]);
write(fd[1], message, strlen(message));
close(fd[1]);
exit(0);
}
else
{
pid2 = fork();
if (pid2 < 0)
{
fprintf(stderr, "Fork failed");
return 1;
}
if (pid2 == 0)
{
// Child process 2
char message[] = "Hello from child process 2!\n";
close(fd[0]);
write(fd[1], message, strlen(message));
close(fd[1]);
exit(0);
}
else
{
// Parent process
close(fd[1]);
char buffer[100];
read(fd[0], buffer, 100);
printf("%s", buffer);
read(fd[0], buffer, 100);
printf("%s", buffer);
close(fd[0]);
}
}
return 0;
}
运行结果
[senllang@localhost pipetest]$ gcc pipetest.c -o pipetest
[senllang@localhost pipetest]$ ./pipetest
Hello from child process 1!
Hello from child process 2!
结尾
作者邮箱:study@senllang.onaliyun.com
如有错误或者疏漏欢迎指出,互相学习。另外有什么想要了解的内容,也可以给我发邮件,互相谈讨,定知无不言。
注:未经同意,不得转载!