【进程】进程间通信----管道
发布日期:2021-05-10 06:33:24 浏览次数:17 分类:精选文章

本文共 2746 字,大约阅读时间需要 9 分钟。

管道与posix ipc mechanisms

1、管道

什么是管道?

管道是指一个进程与另一个进程之间的一种数据流连接,能够实现进程间的通信。在Unix系统中,管道是最古老的进程间通信机制之一。

2、匿名管道

2.1 匿名管道的本质

匿名管道在内核中开辟一段缓冲区,这段缓冲区没有标识符,也称为“写入缓冲区”。由于匿名管道没有标识符,难以被其他进程直接找到,因此只能在进程之间使用。

2.2 文件描述符数组(fd[2])

  • fd[0]:读端文件描述符,用于读取数据。
  • fd[1]:写端文件描述符,用于写入数据。
  • fd[2]:文件描述符数组,其真正作用是保存文件描述符。fd[0]fd[1]是被pipe函数赋予的。

2.3 匿名管道的特性

  • 只能用于亲缘关系进程间通信:管道只能在有直接或间接连接的两个进程之间传输数据。
  • 单双工通信:数据可以按字节流式发送,读端可以决定读取的数据量。
  • 提供流式服务:写端数据被立即传输至读端,读端可以选择读取的字节数和频率。
  • 管道跟随进程生命周期:当写进程退出或终止,管道即被销毁。

2.4 容量限制

  • 管道默认容量:64k字节。
  • 缓冲区大小:4k字节。
  • 由于写入字节数通常小于4k,写操作的原子性得到了保证。

2.5 匿名管道的应用示例

#include 
#include
int main() {
int fd[2];
if (pipe(fd) < 0) {
printf("pipe error\n");
return -1;
}
// 打印文件描述符
printf("fd[0]: %d\n", fd[0]);
printf("fd[1]: %d\n", fd[1]);
int child = fork();
if (child < 0) {
printf("fork error\n");
return -1;
} else if (child == 0) {
// 子进程
close(fd[0]);
if (write(fd[1], "i love you", 11) < 0) {
printf("write error\n");
return -1;
}
return 0;
} else {
// 父进程
close(fd[1]);
char buff[1024] = {0};
if (read(fd[0], buff, sizeof(buff) - 1) < 0) {
printf("read error\n");
return -1;
}
printf("%s\n", buff);
return 0;
}
}

3、int fcntl(int fd, int cmd, ...)

3.1 调用功能

int fcntl(int fd, int cmd, ...): 将cmd参数传递给fd,并执行相应操作。

  • F_GETFL:获取文件描述符属性。
  • F_SETFL:设置文件描述符属性。如需设置非阻塞属性,将flag"| O_NONBLOCK".

3.2 非阻塞操作

int flag = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, flag | O_NONBLOCK);

3.3 非阻塞属性设置说明

  • 写端非阻塞:关闭读端文件描述符。若尝试读取,进程将收到SIGPIPE信号。
  • 读端非阻塞:关闭写端文件描述符。若尝试写入,进程将收到SIGPIPE信号。

4、命名管道

4.1 命名管道的本质

命名管道也称为fuse, 它在内核中开辟一段缓存区,并附带标识符。这使得任何具有相同标识符的进程都可以找到该缓存区,无需亲缘关系。

4.2 创建命名管道

  • 命令行工具
    mkfifo filename
  • 程序中创建
    int mkfifo(const char *path, mode_t mode);

4.3 命名管道的区别

  • 匿名管道:由pipe函数创建。
  • 命名管道:由mkfifo函数创建。

4.4 命名管道的应用示例

#include 
#include
#include
int main() {
int fifo = mkfifo("./fifo_test", 0664);
if (fifo < 0) {
printf("mkfifo error\n");
return -1;
}
char buf[] = "Hello World\n";
int fd = open("./fifo_test", O_RDWR);
if (fd < 0) {
printf("open error\n");
return -1;
}
if (write(fd, buf, sizeof(buf) - 1) < 0) {
printf("write error\n");
return -1;
}
char buf2[1024] = {0};
if (read(fd, buf2, sizeof(buf2) - 1) < 0) {
printf("read error\n");
return -1;
}
printf("%s", buf2);
return 0;
}

5、管道本质

5.1 管道的内部结构

  • Linux使用file结构和VFS inode来实现管道。
  • 通过将两个file结构指向同一个inode,形成一个临时的缓冲区。

5.2 管道的读写机制

  • 写进程将数据复制到共享的物理页面上。
  • đọc进程从共享页面读取数据到自己的地址空间。
  • 读写操作会唤醒对方,确保流程切换。

5.3 管道的优化

  • POSIX标准定义了管道行为,确保不同系统间的一致性。
  • 读写端的 suspend/resume 机制优化了资源利用率。
上一篇:【Linux】进程间通信----共享内存
下一篇:【Linux】动态库和静态库

发表评论

最新留言

能坚持,总会有不一样的收获!
[***.219.124.196]2025年04月22日 07时00分47秒