今天主要记录Linux环境下多进程编程的关键技术点,包括进程创建与控制、进程间通信(IPC)机制、以及信号处理等实战内容。这些知识是构建稳定后台服务的基础能力,也是面试中高频考察的Linux系统编程要点。
c复制#include <unistd.h>
pid_t fork(void);
这个看似简单的函数调用实际上完成了以下复杂操作:
关键点:fork()的"一次调用,两次返回"特性。父进程返回子进程PID,子进程返回0。
c复制// 等待子进程结束的三种方式
wait(NULL); // 阻塞等待任意子进程
waitpid(pid, &status, 0); // 指定等待特定子进程
waitid(P_PID, pid, &infop, WEXITED); // 更精细的控制
常见进程状态转换:
匿名管道示例:
c复制int pipefd[2];
pipe(pipefd); // 创建管道
if (fork() == 0) { // 子进程
close(pipefd[0]); // 关闭读端
write(pipefd[1], "hello", 6);
} else { // 父进程
close(pipefd[1]); // 关闭写端
char buf[10];
read(pipefd[0], buf, sizeof(buf));
}
命名管道(mkfifo)特点:
c复制// 创建共享内存段
int shm_id = shmget(IPC_PRIVATE, size, IPC_CREAT | 0666);
// 附加到进程地址空间
void *shm_ptr = shmat(shm_id, NULL, 0);
// 使用完成后分离
shmdt(shm_ptr);
// 最后删除共享内存段
shmctl(shm_id, IPC_RMID, NULL);
注意事项:共享内存需要配合信号量等同步机制使用,否则会出现竞态条件。
| 信号值 | 名称 | 触发条件 |
|---|---|---|
| SIGINT | 2 | 键盘中断(Ctrl+C) |
| SIGKILL | 9 | 强制终止进程 |
| SIGSEGV | 11 | 无效内存访问 |
| SIGALRM | 14 | 定时器信号 |
| SIGCHLD | 17 | 子进程状态改变 |
c复制void sig_handler(int signo) {
if (signo == SIGINT) {
printf("Received SIGINT\n");
// 清理资源
exit(0);
}
}
int main() {
struct sigaction sa;
sa.sa_handler = sig_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(SIGINT, &sa, NULL);
while(1) {
// 主程序逻辑
}
}
产生原因:父进程未调用wait()系列函数
解决方案:
c复制signal(SIGCHLD, SIG_IGN); // 简单忽略
c复制while (waitpid(-1, NULL, WNOHANG) > 0);
使用工具组合:
bash复制ps aux --sort=-%mem # 内存使用排序
lsof -p <pid> # 查看打开文件
strace -p <pid> # 跟踪系统调用