Linux 进程与资源管理
Chen Kai CTO

一、进程与程序管理

1. 进程与程序之间的关系

在 Linux 中,程序是存储在硬盘上的静态可执行文件,而进程是程序加载到内存中后运行的实例。每次运行程序时,系统都会创建一个或多个进程,每个进程拥有独立的内存空间、进程ID(PID)、父进程ID(PPID)以及各自的运行环境。进程是动态存在的,是源代码和数据的结合体,是一个资源单位,而程序则是静态存储的代码集合。

线程则是 CPU 运行的最小工作单位,例如一个网易云音乐进程可能包含两个线程,一个是下载音乐,一个是播放歌曲。因此进程可以理解为工地上指挥干活的包工头,线程是具体干活的工人。

![程序、进程、线程和任务之间关系| iBit程序猿](./Linux 进程与资源管理/program-process-task-thread_1589445704853.jpeg)

例子

  • 当你在 Ubuntu 中运行 vim myfile.txt 时,vim 程序从硬盘加载到内存中,产生一个进程,该进程负责编辑 myfile.txt
  • 同一个程序可以同时启动多个进程,例如在浏览器中打开多个标签页时,每个标签页可能对应一个独立的进程或线程。
1
2
3
# 示例:启动 vim 进程并查看 PID
vim myfile.txt &
ps -ef | grep vim

![image-20250206003356484](./Linux 进程与资源管理/image-20250206003356484.png)

2. 进程的特点

进程的独立性与并发性

  • 独立性:每个进程都有自己的内存空间和系统资源,彼此相互隔离。
  • 并发性:操作系统允许多个进程同时运行,通过多任务调度实现并发处理。
  • 动态性:进程不断创建、执行、终止,状态实时变化。【操作系统的运行就是不断的创建进程和销毁进程】
  • 父子关系:进程由父进程通过 fork() 调用创建,形成父子结构;父进程的 PPID 字段指明了其创建者。
  • 调度性:操作系统使用调度算法(如时间片轮转、优先级调度)来决定进程的执行顺序。

示例

  • 使用 ps -ef 查看进程时,可以看到每个进程的 PID 和 PPID,帮助理解父子进程关系。
  • 父进程退出后,子进程成为孤儿进程,由 initsystemd 接管。
  • 当子进程结束但父进程未调用 wait() 回收时,会产生僵尸进程,其信息仍保留在进程表中。
1
2
3
# 查看进程父子关系示例
ps -ef | grep bash
pstree -p

二、Linux 系统资源管理概述

运维工作围绕硬件和软件资源展开,合理管理这些资源可确保系统高效稳定运行。下面介绍主要硬件与软件资源及常用监控工具。

1. 硬件资源管理

磁盘资源

  • 容量:硬盘或 SSD 提供的总存储空间。

  • 读写性能

    • 机械硬盘(HDD):容量大、价格低、速度较慢。
    • 固态硬盘(SSD):速度快、价格高、容量相对较小。
  • 常用命令

    1
    2
    3
    df -h       # 查看磁盘使用情况
    lsblk # 列出块设备及挂载点
    iostat # 监控磁盘 I/O 性能

![image-20250206111508163](./Linux 进程与资源管理/image-20250206111508163.png)

![image-20250206111526729](./Linux 进程与资源管理/image-20250206111526729.png)

![image-20250206111544412](./Linux 进程与资源管理/image-20250206111544412.png)

  • 操作系统 0 号进程:0号进程会创建出一堆内核级进程,提供操作系统运行
  • 用户级别 1 号进程:
    • 基于 1 号进程创建子进程 sshd 服务
    • 基于 sshd 服务产生 ssh 客户端连接进程
    • 基于 ssh 进程产生用户 bash 进程
    • 基于 bash 进程产生用户执行的其他进程

内存资源

  • 总内存与剩余内存:反映系统当前内存占用情况。

  • 常用命令

    1
    2
    free -hfree -h     # 查看内存和交换空间使用情况
    vmstat # 查看虚拟内存统计信息

![image-20250206153209092](./Linux 进程与资源管理/image-20250206153209092.png)

