面试题答案
一键面试设计思路
- 信号处理:使用信号机制来捕获子进程状态变化的信号,例如
SIGCHLD
信号。当子进程状态发生改变(如正常退出、异常终止或停止)时,内核会向父进程发送SIGCHLD
信号。 - wait系列函数:在信号处理函数中调用
waitpid
函数来等待子进程结束,并获取其状态信息。waitpid
可以指定等待特定的子进程,避免父进程等待所有子进程结束。 - 进程间通信(可选):如果需要在子进程之间或子进程与父进程之间传递数据,可以使用管道、共享内存等IPC机制。这里主要是为了获取子进程状态,所以IPC不是核心部分,但在实际应用中可能会用到。
关键代码段作用
- 信号处理函数:
void sigchld_handler(int signum) {
pid_t pid;
int status;
// 使用waitpid等待子进程结束,避免僵尸进程
while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
if (WIFEXITED(status)) {
printf("子进程 %d 正常退出,退出状态: %d\n", pid, WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
printf("子进程 %d 异常终止,信号: %d\n", pid, WTERMSIG(status));
}
}
}
这个函数在接收到SIGCHLD
信号时被调用。waitpid
函数的-1
参数表示等待任意子进程,WNOHANG
标志表示如果没有子进程结束则立即返回,不阻塞。通过WIFEXITED
和WIFSIGNALED
宏来判断子进程是正常退出还是异常终止,并获取相应的状态信息。
- 注册信号处理函数:
struct sigaction sa;
sa.sa_handler = sigchld_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
perror("sigaction");
exit(EXIT_FAILURE);
}
这里使用sigaction
函数注册SIGCHLD
信号的处理函数。SA_RESTART
标志使得被信号中断的系统调用自动重启。
- 创建子进程:
pid_t pid = fork();
if (pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
} else if (pid == 0) {
// 子进程代码
// 执行不同时长的任务
sleep(2); // 示例,执行2秒任务
exit(EXIT_SUCCESS);
}
使用fork
函数创建子进程,子进程执行具体的任务,这里以sleep
模拟任务执行时长,最后通过exit
退出。父进程则继续执行其他逻辑,等待子进程状态变化信号。
完整示例代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
void sigchld_handler(int signum) {
pid_t pid;
int status;
while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
if (WIFEXITED(status)) {
printf("子进程 %d 正常退出,退出状态: %d\n", pid, WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
printf("子进程 %d 异常终止,信号: %d\n", pid, WTERMSIG(status));
}
}
}
int main() {
struct sigaction sa;
sa.sa_handler = sigchld_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
perror("sigaction");
exit(EXIT_FAILURE);
}
for (int i = 0; i < 5; i++) {
pid_t pid = fork();
if (pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
} else if (pid == 0) {
// 子进程执行不同时长任务
sleep(i + 1);
exit(i);
}
}
// 父进程继续执行其他逻辑,等待信号处理子进程状态
while (1) {
sleep(1);
}
return 0;
}
在这个示例中,创建了5个子进程,每个子进程执行不同时长的任务后退出。父进程通过信号处理机制及时获取子进程的状态,避免了进程僵死和资源泄漏。