面试题答案
一键面试解决读端和写端同步问题的方法
- 使用信号:
- 写端在写完数据后发送一个信号给读端,读端捕获该信号后进行读操作。例如使用
sigaction
函数来设置信号处理函数,kill
函数来发送信号。
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> #include <string.h> int pipefd[2]; void signal_handler(int signum) { char buffer[1024]; ssize_t bytes_read = read(pipefd[0], buffer, sizeof(buffer)); if (bytes_read > 0) { buffer[bytes_read] = '\0'; printf("Read from pipe: %s\n", buffer); } } int main() { if (pipe(pipefd) == -1) { perror("pipe"); exit(EXIT_FAILURE); } struct sigaction sa; sa.sa_handler = signal_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; if (sigaction(SIGUSR1, &sa, NULL) == -1) { perror("sigaction"); exit(EXIT_FAILURE); } pid_t pid = fork(); if (pid == -1) { perror("fork"); exit(EXIT_FAILURE); } else if (pid == 0) { // 子进程(写端) close(pipefd[0]); const char *message = "Hello, pipe!"; if (write(pipefd[1], message, strlen(message)) != strlen(message)) { perror("write"); exit(EXIT_FAILURE); } close(pipefd[1]); kill(getppid(), SIGUSR1); exit(EXIT_SUCCESS); } else { // 父进程(读端) close(pipefd[1]); pause(); close(pipefd[0]); wait(NULL); exit(EXIT_SUCCESS); } }
- 写端在写完数据后发送一个信号给读端,读端捕获该信号后进行读操作。例如使用
- 使用文件描述符的阻塞与非阻塞模式:
- 将读端或写端设置为非阻塞模式。例如使用
fcntl
函数设置文件描述符为非阻塞。写端在非阻塞模式下,如果管道已满,write
函数会立即返回错误,此时写端可以选择等待一段时间后重试。读端在非阻塞模式下,如果管道中无数据,read
函数也会立即返回错误,读端可以等待或执行其他任务。
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <string.h> int main() { int pipefd[2]; if (pipe(pipefd) == -1) { perror("pipe"); exit(EXIT_FAILURE); } // 设置读端为非阻塞 int flags = fcntl(pipefd[0], F_GETFL, 0); if (flags == -1) { perror("fcntl F_GETFL"); exit(EXIT_FAILURE); } if (fcntl(pipefd[0], F_SETFL, flags | O_NONBLOCK) == -1) { perror("fcntl F_SETFL"); exit(EXIT_FAILURE); } pid_t pid = fork(); if (pid == -1) { perror("fork"); exit(EXIT_FAILURE); } else if (pid == 0) { // 子进程(写端) close(pipefd[0]); const char *message = "Hello, pipe!"; ssize_t bytes_written = 0; while (bytes_written < strlen(message)) { ssize_t res = write(pipefd[1], message + bytes_written, strlen(message) - bytes_written); if (res == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { // 管道满,等待一段时间 usleep(100000); } else { perror("write"); exit(EXIT_FAILURE); } } else { bytes_written += res; } } close(pipefd[1]); exit(EXIT_SUCCESS); } else { // 父进程(读端) close(pipefd[1]); char buffer[1024]; ssize_t bytes_read; while ((bytes_read = read(pipefd[0], buffer, sizeof(buffer))) == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { // 无数据,等待一段时间 usleep(100000); } else { perror("read"); exit(EXIT_FAILURE); } } buffer[bytes_read] = '\0'; printf("Read from pipe: %s\n", buffer); close(pipefd[0]); wait(NULL); exit(EXIT_SUCCESS); } }
- 将读端或写端设置为非阻塞模式。例如使用
可能出现的错误及错误处理
pipe
函数失败:- 错误原因:系统资源不足,例如打开的文件描述符过多。
- 错误处理:在调用
pipe
函数后,通过检查返回值来判断是否成功。如果pipe
返回-1
,使用perror
函数打印错误信息并根据情况退出程序。
int pipefd[2]; if (pipe(pipefd) == -1) { perror("pipe"); exit(EXIT_FAILURE); }
fork
函数失败:- 错误原因:系统资源不足,例如进程表已满。
- 错误处理:在调用
fork
函数后,通过检查返回值来判断是否成功。如果fork
返回-1
,使用perror
函数打印错误信息并根据情况退出程序。
pid_t pid = fork(); if (pid == -1) { perror("fork"); exit(EXIT_FAILURE); }
read
函数失败:- 错误原因:读端文件描述符无效、管道被关闭、读操作被信号中断等。
- 错误处理:在调用
read
函数后,检查返回值。如果返回-1
,根据errno
的值判断错误类型。例如,如果errno
是EINTR
,表示读操作被信号中断,可以重新调用read
;如果是其他错误,使用perror
打印错误信息并根据情况处理,如关闭文件描述符并退出程序。
char buffer[1024]; ssize_t bytes_read = read(pipefd[0], buffer, sizeof(buffer)); if (bytes_read == -1) { if (errno == EINTR) { // 重新调用read bytes_read = read(pipefd[0], buffer, sizeof(buffer)); } else { perror("read"); // 关闭文件描述符并退出程序等处理 close(pipefd[0]); exit(EXIT_FAILURE); } }
write
函数失败:- 错误原因:写端文件描述符无效、管道已满(非阻塞模式下)、写操作被信号中断等。
- 错误处理:在调用
write
函数后,检查返回值。如果返回-1
,根据errno
的值判断错误类型。例如,如果errno
是EINTR
,可以重新调用write
;如果是EAGAIN
或EWOULDBLOCK
(非阻塞模式下管道满),可以等待一段时间后重试;对于其他错误,使用perror
打印错误信息并进行相应处理,如关闭文件描述符并退出程序。
const char *message = "Hello, pipe!"; ssize_t bytes_written = write(pipefd[1], message, strlen(message)); if (bytes_written == -1) { if (errno == EINTR) { // 重新调用write bytes_written = write(pipefd[1], message, strlen(message)); } else if (errno == EAGAIN || errno == EWOULDBLOCK) { // 等待一段时间后重试 usleep(100000); bytes_written = write(pipefd[1], message, strlen(message)); } else { perror("write"); // 关闭文件描述符并退出程序等处理 close(pipefd[1]); exit(EXIT_FAILURE); } }