CPU 资源

  • 核心数与负载:监控各进程对 CPU 的占用情况和系统整体负载。

  • 常用命令

    1
    2
    3
    top
    htop
    mpstat

网络资源

  • 带宽、吞吐量与延迟:监控网络接口的数据传输速率和状态。

  • 常用命令

    1
    2
    iftop       # 实时监控网络流量
    ip -s link # 查看接口统计信息

2. 软件资源与服务管理

  • 系统服务:如 sshd, crond, ntpd 等,通过 systemctl 管理。
  • 日志管理:利用 rsyslogjournalctl 分析系统和应用日志。
  • 计划任务:使用 crontab 配置定时任务,实现自动化运维。

例子

1
2
3
systemctl status sshd
systemctl restart ntpd
crontab -e

3. Buffer 和 Cache 讲解

Linux 系统将内存划分为多个区域,其中 Buffer 和 Cache 是两个非常重要的区域。理解这两个概念有助于更好地优化系统性能和排查问题。

缓存与性能

  1. 高效的内存使用:内存中的 Buffer 和 Cache 提供了数据的快速访问通道。当程序需要读取文件时,Linux 会首先检查 Cache 中是否有该文件的数据,如果有,直接从内存中读取,速度非常快;如果没有,则从磁盘读取并缓存到内存。
  2. 提升磁盘 I/O 性能:Buffer 和 Cache 缓存使得磁盘 I/O 变得更加高效,减少了磁盘的访问次数,提高了系统性能。

![Buffer Cache](./Linux 进程与资源管理/maxresdefault.jpg)

Buffer(写入数据时加速)

定义:Buffer 是操作系统用于存储即将写入磁盘的数据的内存区域。当程序要写入文件时,数据首先会被存储在内存中的 Buffer 中,等到合适的时机再分批次写入磁盘。没有 buffer 的时候会产生大量的零碎文件,不断的把碎片文件写如此盘,太慢了。

作用:它减少了程序直接与磁盘交互的次数,从而提高了 I/O 性能。

Cache(读取数据时加速)

定义:Cache 是用于存储已经读取过的数据,目的是加速后续访问。Cache 保存了频繁访问的磁盘数据,以减少磁盘 I/O 操作,提高数据读取速度。

作用:缓存是内存中高速存取的数据区域,频繁访问的数据会被缓存在内存中,而不是每次都去磁盘读取。

![buff和cached解析- 屯子里唯一的架构师- 博客园](./Linux 进程与资源管理/2478745-20220518204225587-325292210.png)

三、进程监控工具详解

1. 使用 top 动态查看进程信息

top 提供实时进程信息,显示 CPU、内存使用率、运行时间、命令等信息。

基本用法

1
top

![image-20250206150129592](./Linux 进程与资源管理/image-20250206150129592.png)

第一行的 load average 里,CPU是几核的,数字越接近几压力就越大,例如CPU是4核的,数字越接近4压力就越大

常用快捷键

  • P:按 CPU 占用排序
  • M:按内存占用排序
  • k:输入 PID 发送信号终止进程
  • q:退出 top 界面

补充(glances):综合监控工具,实时展示系统各项指标。这个工具是基于Python开发的,可以用Python去改造,同时还提供了Web可视化页面,也就是这个程序部署在云上,可以以Web的形式展示系统资源。

![image-20250206150908037](./Linux 进程与资源管理/image-20250206150908037.png)

2. 使用 htop 增强监控体验

htoptop 的增强版,具有友好的彩色界面和交互式操作。

1
2
3
4
# 安装
sudo apt install htop
# 启动
htop

特点

  • 支持鼠标操作
  • 显示树状进程视图
  • 可直接选择并终止进程

![image-20250206150847590](./Linux 进程与资源管理/image-20250206150847590.png)

3. 使用 ps 静态查看进程信息

ps 命令提供当前进程快照信息,可通过不同选项显示详细内容。

常用用法

1
ps -ef

![image-20250206134925579](./Linux 进程与资源管理/image-20250206134925579.png)

