Linux进程基础

一、进程与程序:静态与动态的本质区别

初学者易混淆进程与程序,二者从存在形式、生命周期、资源占用等维度存在本质差异,核心是 "静态文件" 与 "动态执行实例" 的区别:

维度 程序(Program) 进程(Process)
存在形式 静态,存储在硬盘中的代码、数据集合 动态,程序加载到内存后执行的实例
生命周期 永存,除非手动删除文件 暂时,有创建、调度、运行、消亡的完整周期
状态变化 无状态,始终是静态文件 有就绪、运行、阻塞、终止等状态切换
并发特性 无并发概念,仅作为文件存在 支持并发执行,多进程可抢占 CPU 资源
资源占用 不占用系统资源(CPU、内存、文件描述符等) 占用 CPU、内存、IO 等系统资源
运行关联 一个程序可多次运行,生成多个独立进程 一个进程可加载并执行一个或多个程序

直观示例:从代码到进程的转化

复制代码
test.c(源代码文件) → 编译 → test.out(可执行程序/静态文件) → 运行 → process(进程,分配PID)
  • test.c 是程序的 "源码形态",存储于硬盘;
  • test.out 是编译后的可执行文件,仍为静态程序;
  • 执行 ./test.out 后,系统为其分配内存、PID,才成为动态运行的进程。

虚拟内存与 MMU:进程隔离的核心保障

Linux 通过虚拟内存和内存管理单元(MMU)实现多进程安全运行:

  • 隔离性:每个进程拥有独立虚拟地址空间,MMU 负责虚拟地址到物理地址的映射,进程间无法直接访问内存,避免篡改;
  • 安全性 :内核运行在核心态,进程运行在用户态,进程需通过系统调用(如 fork()exit())并经权限校验后才能调用内核功能,防止恶意破坏。

二、进程的分类:按运行特性划分

根据运行方式和交互特性,Linux 进程分为三类,操作系统通过差异化调度策略适配其特性,实现系统并发:

1. 交互式进程

  • 核心特征:运行依赖用户输入,交互性强,执行后返回输出;
  • 典型示例:终端中的 vimsshtop,图形界面的浏览器、编辑器;
  • 调度特点:优先保证响应速度,分配更短时间片、更高优先级,避免用户操作卡顿。

2. 批处理进程

  • 核心特征:无需用户实时交互,按预设逻辑批量执行;
  • 典型示例:Shell 脚本、数据库批量备份程序、日志分析脚本;
  • 调度特点:系统负载较低时执行(如夜间),优先级低于交互式进程,避免占用前台资源。

3. 守护进程(Daemon Process)

  • 核心特征:系统启动后自动运行,长期驻留内存,休眠状态下等待触发;
  • 典型示例:nginx/apache(Web 服务)、rsyslogd(日志收集)、系统更新进程;
  • 调度特点:后台常驻,优先级稳定,PPID 通常为 1(由 init 进程接管)。

操作系统的进程状态切换图

进程的内存空间图

linux的进程状态切换图

进程分类的核心价值:实现系统并发

进程分类的本质是优化系统并发能力 ------ 操作系统在一段时间内同时运行多个任务的能力:

  • 单 CPU 核心:通过调度器快速切换进程(时间片轮转),宏观上 "同时运行",微观上同一时刻仅一个进程执行;
  • 多 CPU 核心:升级为并行,多个进程可在不同核心真正同时执行,提升吞吐量。

Linux 针对不同进程的调度优化目标:

  • 保证交互式进程响应速度;
  • 提升批处理进程执行效率;
  • 维持守护进程稳定常驻。

三、父子进程关系:fork () 创建与写时复制机制

Linux 中除 init 进程(PID=1,系统启动时创建)外,所有进程都有且仅有一个父进程,形成树形结构,核心创建方式是 fork() 系统调用。

3.1 fork () 函数的核心特性

fork() 遵循 "一次调用,两次返回" 规则:

  • 调用时,内核为新进程分配 PCB(进程控制块),复制父进程大部分资源;
  • 父进程中,fork() 返回子进程 PID(正整数),用于管理子进程;
  • 子进程中,fork() 返回 0,可通过 getppid() 获取父进程 PID;
  • 调用失败(如进程数达上限)返回 -1,并设置 errno

3.2 写时复制(Copy-On-Write):高效的内存复用策略

早期 fork() 会复制父进程全部内存空间(代码段、数据段、堆、栈),若子进程立即执行 exec 加载新程序,内存复制完全浪费。Linux 2.6 内核后引入写时复制(COW),核心原理:

  • fork() 执行后,父子进程共享所有内存页,且标记为 "只读";
  • 当任意一方修改内存页时,内核为该页创建副本,分配给修改方单独使用;
  • 优势:降低 fork() 开销,提升进程创建效率,节省内存。

3.3 fork () 代码示例:区分父子进程

复制代码
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int global_var = 10; // 全局变量,存储在数据段

