面试题答案
一键面试1. 实现进程组优雅退出机制
- 信号处理:
- 在父进程中,为常见的终止信号(如
SIGTERM
、SIGINT
)注册信号处理函数。在信号处理函数中,使用killpg
函数向整个进程组发送相同的信号,这样所有子进程都会收到终止信号。 - 例如:
- 在父进程中,为常见的终止信号(如
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
void sig_handler(int signum) {
pid_t pgid = getpgid(0);
killpg(pgid, signum);
}
int main() {
signal(SIGTERM, sig_handler);
signal(SIGINT, sig_handler);
// 创建子进程等操作
return 0;
}
- 子进程异常终止处理:
- 在父进程中,使用
waitpid
或wait
系列函数来等待子进程的状态变化。当子进程异常终止时(通过WIFEXITED
、WIFSIGNALED
等宏判断),父进程可以采取相应的措施,比如向其他子进程发送终止信号。 - 例如:
- 在父进程中,使用
pid_t pid = fork();
if (pid == 0) {
// 子进程代码
exit(0);
} else if (pid > 0) {
int status;
pid_t wpid = waitpid(pid, &status, 0);
if (wpid == pid) {
if (WIFSIGNALED(status)) {
// 子进程异常终止,向其他子进程发信号
pid_t pgid = getpgid(0);
killpg(pgid, SIGTERM);
}
}
}
2. 保证数据一致性和资源正确释放的关键要点与设计思路
- 数据一致性:
- 共享内存:
- 使用信号量(
semaphore
)来控制对共享内存的访问。在进程访问共享内存前获取信号量,访问结束后释放信号量。 - 例如,使用
semget
、semop
等函数。创建信号量集:
- 使用信号量(
- 共享内存:
int semid = semget(IPC_PRIVATE, 1, 0666);
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
} arg;
arg.val = 1;
semctl(semid, 0, SETVAL, arg);
访问共享内存前获取信号量:
struct sembuf sb;
sb.sem_num = 0;
sb.sem_op = -1;
sb.sem_flg = 0;
semop(semid, &sb, 1);
// 访问共享内存
sb.sem_op = 1;
semop(semid, &sb, 1);
- 引入事务机制,将对共享内存的一系列操作视为一个原子操作。在操作开始前记录共享内存的状态,操作过程中如果出现异常,可以回滚到初始状态。
- **管道**:
- 确保读写操作的同步。如果一个进程从管道读取数据,另一个进程向管道写入数据,要保证写入操作完成后,读取操作才开始,反之亦然。可以使用信号或同步原语(如互斥锁)来实现。
- 例如,使用 `pthread_mutex_t` 来保护管道操作:
pthread_mutex_t pipe_mutex = PTHREAD_MUTEX_INITIALIZER;
// 写管道前加锁
pthread_mutex_lock(&pipe_mutex);
write(pipe_fd[1], data, sizeof(data));
pthread_mutex_unlock(&pipe_mutex);
// 读管道前加锁
pthread_mutex_lock(&pipe_mutex);
read(pipe_fd[0], buffer, sizeof(buffer));
pthread_mutex_unlock(&pipe_mutex);
- 资源正确释放:
- 文件描述符:
- 在进程终止前,显式关闭所有打开的文件描述符,包括管道、共享内存相关的文件描述符。可以使用
close
函数。 - 例如,对于管道
pipe_fd
:
- 在进程终止前,显式关闭所有打开的文件描述符,包括管道、共享内存相关的文件描述符。可以使用
- 文件描述符:
close(pipe_fd[0]);
close(pipe_fd[1]);
- 对于共享内存,使用 `shmdt` 解除映射,`shmctl` 删除共享内存段。
// 解除共享内存映射
shmdt(shmaddr);
// 删除共享内存段
shmctl(shmid, IPC_RMID, NULL);
- **内存分配**:
- 使用 `malloc` 等函数分配的内存,在进程终止前要使用 `free` 函数释放。如果是使用 `mmap` 进行内存映射,要使用 `munmap` 解除映射。
- 例如:
char *ptr = (char *)malloc(100);
// 使用ptr
free(ptr);
- **进程相关资源**:
- 父进程在等待子进程结束后,正确处理子进程的退出状态,避免产生僵尸进程。通过 `wait` 系列函数等待子进程,并根据返回状态进行相应处理。
通过上述机制,可以在基于C语言的Linux项目中,实现进程组的优雅退出,并保证数据一致性和资源的正确释放。