ps 看到的进程名字,如果带有中括号,那就是Linux内核自动生成的进程,如果没有中括号(第一行),就是发行版系统,用户生成的各种进程。也就是说,Linux 是一个开源内核,而 Ubuntu 则是基于 Linux 内核构建的一个 Linux 发行版。

  • Linux 内核:Linux 内核是由 Linus Torvalds 在 1991 年首次发布的,它是操作系统的核心,负责硬件管理、内存管理、进程调度、文件系统等基本功能。内核本身不包含用户界面或应用程序,它只是提供了系统资源的抽象层和基本服务。
  • Linux 发行版(Distribution):Linux 发行版是在 Linux 内核的基础上,整合了大量软件包、工具和应用程序,形成一个完整的操作系统。发行版会添加图形界面、命令行工具、安装程序和各种预配置的软件,使得普通用户也能方便地使用 Linux 系统。Ubuntu 就是众多 Linux 发行版之一,它以用户友好、易于安装和良好的社区支持而著称。

简单来说,Ubuntu 使用 Linux 内核,并在其基础上整合了各种软件包和工具,构成一个完整的操作系统。其他知名的发行版还有 Debian、Fedora、CentOS 等,它们都共享同一个 Linux 内核,但在软件选择、配置和用户体验上有所不同。

ps -ef 是 Unix 风格的命令,有短横线,而 ps auf 是 BSD 风格的命令,没有短横线。Unix 风格的具体命令格式和其他参数如下:

1
ps -ef -p <pid_list> -C <command> -U <user>
  • -e:显示所有进程(等同于 –A),即不限制进程所属的终端、用户等。
  • -f:使用全格式输出(Full-format),会显示额外的信息,例如 UID、PID、PPID、C(CPU 占用)、STIME、TTY、TIME、CMD 等。输出内容更详细。
  • -p:根据进程 ID(PID)过滤显示,仅列出指定 PID 的进程。使用时需要在后面跟上一个或多个 PID 列表,例如 -p 1234,5678
  • -C:根据命令名过滤显示,仅列出指定命令名称对应的进程。例如 -C sshd 会显示所有名称为 sshd 的进程。
  • -U:根据实际用户(Real user)过滤显示,仅显示指定用户拥有的进程。例如 -U root 会显示 root 用户的所有进程。

BSD 风格的命令的各个选项的含义如下:

  • a:显示所有与终端相关的进程,不仅限于当前用户的进程。
  • x:显示所有进程,包括那些没有控制终端(TTY)的进程(例如守护进程或后台进程)。
  • u:以用户导向格式显示进程信息,输出中会包含 USER、PID、%CPU、%MEM、VSZ、RSS、TTY、STAT、START、TIME、COMMAND 等列,更加直观易读。
  • f:以树状结构(forest)的形式 ps显示进程之间的父子关系,有助于理解进程的继承关系。
  • o:允许用户自定义输出格式,可以指定输出哪些列。如果后面没有跟参数,则使用默认格式;通常可以写成 ps axu -o pid,ppid,cmd 这种形式来指定需要显示的列。
  • k:用来指定排序关键字,控制输出结果的排序方式。例如可以按 PID、CPU 占用或内存等排序。如果后面没有跟具体参数,则通常使用默认排序方式。

![image-20250206150048998](./Linux 进程与资源管理/image-20250206150048998.png)

ps 可以去掉 grep 显示的那个进程,方法是使用 -v 参数(结果取反):

![image-20250206152136083](./Linux 进程与资源管理/image-20250206152136083.png)

4. 使用 pstree 显示进程树

pstree 以树状结构展示进程父子关系,帮助理解进程层级。

基本用法

1
pstree -p

![image-20250206151051241](./Linux 进程与资源管理/image-20250206151051241.png)

5. 使用 pidof 查找进程 PID

pidof 能查找指定进程的 PID,适合脚本自动化管理。

基本用法

1
2
# 执行 pidof sshd 返回所有 SSH 守护进程的 PID。
pidof sshd

6. 使用 lsof 查看打开的文件