int main() {
    pid_t pid;
    int local_var = 20; // 局部变量,存储在栈

    pid = fork();
    if (pid == -1) {
        perror("fork failed");
        return 1;
    } else if (pid > 0) {
        // 父进程执行逻辑
        global_var++;
        local_var++;
        printf("父进程 - PID: %d, 子进程PID: %d\n", getpid(), pid);
        printf("父进程 - global_var: %d, local_var: %d\n", global_var, local_var);
        sleep(2); // 等待子进程执行完毕
    } else {
        // 子进程执行逻辑
        printf("子进程 - PID: %d, 父进程PID: %d\n", getpid(), getppid());
        printf("子进程 - global_var: %d, local_var: %d\n", global_var, local_var);
        // 修改变量,触发写时复制
        global_var += 2;
        local_var += 2;
        printf("子进程修改后 - global_var: %d, local_var: %d\n", global_var, local_var);
    }
    return 0;
}

运行结果分析

  • 子进程初始变量与父进程一致(共享内存页);
  • 父进程修改变量不影响子进程,子进程修改时触发 COW,生成独立内存页副本。

四、进程的调度:CPU 资源的分配策略

多进程争夺有限的 CPU 核心资源时,Linux 内核调度器通过合理策略分配 CPU 时间,平衡公平性与响应性。

4.1 调度的核心逻辑:宏观并行与微观串行

  • 宏观并行:通过进程快速切换,用户感知所有进程 "同时运行";
  • 微观串行:单个 CPU 核心同一时刻仅执行一个进程的指令。

4.2 进程上下文切换

当进程时间片耗尽,内核切换到其他进程运行的过程,核心步骤:

  1. 保存当前进程状态(PCB 标识、寄存器值、程序计数器、内存映射等);
  2. 将状态写入内存,释放 CPU;
  3. 读取待运行进程的状态,恢复到寄存器和 CPU;
  4. 按程序计数器继续执行该进程。

注意:上下文切换存在系统开销,过于频繁会降低整体性能。

4.3 Linux 主流调度算法

调度算法 核心逻辑 适用场景 特点
时间片轮转 就绪进程轮流占用 CPU,时间片耗尽触发切换 交互式进程 保证公平获取 CPU 资源
短任务优先 优先调度运行时间更短的进程 批处理进程 降低整体任务平均等待时间
优先级调度 高优先级进程优先获取 CPU 响应敏感的实时任务 按优先级分配资源
完全公平调度器(CFS) 按 "权重" 分配 CPU 时间,权重越高时间片越长 内核默认(通用场景) 兼顾公平性与响应性
实时调度(SCHED_FIFO/SCHED_RR) 先进先出 / 实时进程时间片轮转 工业控制、自动驾驶等实时任务 优先级高于普通进程,可抢占 CPU

4.4 进程调度相关命令

命令 功能说明 常用示例
ps aux 显示所有进程详细信息(PID、状态、CPU 占用率等) `ps aux grep nginx`(过滤 nginx 进程)
top 实时监控进程资源占用,支持交互式调整优先级 P 按 CPU 排序,renice 调整优先级
kill 向进程发送信号,终止 / 调整进程状态 kill -9 1234(强制终止 PID=1234 的进程)
killall 按进程名批量关闭进程 killall -9 a.out(关闭所有 a.out 进程)

总结

进程是 Linux 实现并发的基本单位:

  • 与程序的核心区别是 "动态执行" 与 "静态文件";
  • 按运行特性分为交互式、批处理、守护进程,适配不同调度策略;
  • fork() 是创建进程的核心调用,写时复制优化了内存复用;
  • 调度算法通过平衡公平性与响应性,实现 CPU 资源的高效分配,配套命令可快速管理进程。
相关推荐
_OP_CHEN2 小时前
【Git原理与使用】(五)Git 多人协作:从分支协作到冲突解决,团队开发效率翻倍秘籍
linux·运维·git·团队开发·运维开发·企业级组件·git多人协作
添砖java‘’3 小时前
常见的进程间通信方式详解
linux·c++·操作系统·信息与通信·进程通信
企鹅侠客3 小时前
Linux性能调优:详解CPU使用率计算方式
linux·运维·服务器·性能调优
秋深枫叶红3 小时前
嵌入式第三十四篇——linux系统编程——进程
linux·服务器·数据库·学习
普通网友3 小时前
深入探讨Linux驱动开发:字符设备驱动开发与测试_linux 驱动开发设备号(2)
java·linux·驱动开发
hakuii3 小时前
linux中的一些配置
linux·运维·服务器
时光の尘3 小时前
嵌入式面试八股文(十九)·裸机开发与RTOS开发的区别
linux·stm32·单片机·iic·rtos·spi
瑾珮4 小时前
nmcli命令网络配置
linux·网络
qq_420443274 小时前
AMD显卡在windows中通过WSL安装使用stable diffusion(WebUI和ComfyUI)
linux·windows·ubuntu·stable diffusion·wsl