Linux 系统编程:进程管理——从 fork 到迷你 Shell
·
Linux 系统编程:进程管理——从 fork 到迷你 Shell
1. 为什么需要进程管理?
之前学文件 I/O,我学会了操作文件。但怎么让多个程序同时运行?怎么让它们之间传输数据?这就是进程管理要解决的问题。
2. 核心概念
2.1 进程创建 —— fork()
fork() 将程序一分为二,变成父子两个进程。一次调用,两次返回:
- 子进程返回
0 - 父进程返回子进程的 PID(大于 0)
- 失败返回
-1
fork 刚完成时,父子进程内存内容完全相同,之后各自独立运行。
pid_t pid = fork();
if (pid == -1) {
perror("fork");
exit(1);
}
if (pid == 0) {
printf("我是子进程\n");
} else {
printf("我是父进程,子进程 PID = %d\n", pid);
}
2.2 程序替换 —— execlp() / execvp()
这两个函数把当前进程完全替换成另一个可执行程序。成功不返回,失败才返回。
| 函数 | 传参方式 | PATH 搜索 |
|---|---|---|
execlp |
一个个列,NULL 结尾 | 自动搜索 |
execvp |
放数组里,传首位地址 | 自动搜索 |
// execlp 方式
execlp("ls", "ls", "-l", NULL);
// execvp 方式
char *args[] = {"ls", "-l", NULL};
execvp(args[0], args);
exec 成功后原程序被覆盖,perror + exit(1) 只是失败时的兜底。
2.3 子进程回收 —— wait()
父进程调用 wait(NULL) 会阻塞等待,直到任意一个子进程退出才继续。
pid_t pid = fork();
if (pid == 0) {
execlp("ls", "ls", "-l", NULL);
perror("execlp");
exit(1);
}
wait(NULL);
printf("子进程已退出\n");
2.4 管道通信 —— pipe()
pipe(fd) 创建一根数据水管,fd[0] 是读端,fd[1] 是写端。
子进程(stdout) → dup2 → fd[1] ──水流──→ fd[0] → dup2 → 父进程(stdin)
int fd[2];
pipe(fd);
pid_t pid = fork();
if (pid == 0) {
close(fd[0]);
dup2(fd[1], 1);
close(fd[1]);
execlp("cat", "cat", "/etc/hostname", NULL);
perror("cat");
exit(1);
}
close(fd[1]);
dup2(fd[0], 0);
close(fd[0]);
execlp("wc", "wc", "-c", NULL);
perror("wc");
exit(1);
运行输出 14,和 cat /etc/hostname | wc -c 结果一致。
2.5 重定向 —— dup2()
// > 输出重定向:stdout 写进文件
int fd = open("out.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
dup2(fd, 1);
close(fd);
// < 输入重定向:stdin 从文件读
int fd = open("input.txt", O_RDONLY);
dup2(fd, 0);
close(fd);
3. 踩到的坑
坑1:父进程忘了关管道 fd
- 现象:管道程序永远卡住,不退出
- 原因:fork 后父进程还抓着写端 fd[1],读端永远等不到 EOF
- 修复:父进程不用管道时,必须
close(fd[0]); close(fd[1]);
坑2:strcmp 和 == 搞混
- 现象:检测
<符号永远不生效 - 原因:
args[i] == "<"比较的是指针地址,不是字符串内容 - 修复:改成
strcmp(args[i], "<") == 0
4. 迷你 Shell
我把这些知识串起来做了一个迷你 Shell,演进路线:
| 版本 | 新增能力 |
|---|---|
| v1 | while + fgets + fork + execlp 骨架 |
| v2 | strtok 参数切割 + execvp + exit 内置 |
| v3 | 管道 ` |
| v4 | 重定向 > < 支持 |
核心思路:
while(1)+fgets循环读取用户命令strtok按空格拆成命令数组- 检测到
|时,pipe+ 双fork连接左右命令 - 检测到
>/<时,open+dup2重定向 - 普通命令走
fork+execvp+wait
// 简化版:用 pipe 实现 ls -l | grep .c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
int fd[2];
pipe(fd);
pid_t pid = fork();
if (pid == 0) {
dup2(fd[1], 1);
close(fd[0]);
close(fd[1]);
execlp("ls", "ls", "-l", NULL);
perror("execlp ls");
exit(1);
}
dup2(fd[0], 0);
close(fd[1]);
close(fd[0]);
execlp("grep", "grep", ".c", NULL);
perror("execlp grep");
exit(1);
}
$ gcc -Wall -Wextra -g pipe_shell_demo.c -o pipe_shell_demo
$ ./pipe_shell_demo
总结
从 fork/exec/wait 出发,到 pipe/dup2 管道重定向,最后拼出一个带管道功能的迷你 Shell。学完这套,对"Shell 到底怎么执行命令"有了实实在在的理解。
更多推荐




所有评论(0)