lsof(List Open Files)用于列出系统中所有打开的文件,包括常见的网络连接、设备和进程占用的文件。以下是一些常用参数及其说明:

  • -a
    与其他选项一起使用,表示“与”关系(AND),只显示同时满足所有条件的结果。

  • -c
    按进程名称过滤,显示指定命令名的进程所打开的文件。
    示例: lsof -c ssh

  • -d
    按文件描述符过滤,显示特定文件描述符(如 cwdtxtmem12 等)的打开文件。
    示例: lsof -d cwd

  • -i [protocol][@hostname|hostaddr][:service|port]
    显示网络相关的打开文件。

    • lsof -i 显示所有网络连接
    • lsof -i tcp 显示所有 TCP 连接
    • lsof -i :80 显示所有使用 80 端口的进程
    • lsof -i @192.168.1.100 显示与特定 IP 相关的连接
  • -n
    禁用主机名解析,直接显示 IP 地址,有助于提高执行速度并避免 DNS 查询延迟。

  • -P
    禁用端口号转换,不把端口号转换为服务名称,直接显示数字端口。

  • -u
    按用户过滤,显示指定用户的所有打开文件。
    示例: lsof -u root

  • -p
    按进程 ID 过滤,显示指定进程的所有打开文件。
    示例: lsof -p 1234

  • +D
    列出指定目录(包括子目录)中所有打开的文件。
    示例: lsof +D /var/log

  • +L1
    列出链接计数小于 1 的文件,通常表示文件已删除但仍被进程占用。

  • -r [interval]
    循环执行 lsof,每隔指定秒数刷新一次结果,常用于实时监控。
    示例: lsof -r 2 (每 2 秒刷新一次)

  • -V
    显示 lsof 版本信息。

示例:

列出所有使用 80 端口的网络连接,不解析主机名和端口名称:

1
lsof -nlsof -nP -i :80P -i :80

![image-20250206173629686](./Linux 进程与资源管理/image-20250206173629686.png)

其他一些用法:

![image-20250206152853213](./Linux 进程与资源管理/image-20250206152853213.png)

查看 Nginx 进程打开了哪些文件,进一步可以从中过滤出日志文件:

![image-20250206153440327](./Linux 进程与资源管理/image-20250206153440327.png)

还可以查看占用文件的进程:

![image-20250206173342247](./Linux 进程与资源管理/image-20250206173342247.png)

7. 网络流量与端口监控

  • iftop:实时显示网络接口数据流量。
  • lsof:查看进程打开的文件或网络端口。
  • netstat, ss

例子:

1
2
sudo iftop -i ens160
lsof -i :80

![image-20250208013603183](./Linux 进程与资源管理/image-20250208013603183.png)

也可以使用 netstat 或者具有更快的查询速度的 ss 命令查看端口信息:

1
2
netstat -tulnp
ss -tulnp # 高并发下性能更高

![image-20250208013219488](./Linux 进程与资源管理/image-20250208013219488.png)

-t 显示 TCP 连接,-u 显示 UDP 连接,-l 显示监听端口(过滤出 State 列中值为 LISTEN 的连接),-n 显示数字形式的地址和端口,-p 表示显示发起连接的进程 pid 和进程名称。

8. 磁盘 I/O 查看

磁盘 I/O 的性能直接影响到系统的运行效率,特别是在进行大规模数据处理时。

iostat 命令提供了关于 CPU 和 I/O 设备的详细统计信息。

1
iostat -x

-x 参数显示扩展信息,包括每个设备的 I/O 性能。

另外也可以使用 iotop 实时查看磁盘 I/O 使用情况,但需要管理员权限来运行,实时显示进程的磁盘 I/O 使用情况。

![image-20250208011840833](./Linux 进程与资源管理/image-20250208011840833.png)

iotop -k 可以动态显示 IO 情况:

![image-20250208012108808](./Linux 进程与资源管理/image-20250208012108808.png)

9. 内存资源查看 free

在 Linux 系统中,查看内存资源的使用情况,可以使用 free 命令。该命令提供了有关系统内存使用的汇总信息。

  1. 查看内存使用情况

    1
    free -h

