
Linux进程间通信(Inter-Process Communication,IPC)是指不同进程间进行数据交换和信息传递的机制。它允许多个进程共享资源、同步执行、模块化设计及数据传递。常见的IPC方式包括管道、消息队列、共享内存、信号量、套接字等。这些方式各有特点,适用于不同的应用场景,是实现进程间高效协作的关键。本篇主要学习管道部分。

在 Linux 系统中,管道本质上是一种特殊的文件,它在内存中开辟了一段缓存空间来存储数据,遵循先进先出(FIFO)的原则,就像一个队列,先写入管道的数据会先被读出。
①创建管道:在Linux中,可以使用pipe()系统调用来创建一个管道。pipe()函数接受一个指向int类型数组的指针作为参数,该数组将包含两个文件描述符:一个用于读(fd[0]),另一个用于写(fd[1])。
②数据传递:
③同步与阻塞:
④生命周期:管道的生命周期随进程而存在。当所有使用管道的文件描述符都被关闭时,管道将被销毁。
⑤管道关闭:当进程不再需要使用管道时,应该关闭相应的文件描述符。当所有指向管道的文件描述符都被关闭后,管道所占用的内核资源会被释放。
⑥使用示例:在命令行中,可以使用管道符号|将多个命令连接起来,使得一个命令的输出直接作为另一个命令的输入。例如,ls | grep "txt"命令会列出当前目录下的所有文件,并通过grep命令筛选出包含"txt"字符串的文件名。
pipe()系统调用创建匿名管道。pipe()函数的原型为:
int pipe(int pipefd[2]);pipefd数组中,pipefd[0]指向管道的读端,pipefd[1]指向管道的写端。pipe()函数返回 0;errno以指示错误原因。下面是一个简单的父子进程通过匿名管道通信的示例代码:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main() {
int pipe_fd[2];
pid_t pid;
char buffer[1024];
// 创建匿名管道
if (pipe(pipe_fd) == -1) {
perror("pipe");
return 1;
}
// 创建子进程
pid = fork();
if (pid == -1) {
perror("fork");
return 1;
} else if (pid == 0) {
// 子进程关闭读端
close(pipe_fd[0]);
// 向管道写端写入数据
write(pipe_fd[1], "Hello, parent!", 14);
// 关闭写端
close(pipe_fd[1]);
} else {
// 父进程关闭写端
close(pipe_fd[1]);
// 从管道读端读取数据
ssize_t bytes_read = read(pipe_fd[0], buffer, sizeof(buffer));
if (bytes_read > 0) {
buffer[bytes_read] = '\0';
printf("Parent received: %s\n", buffer);
}
// 关闭读端
close(pipe_fd[0]);
}
return 0;
}首先通过pipe()创建匿名管道,然后使用fork()创建子进程。子进程关闭读端,向写端写入数据;父进程关闭写端,从读端读取数据。通过这种方式,实现了父子进程之间的通信。
mkfifo()函数创建命名管道。mkfifo()函数原型为:
int mkfifo(const char *pathname, mode_t mode);其中pathname是命名管道的路径名,mode指定管道的权限。创建成功时返回 0,失败返回 - 1。
以下是一个使用命名管道进行通信的示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#define FIFO_NAME "my_fifo"
int main() {
int fd;
char buffer[1024];
// 创建命名管道
if (mkfifo(FIFO_NAME, 0666) == -1 && errno!= EEXIST) {
perror("mkfifo");
return 1;
}
// 打开命名管道进行读操作
fd = open(FIFO_NAME, O_RDONLY);
if (fd == -1) {
perror("open");
return 1;
}
// 从命名管道读取数据
ssize_t bytes_read = read(fd, buffer, sizeof(buffer));
if (bytes_read > 0) {
buffer[bytes_read] = '\0';
printf("Received: %s\n", buffer);
}
// 关闭文件描述符
close(fd);
// 删除命名管道
if (unlink(FIFO_NAME) == -1) {
perror("unlink");
return 1;
}
return 0;
}先使用mkfifo()创建名为my_fifo的命名管道,然后使用open()打开管道进行读操作,从管道中读取数据并打印,最后使用unlink()删除命名管道。
①读规则
②写规则
①读规则
②写规则
pipe()函数就可以轻松创建一个匿名管道,进程可以方便地通过管道进行数据传输,对于简单的进程间通信场景,能够快速实现数据的传递和共享。
综上所述,在嵌入式Linux应用开发中,管道作为进程间通信的基础方式意义重大。它分为匿名管道和命名管道,前者用于亲缘进程,创建简单,通过`pipe()`函数实现;后者突破进程关系限制,在文件系统以特殊文件存在,用`mkfifo()`创建。管道读写遵循特定规则,虽有半双工、缓存有限等不足,但胜在简单高效,是实现进程间数据传递的重要工具。
答:
pipe()系统调用创建,返回两个文件描述符:fd[0](读端)和fd[1](写端)。
mkfifo命令或mkfifo()函数创建,存在于文件系统中(如/tmp/myfifo)。
unlink())或随系统重启消失。
// 匿名管道示例
int fd[2];
pipe(fd); // 创建管道
if (fork() == 0) {
close(fd[0]); // 子进程关闭读端
write(fd[1], "Hello", 6);
} else {
close(fd[1]); // 父进程关闭写端
char buf[6];
read(fd[0], buf, 6);
}答:
阻塞场景:
write())会阻塞。
SIGPIPE信号(默认终止进程)。
解决方案:
使用fcntl()设置非阻塞模式:
fcntl(fd, F_SETFL, O_NONBLOCK);多路复用(如select()/poll())监控管道状态。
确保读端及时读取数据。
答:
fcntl(fd, F_GETPIPE_SZ)获取缓冲区大小(默认通常为64KB)。
write()会等待直到有空间。
write()返回EAGAIN错误。
write()数据量 ≤ PIPE_BUF(通常4KB)时,操作是原子性的;否则数据可能被分割。
open()会阻塞,直到读端打开。
O_NONBLOCK标志避免阻塞:
int fd = open("/tmp/myfifo", O_WRONLY | O_NONBLOCK);SIGPIPE信号。
场景:读端已关闭,写端继续写入。
答:
write()会返回EPIPE错误,并触发SIGPIPE信号(默认终止进程)。
解决方案:
忽略SIGPIPE信号:
signal(SIGPIPE, SIG_IGN);检查write()返回值,处理EPIPE错误:
if (write(fd, buf, len) == -1) {
if (errno == EPIPE) {
// 处理管道破裂
}
}答:
PIPE_BUF)。
PIPE_BUF大小限制保证原子性。
mkfifo /tmp/myfifo
cat /tmp/myfifo & # 后台启动读端
echo "Hello" > /tmp/myfifoman命令,如man pipe、man mkfifo查看相关内容;也可访问man7.org在线查看。