Linux进程与进程控制学习总结
一、核心概念
-
进程定义
- 进程是程序的一次执行实例,是资源分配和调度的基本单位。
- 特性:动态性、并发性、独立性、异步性、结构性。
-
进程控制块(PCB)
- 操作系统通过
task_struct
结构体(PCB)管理进程,包含:- 进程标识(PID、PPID)
- 状态信息(运行、就绪、阻塞等)
- 资源信息(内存、打开的文件、信号量等)
- 操作系统通过
-
进程标识
- PID(进程ID):唯一标识进程的正整数。
- PPID(父进程ID):创建当前进程的父进程PID。
-
进程状态
状态 描述 运行 正在占用CPU执行指令 就绪 已获得除CPU外的所有资源,等待调度 阻塞 因等待事件(如I/O完成)暂停执行 终止 进程执行完毕或被强制终止 -
进程与程序的区别
- 程序:静态的代码文件(如
a.out
)。 - 进程:动态的程序执行实例,包含执行上下文和资源。
- 程序:静态的代码文件(如
二、进程结构
-
内存布局
- 代码段:存储可执行指令。
- 数据段:存储全局变量和静态变量。
- 堆段:动态分配的内存(如
malloc
)。 - 栈段:存储局部变量和函数调用信息。
-
进程模式
- 用户模式:执行用户程序,权限受限。
- 内核模式:执行系统调用或中断处理,可操作特权指令。
三、进程操作函数
1. 创建进程(fork
)
#include <unistd.h>
pid_t fork(void); // 返回值:子进程返回0,父进程返回子进程PID,失败返回-1
示例:创建子进程并区分父子进程逻辑
#include <stdio.h>
#include <unistd.h>
int main() {
pid_t pid = fork();
if (pid == 0) {
printf("子进程PID: %d\n", getpid()); // 子进程逻辑
} else if (pid > 0) {
printf("父进程PID: %d, 子进程PID: %d\n", getpid(), pid); // 父进程逻辑
} else {
perror("fork失败");
}
return 0;
}
输出:
父进程PID: 1234, 子进程PID: 1235
子进程PID: 1235
2. 进程退出(exit
vs _exit
)
exit
:清理缓冲区后退出(如刷新stdio
流)。_exit
:直接终止进程,不清理缓冲区。
#include <stdlib.h>
void exit(int status); // 标准退出函数
#include <unistd.h>
void _exit(int status); // 直接终止进程
3. 进程替换(exec
函数族)
#include <unistd.h>
int execl(const char *path, const char *arg, ... /* NULL */); // 路径+参数列表
int execv(const char *path, char *const argv[]); // 路径+参数数组
int execlp(const char *file, const char *arg, ...); // 文件名+自动搜索PATH
示例:使用execlp
执行ls
命令
#include <unistd.h>
int main() {
execlp("ls", "ls", "-l", NULL); // 替换当前进程为ls -l
perror("exec失败"); // 若exec失败才会执行
return 1;
}
4. 进程等待(wait
)
#include <sys/wait.h>
pid_t wait(int *status); // 等待任意子进程结束,返回子进程PID
示例:父进程等待子进程结束
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
int main() {
pid_t pid = fork();
if (pid == 0) {
printf("子进程运行\n");
sleep(2);
_exit(0);
} else {
wait(NULL); // 阻塞等待子进程结束
printf("子进程已终止\n");
}
return 0;
}
输出:
子进程运行
(2秒后)
子进程已终止
四、综合例题
题目:使用fork
和exec
实现一个简单的Shell,执行用户输入的命令。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
int main() {
char command[256];
while (1) {
printf("> ");
fgets(command, sizeof(command), stdin);
command[strcspn(command, "\n")] = '\0'; // 去除换行符
pid_t pid = fork();
if (pid == 0) { // 子进程执行命令
execlp(command, command, NULL);
perror("命令执行失败");
_exit(1);
} else if (pid > 0) { // 父进程等待子进程结束
wait(NULL);
} else {
perror("fork失败");
}
}
return 0;
}
输出示例:
> ls
file1.txt file2.txt
> pwd
/home/user
五、常见问题
fork
后的执行顺序:父子进程执行顺序由调度器决定,无法预知。- 僵尸进程:子进程终止后未被父进程
wait
回收,需通过信号处理或显式回收避免。 exec
函数族的选择:execlp
/execvp
:自动搜索PATH
环境变量。execle
/execve
:可指定自定义环境变量。
总结
- 进程管理核心:通过
fork
创建进程,exec
替换进程映像,wait
同步进程。 - 关键区别:
exit
与_exit
:前者清理缓冲区,后者直接终止。fork
返回值:父进程返回子进程PID,子进程返回0。
- 应用场景:
- 多任务处理(如Web服务器)。
- 进程间通信(需结合管道、信号等机制)。
- 实现Shell或任务调度器。
通过掌握进程控制函数,能够编写高效的多进程程序,充分利用系统资源。