-h 参数会以人类易读的格式(如 MB、GB)显示内存使用情况。

  1. 输出详细信息

    1
    free -m

    -m 参数以 MB 为单位显示内存使用情况,适用于查看内存总量和剩余空间。

  2. 查看内存使用变化

    1
    watch free -h

    使用 watch 命令可以实时监控内存使用情况,自动刷新显示结果。

![image-20250208010259465](./Linux 进程与资源管理/image-20250208010259465.png)

swap 是硬件交换分区(虚拟内存),防止内存用完导致系统崩溃,临时拿磁盘的一些空间当做内存使用。

10. CPU 与负载管理

在 Linux 系统中,查看 CPU 核心数非常简单,可以使用以下命令来查看当前系统中的 CPU 配置:

  • lscpu 命令

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
      lscpu | grep -i '^cpu(s)'

    ​ 这个命令会输出系统中 CPU 的核心数信息,例如:

    ​ ![image-20250208004559916](./Linux 进程与资源管理/image-20250208004559916.png)

    * **`top` 命令**:在 `top` 命令的实时监控界面中,按下 1 键可以查看每个 CPU 核心的使用情况。理想情况下,所有 CPU 核心都会有进程在执行,这表示 CPU 被充分利用,系统的运行效率最大化。

    理想的 CPU 利用状态是系统中的每个核心都在处理进程。在高并发编程中,目标是让系统的每个 CPU 核心都得到充分的利用,这样才能在多核 CPU 系统中达到最大的工作效率。例如,如果你有 4 个核心的 CPU,理想情况下,应该有 4 个进程同时运行,每个进程占用一个 CPU 核心。这样系统可以最大化地发挥 CPU 的处理能力。如果你的代码或程序设计不当,导致只有一个核心在执行任务,而其他核心空闲,这样就不能充分利用所有的 CPU 核心,导致系统资源浪费。

    * **uptime 命令**:

    ```bash
    # 输出示例
    06:56:12 up 12 days, 3:45, 3 users, load average: 0.22, 0.45, 0.56

    在输出中,load average 后面跟着的是三个数字,分别表示 1 分钟、5 分钟和 15 分钟内的平均负载。要如何解读这些负载值呢?

    • 理想的负载情况:如果这三个值差不多,说明系统运行平稳,负载是均衡的。
    • 负载迅速增加:如果 1 分钟内的负载值远大于 15 分钟的负载值,说明系统在短时间内负载突然上升,可能表示系统正在经历某种突发的工作负载。
    • 负载下降:如果 1 分钟内的负载值小于 15 分钟的负载值,说明系统负载正在下降,负载压力减轻。

    就上面的例子而言:

    • 在过去 1 分钟内,系统的负载平均值为 0.22。

    • 在过去 5 分钟内,系统的负载平均值为 0.45。

    • 在过去 15 分钟内,系统的负载平均值为 0.56。

    这些值相差不大,表明系统负载较为平稳,没有出现过高的负载压力。

11. Stress 负载压力测试

stress 是一个常用的负载测试工具,用于模拟 CPU、内存、磁盘和 I/O 负载等各种场景。它可以帮助你进行性能测试或模拟系统的高负载状态,评估系统的稳定性。

  1. 安装 stress

    1
    sudo apt-get install stress
  2. 进行 CPU 压力测试

    1
    stress --cpu 4 --timeout 60s

    这条命令会启动 4 个进程,进行 60 秒的 CPU 压力测试。

    ![image-20250208005755365](./Linux 进程与资源管理/image-20250208005755365.png)

    其他的还有 pidstatmpstat

    ![image-20250208005952558](./Linux 进程与资源管理/image-20250208005952558.png)

    ![image-20250208010015379](./Linux 进程与资源管理/image-20250208010015379.png)

  3. 内存压力测试

    1
    stress --vm 2 --vm-bytes 1G --timeout 60s

    启动 2 个虚拟内存进程,每个进程分配 1GB 内存,持续 60 秒。

  4. I/O 压力测试

    1
    stress --io 4 --timeout 60s

    进行 I/O 操作压力测试,启动 4 个 I/O 密集型进程,测试持续 60 秒。

四、进程信号与后台管理

1. 使用 kill 发送信号

kill 命令用于向进程发送信号,以请求进程终止或进行其他处理。

基本用法

1
kill <PID>

默认发送 SIGTERM 信号(等于加 15 信号),通常请求进程正常退出。

强制终止

1
kill -9 <PID>

发送 SIGKILL 信号,强制立即终止进程。

**不关闭进程,重新加载其配置文件,如 reload 操作 **:

1
kill -1 <PID>

kill 命令的其他信号参数如下:

![image-20250206233455373](./Linux 进程与资源管理/image-20250206233455373.png)

其中 2 信号等于中断( Ctrl + C);3 信号等于退出(Ctrl + \);15 信号是终止,通常是系统关机时发送;20 信号是暂停进程(Ctrl + Z)。下面可以看到我们使用 Ctrl + Z 暂停进程后,ping 的进程被挂起了,使用 kill 并没有杀掉他,但 ping 并没有响应 SIGTERM,我们需要 SIGKILL(信号 9)强制终止:

![image-20250206234327729](./Linux 进程与资源管理/image-20250206234327729.png)

我们可以再做个实验,在一个终端 ping,在另一个终端使用正常的 kill 命令,看是否会被杀死:

![image-20250206234726903](./Linux 进程与资源管理/image-20250206234726903.png)

![image-20250206234815881](./Linux 进程与资源管理/image-20250206234815881.png)

可以看到,当 ping 不是在暂停状态时,是可以直接被杀死的,不需要强制。

killall 可以将所有同名的进程杀死,但不建议用,容易挨揍。

2. 后台运行命令

使用 & 将命令放入后台

基本用法

1
./long_task.sh &

使用 nohup 保证任务在注销后继续运行

即使非正常退出,nohup + & 的任务也还在,但如果不加 nohup 就不在了。

1
nohup ./backup.sh &

bgfg

bg 是在 Ctrl + Z 后将后台的程序继续运行起来,fg 是将前台运行的程序放到后台运行,后面接查看 jobs 后显示的对应进程的 ID 号,注意无论是不是后台运行该在终端输出的还是会输出。

五、调整进程优先级

1. 使用 nice 启动进程

nice 命令在启动进程时指定其优先级。nice 值越高,进程优先级越低;nice 值范围通常为 -20(最高优先级)到 19(最低优先级)。

基本用法

1
nice -n 10 ./myscript.sh

例子

启动脚本 myscript.sh 时以 nice 值 10 运行,降低其对 CPU 的抢占。

2. 使用 renice 调整正在运行进程的优先级

renice 命令用于修改已运行进程的 nice 值。

基本用法

1
renice -n -5 -p <PID>

例子

调整进程 2345 的优先级,将 nice 值设置为 -5(提高优先级):

1
renice -n -5 -p 2345

六、进程父子关系与特殊进程状态

1. 进程父子关系

![Redis数据持久化- Aphasia’s personal blog](./Linux 进程与资源管理/copy-on-write.png)

  • 每个进程通过 fork() 调用由父进程创建。

  • 父进程子进程通过 PPID(父进程 ID)关联。

    ![image-20250206113610023](./Linux 进程与资源管理/image-20250206113610023.png)

    我们登录的 XShell 终端的流程是:

    1. 系统上运行了 sshd 服务
    2. 通过 ssh 客户端命令都是去连接这个服务而产生的一堆子进程

    可以通过 ps -auxf 查看:

    ![image-20250206113904636](./Linux 进程与资源管理/image-20250206113904636.png)

    我们可以查看对应进程的父子关系来验证:

    ![image-20250206114542373](./Linux 进程与资源管理/image-20250206114542373.png)

    sshd 第一行的进程是 sshd 服务本身,sshd 第二行的进程是 ssh 客户端,远程连接的会话进程。后续的命令都是基于 957499 这个进程生成的子进程,在 bash 的第三行是用户登录解释器产生的 bash 进程, id 是 957500,后续的命令又都是这个 bash 进程生成的子进程。

2. 孤儿进程

父进程退出后(因为某些原因挂了),子进程由 initsystemd 接管(被 1 号进程接替,去管理这些孤儿进程的数据)。

![孤儿进程流程图模板_ProcessOn思维导图、流程图](./Linux 进程与资源管理/63b77cb276213111b4734938.png)

具体可以运行这段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import os
import time

def child_process():
# 第一次输出,可能仍是父进程存在时
print("Child process: PID = {}, Parent PID = {}".format(os.getpid(), os.getppid()))
# 等待父进程退出(父进程突然退出后,孤儿进程会被 init 领养)
time.sleep(3)
# 父进程退出后再次输出,此时父进程 PID 应该变为 1
print("Child process after parent's exit: PID = {}, Parent PID = {}".format(os.getpid(), os.getppid()))

if __name__ == "__main__":
pid = os.fork()
if pid > 0:
# 父进程分支
print("Parent process: PID = {}, Child PID = {}".format(os.getpid(), pid))
# 突然退出,父进程立即结束
os._exit(0)
else:
# 子进程分支
child_process()

![image-20250206141429890](./Linux 进程与资源管理/image-20250206141429890.png)

流程讲解:

  1. 程序调用 os.fork() 创建子进程。
    1. 父进程中,os.fork() 返回子进程的 PID;
    2. 子进程中,返回值为 0。
  2. 父进程打印自己的 PID 和子进程 PID 后,使用 os._exit(0) 立即退出,使子进程成为孤儿进程。
  3. 子进程先打印初始时的父进程 PID,再等待 3 秒(确保父进程已经退出),最后再次打印自己的 PID 和父进程 PID。此时,由于父进程已退出,操作系统会将子进程的父进程设置为 init(在大多数系统中,其 PID 为 1)。

3. 僵尸进程

![僵尸进程(Zombie Process) – 常量笔记](./Linux 进程与资源管理/GIqlONjnXBrkuRz4DXxSdr0WBeSmud5olIwsN9w7B3TRy6IBNgaqaRphuj4PB2194l89F9vjp6GHteXc3txRX5SDJMQVgN6m76YejCRl32TfGJQ.png)

  • 进程退出后,内核保留其退出状态,等待父进程回收;
  • 父进程若不调用 wait(),子进程则成为僵尸;
  • 僵尸进程不会占用大量资源,但过多会耗尽系统进程表。

查看方式

1
ps aux | grep ' Z '

具体例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import os
import time

def main():
pid = os.fork()
if pid > 0:
# 父进程
print("Parent process: PID = {}, Child PID = {} (child will become zombie)".format(os.getpid(), pid))
# 父进程暂停 15 秒,在此期间子进程已退出但未被回收,处于僵尸状态
time.sleep(15)
# 最后回收僵尸进程
os.wait()
print("Zombie child has been reaped.")
else:
# 子进程
print("Child process: PID = {}, Parent PID = {}".format(os.getpid(), os.getppid()))
# 子进程立即退出,进入僵尸状态(直到父进程调用 wait())
os._exit(0)

if __name__ == '__main__':
main()

在调用 os.fork() 后,实际上会产生两个独立的进程:一个是父进程,一个是子进程。这两个进程都会从 os.fork() 这一行开始继续执行接下来的代码,但它们各自拥有不同的返回值:

  • 在父进程中os.fork() 返回的是子进程的 PID(一个正整数),因此条件 if pid > 0 为真,父进程会执行 if 分支的代码。
  • 在子进程中os.fork() 返回 0,所以条件 if pid > 0 为假,子进程会执行 else 分支的代码。os.wait() 会使父进程等待任一子进程结束,并收集其退出状态(也称为回收僵尸进程)。当父进程执行到这行代码时,之前已经退出的子进程将被系统正式回收,其“僵尸状态”消失。

这两个进程是并行运行的,它们共享同一段代码,但各自根据 os.fork() 返回的不同值来选择不同的执行路径。这样就可以同时看到父进程和子进程的输出,而它们的 PID 值分别反映了各自的情况(父进程得到子进程的 PID,子进程得到 0)。

![image-20250206142957326](./Linux 进程与资源管理/image-20250206142957326.png)

上面的代码运行期间,我们可以在另一个窗口通过 ps aux | grep Z 观察到子进程变成僵尸进程(状态为 “Z”):

![image-20250206142932763](./Linux 进程与资源管理/image-20250206142932763.png)

七、挂载点的理解与实例详解

在 Linux 中,整个文件系统是一个统一的目录树,所有的文件和目录都位于根目录 / 下。挂载点(Mount Point)就是在这个目录树中预先创建的一个空目录,用于将其他存储设备(例如硬盘分区、USB 驱动器、网络文件系统等)的文件系统“挂载”到这个目录上,从而将这些外部文件系统整合到主文件系统中,实现统一管理和访问。

1. 挂载点的基本概念

  • 统一文件树
    Linux 的设计理念是“一切皆文件”,整个系统都被构造为一个单一的目录树。无论是系统硬盘、外接存储设备,还是网络文件系统,最终都可以通过挂载点呈现在同一个目录结构中。

  • 挂载操作
    挂载就是把一个独立的文件系统连接到现有目录树的某个目录上,使得这个目录成为访问该文件系统的入口。挂载完成后,用户访问该目录时,实际上访问的是挂载的设备中的文件。

  • 卸载操作
    卸载(umount)则是将挂载的设备从目录树中移除。在卸载之前,必须确保没有进程在使用挂载点,否则卸载操作会失败。

2. 常见的挂载点示例

  • 根目录 (/)
    整个系统的根,所有挂载的文件系统最终都挂载在根目录下。例如,系统安装盘通常直接挂载在 / 上。

  • 临时挂载目录 (/mnt/media)
    用于临时挂载外部设备。例如,当你插入一个 USB 驱动器时,系统可能自动将其挂载到 /media/username/USB_DRIVE 或你手动将其挂载到 /mnt/usb

  • 自定义挂载点
    在服务器环境中,经常为不同用途创建专用挂载点,如 /data 用于存储用户数据,/backup 用于备份文件。这样不仅方便管理,还能根据设备性能合理分配任务(例如将高性能 SSD 挂载在数据库目录下,而将大容量 HDD 用于存储日志文件)。

3. 挂载命令与操作示例

  • 挂载设备
    使用 mount 命令将设备挂载到指定目录。例如,将设备 /dev/sdb1 挂载到 /mnt/data
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
      mount /dev/sdb1 /mnt/data

    ​ 挂载后,访问 /mnt/data 就相当于访问 /dev/sdb1 中的所有文件。

    * **查看当前挂载情况**

    使用 `mount` 或 `findmnt` 查看当前所有挂载的设备及其挂载点:

    ```bash
    mount
    findmnt
  • 卸载设备

    使用 umount 命令卸载挂载的文件系统:

    1
    umount /mnt/data

    注意:在卸载前必须确保没有进程正在使用 /mnt/data。

