面试题答案
一键面试- 匿名管道概述:
- 匿名管道是一种半双工的通信方式,数据只能单向流动,常用于具有亲缘关系(如父子进程)的进程间通信。
- 系统调用及作用:
- pipe函数:
- 函数原型:
int pipe(int pipefd[2]);
- 作用:创建一个匿名管道。
pipefd
是一个由两个文件描述符组成的数组,pipefd[0]
用于从管道读取数据,pipefd[1]
用于向管道写入数据。若成功,返回0;若失败,返回 -1并设置errno
。
- 函数原型:
- fork函数:
- 函数原型:
pid_t fork(void);
- 作用:用于创建一个新进程,新进程(子进程)几乎是当前进程(父进程)的一个副本。通常在
pipe
调用之后使用fork
,这样父子进程就可以通过之前创建的管道进行通信。父进程和子进程会共享管道的文件描述符。返回值在父进程中是子进程的进程ID,在子进程中是0,若失败返回 -1 并设置errno
。
- 函数原型:
- read函数:
- 函数原型:
ssize_t read(int fd, void *buf, size_t count);
- 作用:从文件描述符
fd
(这里是pipefd[0]
)中读取最多count
字节的数据到缓冲区buf
中。返回值为实际读取的字节数,若为0表示到达文件末尾(管道中没有数据且写端已关闭),若为 -1表示出错并设置errno
。
- 函数原型:
- write函数:
- 函数原型:
ssize_t write(int fd, const void *buf, size_t count);
- 作用:将缓冲区
buf
中count
字节的数据写入到文件描述符fd
(这里是pipefd[1]
)。返回值为实际写入的字节数,若为 -1表示出错并设置errno
。
- 函数原型:
- pipe函数:
- 实现步骤示例(以父子进程为例):
- 父进程调用
pipe
创建管道。 - 父进程调用
fork
创建子进程。 - 父子进程根据需求关闭不需要的管道文件描述符(例如父进程关闭读端
pipefd[0]
,子进程关闭写端pipefd[1]
,当然情况也可相反)。 - 关闭不需要的文件描述符后,就可以在父子进程间通过
write
和read
函数进行数据传递。例如父进程向管道写端写入数据,子进程从管道读端读取数据。
- 父进程调用
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#define BUFFER_SIZE 256
int main() {
int pipefd[2];
pid_t cpid;
char buf[BUFFER_SIZE];
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
cpid = fork();
if (cpid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (cpid == 0) { // 子进程
close(pipefd[1]); // 关闭写端
ssize_t numRead = read(pipefd[0], buf, BUFFER_SIZE);
if (numRead == -1) {
perror("read");
exit(EXIT_FAILURE);
}
printf("子进程读取到: %.*s\n", (int)numRead, buf);
close(pipefd[0]);
exit(EXIT_SUCCESS);
} else { // 父进程
close(pipefd[0]); // 关闭读端
const char *msg = "Hello, child!";
ssize_t numWritten = write(pipefd[1], msg, strlen(msg));
if (numWritten == -1) {
perror("write");
exit(EXIT_FAILURE);
}
close(pipefd[1]);
wait(NULL);
exit(EXIT_SUCCESS);
}
}
上述代码展示了父子进程通过匿名管道进行简单数据传递的过程。