一、进程的核心定义与本质
1.1 进程是什么?
进程是程序的一次执行过程,是操作系统分配内存、CPU 等资源的基本单位。简单来说:
- 程序:存储在硬盘上的代码、数据的静态集合(如
a.out、ls命令); - 进程:程序加载到内存中运行的动态过程,包含创建、调度、消亡全生命周期。
1.2 进程与程序的核心区别
| 维度 | 程序 | 进程 |
|---|---|---|
| 状态 | 静态(永存) | 动态(暂时,有生命周期) |
| 特性 | 无状态变化、无并发 | 有状态切换、支持并发 |
| 资源 | 不占用系统资源 | 占用 CPU、内存、文件描述符等资源 |
| 运行关系 | 一个程序可运行多次,生成多个进程 | 一个进程可加载运行一个 / 多个程序 |
| 竞争关系 | 无资源竞争 | 多进程会竞争 CPU、内存等系统资源 |
示例:1.c编译为a.out(程序,静态),执行./a.out后生成一个 PID 为 xxx 的进程(动态),多次执行./a.out会生成多个独立进程。
1.3 PCB:进程的 "身份证"
Linux 系统通过PCB(Process Control Block,进程控制块) 管理进程,PCB 是内核中的结构体,存储进程的所有核心信息,核心字段包括:
- PID:进程唯一标识符(进程 ID);
- 当前工作路径(可通过
chdir修改); - umask:文件创建默认权限掩码(如
0002); - 进程打开的文件列表(文件描述符表);
- 信号相关设置(处理异步 IO);
- 用户 ID / 组 ID:进程的权限归属;
- 内存映射:进程的虚拟内存空间映射;
- 进程状态:运行、休眠、停止等。
1.4 进程的资源限制
Linux 对进程的资源使用有严格限制(可通过ulimit查看 / 修改),常见限制:
- 最大打开文件数:默认 1024;
- 栈大小:默认 8M;
- 最大虚拟内存:受系统物理内存限制;
- CPU 时间片:由调度算法分配。
二、进程的核心特性
2.1 虚拟内存:进程的 "内存隔离墙"
Linux 为每个进程分配独立的虚拟内存空间(默认 0-3G 用户空间 + 3-4G 内核空间),核心作用:
- 隔离性:A 进程无法直接访问 B 进程的内存空间,避免数据篡改;
- 安全性:通过权限控制进程访问内核空间,防止任意操作系统内核;
- 灵活性:进程无需关心物理内存地址,由内核完成虚拟地址到物理地址的映射。
2.2 进程的分类
根据运行特性,Linux 进程可分为三类:
- 交互式进程 :依赖用户输入触发运行,如
vim、bash、ssh,需等待用户操作(输入命令、点击按钮); - 批处理进程 :无需用户交互,批量执行命令,如 shell 脚本、定时任务(
crontab); - 守护进程 :后台自动运行,默认休眠,满足特定条件时触发(如系统更新进程、杀毒软件、
nginx服务),通常以d结尾(如sshd、crond)。
2.3 进程的核心作用:并发
并发是操作系统的核心能力 ------宏观并行,微观串行:
- 宏观:一个时间段内,多个进程看似 "同时运行"(如边刷浏览器边听音乐);
- 微观:某一时刻,CPU 仅能运行一个进程(单核),通过进程调度实现 "并发"。
三、进程的状态与调度
3.1 Linux 进程状态(核心)
进程在生命周期中会在多个状态间切换,通过ps aux可查看状态(STAT列):
| 状态标识 | 含义 | 说明 |
|---|---|---|
| R(Running) | 运行 / 就绪 | 正在 CPU 运行,或等待 CPU 时间片 |
| S(Sleeping) | 可中断休眠 | 等待事件触发(如 IO、信号),可被信号唤醒 |
| D(Uninterruptible Sleep) | 不可中断休眠 | 等待 IO(如磁盘读写),不可被信号唤醒 |
| T(Stopped) | 停止 | 被SIGSTOP信号暂停,可被SIGCONT唤醒 |
| Z(Zombie) | 僵尸进程 | 进程已终止,但父进程未回收其资源(PID 仍存在) |
3.2 进程调度算法
Linux 内核需合理分配 CPU 时间片给多个进程,常见调度算法:
- 时间片轮转:每个进程分配固定时间片(如 10ms),用完后切换到下一个进程;
- 短任务优先:优先调度运行时间短的进程,减少总等待时间;
- 进程优先级:优先级高的进程优先获取 CPU(Linux 优先级范围 0-139,数值越小优先级越高);
- 完全公平调度(CFS):Linux 默认调度算法,按进程的 "CPU 使用占比" 公平分配时间片。
3.3 进程上下文切换
当进程的 CPU 时间片耗尽,内核会切换到下一个进程,这个过程称为 "上下文切换":
- 暂停当前进程(如
a.out),将其状态(PCB、寄存器、PC 指针、内存数据)保存到硬盘 / 内存; - 释放 CPU 资源,加载下一个进程(如
b.out)的上下文数据到内存; - 恢复下一个进程的运行状态,使其占用 CPU 继续执行。
上下文切换会产生一定开销,过度切换会降低系统性能。
四、进程管理的核心命令
4.1 查看进程
| 命令 | 功能 | 示例 | |
|---|---|---|---|
ps aux |
显示所有进程的详细信息(PID、状态、CPU 占用等) | `ps aux | grep a.out(过滤a.out` 进程) |
top |
实时监控进程(Linux 版 "任务管理器") | 按P按 CPU 排序,按M按内存排序 |
|
pstree |
以树形结构显示进程间的父子关系 | pstree -p(显示 PID) |
4.2 终止进程
| 命令 | 功能 | 示例 |
|---|---|---|
kill [信号] PID |
向指定 PID 进程发送信号 | kill -9 12345(强制终止 PID=12345 的进程) |
killall [信号] 进程名 |
终止所有同名进程 | killall -9 a.out(终止所有a.out进程) |
pkill 进程名 |
按进程名终止进程 | pkill -9 sshd(终止所有 sshd 进程) |
常用信号:
-9(SIGKILL,强制终止)、-15(SIGTERM,优雅终止,默认)。
五、进程编程核心函数
5.1 fork():创建子进程
fork()是 Linux 创建进程的核心函数,作用是从当前进程(父进程)克隆一个子进程。
函数原型
c
运行
#include <unistd.h>
pid_t fork(void);
核心特性
- 一次调用,两次返回:父进程和子进程各返回一次;
- 子进程是父进程的 "完全拷贝":复制父进程的 0-3G 用户空间、PCB(仅 PID 不同);
- 执行起点:子进程从
fork()函数的返回处开始执行; - 资源不共享:父子进程的变量、内存空间相互独立(写时复制);
- 执行顺序:父子进程的运行顺序由内核调度决定,无法预测。
返回值
| 场景 | 返回值 | 说明 |
|---|---|---|
| 父进程中 fork 成功 | >0(子进程的 PID) | 可通过返回值区分父 / 子进程 |
| 子进程中 fork 成功 | 0 | 子进程无子进程,返回 0 |
| fork 失败 | -1 | 如资源不足、进程数超限 |
示例代码
c
运行
#include <stdio.h>
#include <unistd.h>
int main() {
pid_t ret = fork();
if (ret > 0) {
// 父进程逻辑
printf("父进程:PID=%d,子进程PID=%d\n", getpid(), ret);
} else if (ret == 0) {
// 子进程逻辑
printf("子进程:PID=%d,父进程PID=%d\n", getpid(), getppid());
} else {
perror("fork失败");
return 1;
}
return 0;
}
5.2 getpid():获取当前进程 PID
函数原型
c
运行
#include <unistd.h>
pid_t getpid(void);
- 功能:返回调用该函数的进程的 PID;
- 参数:无;
- 返回值:当前进程的 PID(非负整数)。
5.3 getppid():获取父进程 PID
函数原型
c
运行
#include <unistd.h>
pid_t getppid(void);
- 功能:返回调用该函数的进程的父进程 PID;
- 参数:无;
- 返回值:父进程的 PID(若父进程已终止,返回 1(init 进程))。
六、核心总结
- 进程是程序的动态执行过程,由 PCB 管理,占用系统资源,支持并发;
- 虚拟内存为进程提供隔离性和安全性,上下文切换是实现并发的核心机制;
- Linux 通过 CFS 等调度算法分配 CPU 时间片,实现 "宏观并行、微观串行";
fork()是创建进程的核心函数,父子进程独立运行,返回值是区分二者的关键;ps/top/kill是进程管理的常用命令,getpid()/getppid()是获取进程 ID 的核心函数。