4. 挂载点在系统管理中的作用

  • 数据整合

    挂载点允许管理员将不同物理设备的文件系统整合到一个统一的目录结构中,便于统一管理。例如,数据库文件可以挂载在一个专用分区(例如 /var/lib/mysql),而用户数据则挂载在另一个分区(例如 /data)。

  • 性能优化

    将性能要求高的应用挂载到高性能设备上(如固态硬盘),而大容量但速度较慢的设备则用于存储归档数据,合理规划存储策略。

  • 备份与恢复

    利用挂载点,可以将外部备份设备挂载到系统中,实现数据的备份与恢复,而无需改变系统的目录结构。

八、综合应用

Nginx 日志非常重要,可以帮助我们查看是否有一些异常的 IP 在进行非授权的访问,例如访问 admin 或者试图访问一些敏感的后台管理文件夹:

![image-20250206233125034](./Linux 进程与资源管理/image-20250206233125034.png)

误删除 nginx 日志文件夹后恢复操作:

![image-20250206233055035](./Linux 进程与资源管理/image-20250206233055035.png)

  • 本文标题:Linux 进程与资源管理
  • 本文作者:Chen Kai
  • 创建时间:2025-02-08 00:00:00
  • 本文链接:https://www.chenk.top/Linux 进程与资源管理/
  • 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
 评论