在Linux系统编程中,管道是一种常用的进程间通信方式。它可以实现父子进程之间或者兄弟进程之间的数据传输。本文将介绍如何使用管道在Linux系统中进行进程通信,并给出相应的代码示例。
管道是一种特殊的文件,它提供了一个缓冲区用于进程间的数据传输。管道可以分为两种类型:匿名管道和命名管道。
在本文中,我们将重点介绍匿名管道的使用。
在Linux系统中,可以使用pipe
函数来创建一个管道。pipe
函数的原型如下:
int pipe(int pipefd[2]);
pipefd
是一个整型数组,用于存储管道的读写文件描述符。pipefd[0]
用于读取管道中的数据,pipefd[1]
用于写入管道中的数据。
下面是一个简单的示例代码,演示了如何使用管道进行父子进程之间的通信:
#include
#include
#include
#include
int main() {
int pipefd[2];
pid_t pid;
char buf[1024];
// 创建管道
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
// 创建子进程
pid = fork();
if (pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (pid == 0) {
// 子进程写入数据到管道
close(pipefd[0]); // 关闭读取端
char* msg = "Hello, parent!";
write(pipefd[1], msg, strlen(msg) + 1);
close(pipefd[1]); // 关闭写入端
exit(EXIT_SUCCESS);
} else {
// 父进程读取管道中的数据
close(pipefd[1]); // 关闭写入端
read(pipefd[0], buf, sizeof(buf));
printf("Received message from child: %s\n", buf);
close(pipefd[0]); // 关闭读取端
exit(EXIT_SUCCESS);
}
}
在上述代码中,首先使用pipe
函数创建了一个管道。然后使用fork
函数创建了一个子进程。子进程使用write
函数将数据写入管道,父进程使用read
函数从管道中读取数据。
父进程创建管道,并创建子进程后,父进程通过管道向子进程发送数据,子进程通过管道接收父进程发送的数据。
下面是一个示例代码,演示了父子进程之间使用管道进行通信的过程:
#include
#include
#include
#include
int main() {
int pipefd[2];
pid_t pid;
char buf[1024];
// 创建管道
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
// 创建子进程
pid = fork();
if (pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (pid == 0) {
// 子进程读取管道中的数据
close(pipefd[1]); // 关闭写入端
read(pipefd[0], buf, sizeof(buf));
printf("Received message from parent: %s\n", buf);
close(pipefd[0]); // 关闭读取端
exit(EXIT_SUCCESS);
} else {
// 父进程写入数据到管道
close(pipefd[0]); // 关闭读取端
char* msg = "Hello, child!";
write(pipefd[1], msg, strlen(msg) + 1);
close(pipefd[1]); // 关闭写入端
exit(EXIT_SUCCESS);
}
}
在上述代码中,首先使用pipe
函数创建了一个管道。然后使用fork
函数创建了一个子进程。子进程使用read
函数从管道中读取数据,父进程使用write
函数将数据写入管道。
要实现兄弟进程之间的通信,可以使用命名管道(named pipe)或者共享内存(shared memory)来实现。
使用命名管道(named pipe):
mkfifo
函数创建命名管道,使用open
函数打开管道进行读写操作。使用共享内存(shared memory):
shmget
函数创建共享内存,使用shmat
函数将共享内存附加到进程的地址空间中进行读写操作。下面是一个使用命名管道的示例代码,演示了兄弟进程之间的通信过程:
#include
#include
#include
#include
#include
#include
int main() {
pid_t pid;
char buf[1024];
const char* fifoName = "/tmp/myfifo";
// 创建命名管道
mkfifo(fifoName, 0666);
// 创建子进程
pid = fork();
if (pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (pid == 0) {
// 子进程从命名管道中读取数据
int fd = open(fifoName, O_RDONLY);
read(fd, buf, sizeof(buf));
printf("Received message from sibling: %s\n", buf);
close(fd);
exit(EXIT_SUCCESS);
} else {
// 父进程向命名管道中写入数据
int fd = open(fifoName, O_WRONLY);
char* msg = "Hello, sibling!";
write(fd, msg, strlen(msg) + 1);
close(fd);
exit(EXIT_SUCCESS);
}
}
在上述代码中,首先使用mkfifo
函数创建了一个命名管道。然后使用fork
函数创建了一个子进程。子进程使用open
函数打开命名管道并从中读取数据,父进程使用open
函数打开命名管道并向其中写入数据。
下面是一个使用mkfifo
和open
函数的示例代码,演示了兄弟进程之间的通信过程:
#include
#include
#include
#include
#include
#include
int main() {
pid_t pid;
char buf[1024];
const char* fifoName = "/tmp/myfifo";
// 创建命名管道
mkfifo(fifoName, 0666);
// 创建子进程
pid = fork();
if (pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (pid == 0) {
// 子进程从命名管道中读取数据
int fd = open(fifoName, O_RDONLY);
read(fd, buf, sizeof(buf));
printf("Received message from sibling: %s\n", buf);
close(fd);
exit(EXIT_SUCCESS);
} else {
// 父进程向命名管道中写入数据
int fd = open(fifoName, O_WRONLY);
char* msg = "Hello, sibling!";
write(fd, msg, strlen(msg) + 1);
close(fd);
exit(EXIT_SUCCESS);
}
}
在上述代码中,首先使用mkfifo
函数创建了一个命名管道。然后使用fork
函数创建了一个子进程。子进程使用open
函数打开命名管道并从中读取数据,父进程使用open
函数打开命名管道并向其中写入数据。
下面是一个使用命名管道实现非血缘关系进程间通信的示例代码:
#include
#include
#include
#include
#include
#include
int main() {
pid_t pid;
char buf[1024];
const char* fifoName = "/tmp/myfifo";
// 创建命名管道
mkfifo(fifoName, 0666);
// 创建子进程
pid = fork();
if (pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (pid == 0) {
// 子进程向命名管道中写入数据
int fd = open(fifoName, O_WRONLY);
char* msg = "Hello, sibling!";
write(fd, msg, strlen(msg) + 1);
close(fd);
exit(EXIT_SUCCESS);
} else {
// 父进程从命名管道中读取数据
int fd = open(fifoName, O_RDONLY);
read(fd, buf, sizeof(buf));
printf("Received message from sibling: %s\n", buf);
close(fd);
exit(EXIT_SUCCESS);
}
}
在上述代码中,首先使用mkfifo
函数创建了一个命名管道。然后使用fork
函数创建了一个子进程。子进程使用open
函数打开命名管道并向其中写入数据,父进程使用open
函数打开命名管道并从中读取数据。
管道作为一种进程间通信方式,具有以下特性和限制:
fifo
函数:在C标准库中没有名为fifo
的函数。
命名管道(FIFO):命名管道是一种特殊的文件,可以在文件系统中创建,并且可以被不同的进程打开和读写。使用mkfifo
函数可以创建命名管道。
兄弟进程间通信:兄弟进程是指由同一个父进程创建的多个子进程。兄弟进程间通信可以使用命名管道实现,其中一个进程向命名管道写入数据,另一个进程从命名管道读取数据。
非血缘关系进程间通信:非血缘关系的进程是指没有共同的父进程的进程。非血缘关系进程间通信同样可以使用命名管道实现,其中一个进程向命名管道写入数据,另一个进程从命名管道读取数据。