面试题答案
一键面试- 进程监控与恢复
- 父进程监控子进程:
- 在程序设计上采用主进程 - 子进程模型。主进程负责监控子进程的状态。当子进程收到
SIGKILL
信号异常终止时,父进程可以通过wait
系列函数获取子进程的退出状态,并重新启动子进程。例如:
- 在程序设计上采用主进程 - 子进程模型。主进程负责监控子进程的状态。当子进程收到
- 父进程监控子进程:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main() {
pid_t pid;
while (1) {
pid = fork();
if (pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
} else if (pid == 0) {
// 子进程逻辑
printf("Child process running...\n");
while (1) {
// 子进程具体工作
sleep(1);
}
exit(EXIT_SUCCESS);
} else {
int status;
waitpid(pid, &status, 0);
if (WIFEXITED(status)) {
printf("Child process exited normally with status %d\n", WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
printf("Child process was terminated by signal %d\n", WTERMSIG(status));
// 重新启动子进程
}
}
}
return 0;
}
- 外部监控工具:使用像
systemd
这样的服务管理工具来监控进程。systemd
可以在进程意外终止(包括因SIGKILL
导致的终止)时自动重启进程。例如,编写一个简单的systemd
服务单元文件(假设程序名为myprogram
,位于/usr/local/bin
目录下):
[Unit]
Description=My Long - running C Program
After=network.target
[Service]
ExecStart=/usr/local/bin/myprogram
Restart=always
RestartSec=5
[Install]
WantedBy=multi - user.target
然后通过 systemctl start myprogram
启动服务,systemd
会负责监控并在程序异常终止时重启。
2. 数据保护与恢复
- 定期数据持久化:在程序运行过程中,定期将关键数据写入磁盘。例如,如果程序是一个数据库服务,定期将内存中的数据刷入磁盘文件。可以使用
fsync
等函数确保数据真正写入磁盘。
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#define DATA_SIZE 1024
void save_data(const char *data, const char *filename) {
int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd == -1) {
perror("open");
exit(EXIT_FAILURE);
}
if (write(fd, data, DATA_SIZE) != DATA_SIZE) {
perror("write");
close(fd);
exit(EXIT_FAILURE);
}
if (fsync(fd) == -1) {
perror("fsync");
close(fd);
exit(EXIT_FAILURE);
}
close(fd);
}
- 事务处理:对于涉及数据操作的程序,采用事务机制。例如在数据库操作中,使用事务来确保一系列操作要么全部成功,要么全部失败。在程序意外终止时,可以通过日志等机制回滚未完成的事务,恢复到程序异常终止前的状态。
- 权限管理
- 最小权限原则:确保运行程序的用户具有最小的权限。如果程序不需要超级用户权限,就以普通用户身份运行。这样,即使其他恶意用户尝试发送
SIGKILL
信号,也可能因为权限不足而无法终止程序。例如,在启动程序时通过setuid
或setgid
函数来降低进程的权限:
- 最小权限原则:确保运行程序的用户具有最小的权限。如果程序不需要超级用户权限,就以普通用户身份运行。这样,即使其他恶意用户尝试发送
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
// 获取普通用户的uid
uid_t target_uid = getpwnam("普通用户名")->pw_uid;
if (setuid(target_uid) == -1) {
perror("setuid");
exit(EXIT_FAILURE);
}
// 程序后续逻辑
return 0;
}
- 访问控制:配置操作系统的访问控制机制,如
iptables
(在Linux系统中),限制对程序相关端口和进程的访问。这样可以防止未经授权的用户向程序发送SIGKILL
信号。
- 进程通信与冗余
- 进程间通信(IPC):如果程序由多个部分组成,可以通过进程间通信机制(如管道、共享内存、消息队列等)进行通信。当一个进程因
SIGKILL
信号终止时,其他进程可以通过 IPC 机制检测到,并采取相应的恢复措施。例如,使用共享内存来传递数据和状态信息:
- 进程间通信(IPC):如果程序由多个部分组成,可以通过进程间通信机制(如管道、共享内存、消息队列等)进行通信。当一个进程因
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define SHM_SIZE 1024
int main() {
key_t key = ftok(".", 'a');
int shmid = shmget(key, SHM_SIZE, IPC_CREAT | 0666);
if (shmid == -1) {
perror("shmget");
exit(EXIT_FAILURE);
}
char *shared_memory = (char *)shmat(shmid, NULL, 0);
if (shared_memory == (void *)-1) {
perror("shmat");
exit(EXIT_FAILURE);
}
// 进程间通过共享内存通信逻辑
shmdt(shared_memory);
return 0;
}
- 冗余进程:运行多个冗余进程来执行相同的任务。当其中一个进程被
SIGKILL
信号终止时,其他冗余进程可以继续提供服务。可以通过负载均衡机制(如nginx
等软件负载均衡器)来分配任务给冗余进程。