Linux进程编程(2)进程间管道通信(无名管道)
什么是无名管道
管道(Pipe)是一种用于进程间通信的机制,在Unix和类Unix系统中广泛使用。它允许一个进程将数据写入到管道中,而另一个进程则可以从管道中读取这些数据。管道通常用于在父进程和子进程之间传递数据。
管道有两种类型:有名管道(Named Pipe)和无名管道(Unnamed Pipe)。在这里,我们主要讨论无名管道,因为在本篇文章中使用的就是无名管道。
无名管道是一种匿名的、单向的通信通道。它只能在有亲缘关系(父子关系)的进程之间使用,因为无名管道在创建时并没有名字,只存在于相应的进程内部。无名管道具有以下特点:
-
单向通信:无名管道是单向的,它只能实现单向的数据传输。其中一个进程用于写入数据,另一个进程用于读取数据。
-
基于文件描述符:无名管道在创建时返回两个文件描述符。一个用于读取数据(读取端),另一个用于写入数据(写入端)。这些文件描述符可以用于像操作文件一样进行数据读写。
-
先进先出:无名管道是一个先进先出(FIFO)的数据结构。先写入的数据先被读取,确保数据按照写入顺序读取。
-
有限大小:无名管道有限制其缓冲区的大小,这意味着一旦写入的数据超过了管道的容量,写入操作将被阻塞,直到有足够空间接收数据。
在本文章中,我们使用pipe()
函数创建了一个无名管道,并在父进程和子进程之间通过管道进行了数据传递。子进程向管道写入了一条消息,父进程从管道中读取并打印出收到的消息。这样实现了简单的进程间通信。
一、用到的函数
1. int pipe(int pipefd[2]);
• 返回值:若成功,返回0;若失败,返回-1。
• 作用:pipe()函数用于创建一个无名管道,通过pipefd数组返回两个文件描述符,pipefd[0]用于读取数据,pipefd[1]用于写入数据。
• 参数:pipefd是一个int类型的数组,用于存储管道的读写文件描述符。调用时直接传入该数组即可。
2. pid_t fork(void);
• 返回值:若成功,返回子进程的PID(在父进程中),返回0(在子进程中);若失败,返回-1。
• 作用:fork()函数用于创建一个新进程,新进程是当前进程(父进程)的副本,成为子进程。子进程从fork()函数返回的值为0,父进程从fork()函数返回子进程的PID。
• 参数:fork()函数没有参数,调用时直接使用即可。
3. int close(int fd);
• 返回值:若成功,返回0;若失败,返回-1。
• 作用:close()函数用于关闭一个文件描述符。在本代码中,用于关闭管道的读取端和写入端,以确保进程之间的通信顺利进行。
• 参数:fd是一个int类型的文件描述符,表示要关闭的文件或管道的描述符。在本代码中,我们需要分别关闭读取端和写入端,所以在适当的位置调用close()函数,并传入相应的文件描述符。
4. ssize_t write(int fd, const void *buf, size_t count);
• 返回值:若成功,返回写入的字节数;若失败,返回-1。
• 作用:write()函数用于将数据从指定缓冲区buf写入文件描述符fd对应的文件或管道。在本代码中,用于子进程向管道写入数据。
• 参数:fd是一个int类型的文件描述符,表示要写入的文件或管道的描述符。buf是一个指向要写入数据的缓冲区。count是要写入的字节数。
5. ssize_t read(int fd, void *buf, size_t count);
• 返回值:若成功,返回读取的字节数;若到达文件末尾,返回0;若失败,返回-1。
• 作用:read()函数用于从文件描述符fd对应的文件或管道中读取数据到指定缓冲区buf。在本代码中,用于父进程从管道读取数据。
• 参数:fd是一个int类型的文件描述符,表示要读取的文件或管道的描述符。`
二、无名管道的实现
1.代码
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main() {
int pipefd[2]; // 创建一个数组来存储管道的读写文件描述符
char buffer[20]; // 创建一个缓冲区用于存储读取的数据
// 创建管道,pipefd[0]用于读取,pipefd[1]用于写入
pipe(pipefd);
pid_t pid = fork(); // 创建一个子进程
if (pid < 0) {
// fork失败
fprintf(stderr, "进程创建失败\n");
return 1;
}
else if (pid == 0)
{
// 子进程
close(pipefd[0]); // 关闭子进程的读取端,因为子进程只写入数据
write(pipefd[1], "Hello from child!", 17); // 向管道写入数据
close(pipefd[1]); // 关闭写入端,表示写入完成
}
else
{
// 父进程
close(pipefd[1]); // 关闭父进程的写入端,因为父进程只读取数据
read(pipefd[0], buffer, sizeof(buffer)); // 从管道读取数据到缓冲区
printf("收到消息:%s\n", buffer); // 打印收到的消息
close(pipefd[0]); // 关闭读取端,表示读取完成
wait(NULL); // 父进程等待子进程结束
}
return 0;
}
2.运行结果
收到消息: Hello from child!