面试题答案
一键面试1. 信号机制
原理:通过注册信号处理函数,当子进程结束时,系统会发送SIGCHLD信号给父进程,父进程的信号处理函数可以在该信号触发时调用waitpid函数来回收子进程资源,而无需父进程一直阻塞等待子进程结束。
优点:
- 非阻塞方式,父进程不会因为等待子进程而阻塞,提高了程序的并发处理能力。
- 可以异步处理子进程结束事件,在子进程结束时及时回收资源。
缺点:
- 信号处理函数的执行环境比较特殊,编写复杂的逻辑可能存在风险,例如不能在信号处理函数中调用一些不安全的函数。
- 信号可能会丢失,在高并发场景下如果信号处理不及时,可能会导致部分子进程资源无法及时回收。
风险:
- 由于信号处理函数是异步执行的,可能会与主程序的其他部分产生竞态条件,例如对共享资源的访问冲突。
代码框架示例:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.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) {
// 处理子进程结束相关逻辑
printf("Child %d terminated with status %d\n", pid, 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(1);
}
// 主程序创建大量子进程逻辑
for (int i = 0; i < 10; i++) {
pid_t pid = fork();
if (pid == 0) {
// 子进程逻辑
sleep(1);
exit(i);
} else if (pid < 0) {
perror("fork");
exit(1);
}
}
// 主程序其他逻辑
while (1) {
// 主程序继续执行其他任务
sleep(1);
}
return 0;
}
2. 非阻塞等待方式
原理:使用waitpid函数的WNOHANG标志,父进程调用waitpid时不会阻塞,而是立即返回。如果有子进程结束,返回子进程的PID并回收资源;如果没有子进程结束,返回0,父进程可以继续执行其他任务,然后定期调用waitpid检查是否有子进程结束。
优点:
- 父进程不会阻塞等待子进程,保持了程序的并发执行能力。
- 实现相对简单,不需要额外的信号处理机制。
缺点:
- 父进程需要定期轮询调用waitpid,增加了CPU开销。在高并发场景下,如果轮询频率过高,会消耗大量CPU资源。
- 可能不能及时回收子进程资源,因为是轮询检查,在两次轮询之间子进程结束可能不能立即处理。
风险:
- 由于轮询频率的不确定性,如果设置不当,可能导致系统资源浪费或者子进程资源回收不及时。
代码框架示例:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main() {
// 主程序创建大量子进程逻辑
for (int i = 0; i < 10; i++) {
pid_t pid = fork();
if (pid == 0) {
// 子进程逻辑
sleep(1);
exit(i);
} else if (pid < 0) {
perror("fork");
exit(1);
}
}
// 主程序轮询等待子进程结束
while (1) {
pid_t pid;
int status;
pid = waitpid(-1, &status, WNOHANG);
if (pid > 0) {
// 处理子进程结束相关逻辑
printf("Child %d terminated with status %d\n", pid, status);
} else if (pid == 0) {
// 没有子进程结束,继续执行其他任务
sleep(1);
} else {
// 错误处理
perror("waitpid");
break;
}
}
return 0;
}
3. 总结
在高并发场景下等待子进程,信号机制更适合需要及时响应子进程结束事件并且对CPU开销比较敏感的场景,但编写信号处理函数需要更加谨慎;非阻塞等待方式实现简单,但要注意合理设置轮询频率以平衡CPU开销和资源回收及时性。根据具体应用场景的需求,可以选择合适的优化策略或结合使用多种策略。