[Linux]Processes

Processes

1 ps

进程是指在机器上运行的程序,由内核进行管理。每个进程都有一个与之关联的标识符,称为进程ID(PID)。PID按照进程创建顺序分配。

使用ps命令可以查看当前正在运行的进程列表:

1
$ ps

输出示例:

1
2
3
PID        TTY     STAT   TIME          CMD
41230 pts/4 Ss 00:00:00 bash
51224 pts/4 R+ 00:00:00 ps

这里各列含义如下:

  • PID:进程ID。
  • TTY:与进程相关的控制终端。
  • STAT:进程状态代码。
  • TIME:总CPU使用时间。
  • CMD:可执行文件或命令名称。

ps命令有多种选项,不同风格(BSD、GNU或Unix)有不同的参数。其中,BSD风格较为常用。

使用以下命令获取更详细的进程信息:

1
$ ps aux
  • a:显示所有用户的全部进程。
  • u:显示关于进程的详细信息。
  • x:列出没有控制终端的进程,这些进程在TTY字段中显示为?,通常为系统启动时自动运行的守护进程。

这将展示更多的字段,包括但不限于:

  • USER:有效用户。
  • %CPU:进程使用的CPU时间占总运行时间的比例。
  • %MEM:进程使用的物理内存比率。
  • VSZ:整个进程的虚拟内存使用量。
  • RSS:常驻集大小,即任务已使用的非交换物理内存。
  • START:进程启动时间。

使用top命令实时监控

top命令提供了一个动态的实时视图,显示系统中各个进程资源使用情况,默认每10秒刷新一次。这对于监控占用大量系统资源的进程非常有用。

1
$ top

2 终端类型与控制终端

ps命令的输出中,存在一个TTY字段,它表示执行命令的终端。终端分为两种类型:常规终端设备和伪终端设备。

常规终端设备 vs 伪终端设备

  • 常规终端设备:指的是可以直接输入并发送输出到系统的本地终端设备。例如,通过按下Ctrl-Alt-F1组合键进入的第一个虚拟控制台(TTY1),这里仅显示终端,没有图形界面等。要退出此模式,可以使用Ctrl-Alt-F7组合键。

  • 伪终端设备:是我们通常使用的终端模拟器,如shell窗口中的终端,标记为PTS(Pseudo Terminal Slave)。通过再次运行ps命令,您可以看到自己的shell进程位于pts/*之下。

控制终端及其重要性

一般而言,进程通常绑定到一个控制终端。例如,在shell窗口中运行如find这样的程序时,如果关闭了该窗口,则相关进程也会随之终止。

然而,有些特殊进程,如守护进程(daemon),它们对于保持系统正常运行至关重要。这类进程通常在系统启动时开始运行,并在系统关机时终止。由于这些进程在后台运行且需要持续工作而不受用户操作的影响,因此它们不绑定到任何控制终端。在ps命令的输出中,这类进程的TTY字段将显示为?,表示没有控制终端。

3 深入理解进程

什么是进程?

进程是系统上运行的程序实例。具体来说,它是操作系统分配内存、CPU和I/O资源以使程序得以运行的结果。为了更好地理解这一点,您可以尝试以下实验:打开三个终端窗口,在其中两个窗口中运行cat命令(不带任何选项),因为cat命令等待标准输入(stdin),所以它将保持进程开启状态。然后,在第三个窗口中执行ps aux | grep cat命令。您会发现有两个名为cat的进程存在,尽管它们调用的是相同的程序。

内核的角色

内核负责管理所有进程。当我们运行一个程序时,内核会将程序代码加载到内存中,确定并分配必要的资源,并持续跟踪每个进程的状态。内核掌握的信息包括:

  • 进程的状态
  • 进程使用的资源及接收情况
  • 进程的所有者
  • 信号处理
  • 以及其他相关信息

资源分配与管理

所有进程都在竞争系统资源,而内核的任务就是根据进程的需求合理分配这些资源。当一个进程结束时,它所占用的资源会被释放出来供其他进程使用。内核确保每个进程都能获得适当数量的资源,从而维持系统的高效运行。

4 进程创建与管理

创建新进程

当创建一个新进程时,现有进程使用所谓的“fork”系统调用(基本上复制了自己。Fork系统调用会生成一个几乎完全相同的子进程,该子进程将获得一个新的进程ID(PID),而原始进程则成为其父进程,并拥有一个称为父进程ID(PPID)的标识符。之后,子进程可以选择继续使用与其父进程相同的程序,但更常见的情况是使用execve系统调用来启动一个新程序。此系统调用会销毁内核为原进程设置的内存管理,并为新程序建立新的内存管理。

我们可以通过以下命令观察这一过程:

1
ps l

l选项提供了一个“长格式”或更加详细的运行进程视图。您会看到一列标记为PPID的字段,这是父进程ID。

所有进程的起源

既然每个进程都需要一个父进程,并且它们都是通过相互克隆形成的,那么必然存在一个所有进程的源头。确实如此,当系统启动时,内核会创建一个名为init的进程,它的PID为1。除非系统关闭,否则init进程无法被终止。它以root权限运行,负责执行许多维持系统运作的进程。

5 进程终止与管理

终止进程

进程可以通过_exit系统调用退出,这将释放该进程占用的资源以供重新分配。当一个进程准备终止时,它会通过所谓的“终止状态”告知内核其终止原因。通常情况下,状态码0表示进程成功结束。然而,这还不足以完全终止一个进程。父进程必须使用wait系统调用来确认子进程的终止,并检查子进程的终止状态。

另一种终止进程的方式是通过信号,之后会讨论。

孤儿进程

如果父进程在子进程之前终止,内核会意识到不会收到对该子进程的wait调用。因此,这些子进程会被标记为“孤儿”,并由init进程接管。最终,init进程会对这些孤儿进程执行wait系统调用,以便它们能够被彻底清除。

僵尸进程

当子进程终止而父进程尚未调用wait时,会发生什么呢?内核会将其转换为僵尸进程。子进程使用的资源已被释放用于其他进程,但在进程表中仍保留有这个僵尸进程的条目。僵尸进程无法被杀死,因为它们实际上已经是“死亡”的状态,所以不能通过信号来消灭它们。最终,若父进程调用了wait系统调用,僵尸进程将会消失,这一过程被称为“收割”。如果父进程没有执行wait调用,init进程将收养这些僵尸进程,并自动执行wait来移除它们。过多的僵尸进程可能会占据进程表的空间,影响新进程的运行,因此这是应当避免的情况。

6 信号

信号是一种通知机制,用于告知进程某事件已经发生。

为何使用信号?

  • 用户可以通过输入特定的终端字符(如Ctrl-C或Ctrl-Z)来终止、中断或挂起进程。
  • 硬件问题出现时,内核可能需要通知相关进程。
  • 软件问题发生时,内核也可能需要通知进程。
  • 它们是进程间通信的一种方式。

信号处理过程

当某个事件产生一个信号后,该信号会被传递给相应的进程,并处于待处理状态直到被接收。如果进程正在运行,信号将会被传递。然而,进程可以通过设置信号掩码来阻止某些信号的传递。当信号被传递时,进程可以执行以下操作之一:

  • 忽略该信号。
  • “捕获”信号并执行特定的处理程序。
  • 进程终止。
  • 根据信号掩码阻塞信号。

常见信号

每个信号都由整数定义,并具有形式为SIGxxx的符号名。一些常见的信号包括:

  • **SIGHUP (1)**:挂断
  • **SIGINT (2)**:中断
  • **SIGKILL (9)**:强制结束进程
  • **SIGSEGV (11)**:段错误
  • **SIGTERM (15)**:软件终止请求
  • SIGSTOP:暂停进程

信号编号可能会有所不同,因此通常通过其名称引用。值得注意的是,某些信号无法被阻塞,例如SIGKILL,它将直接销毁进程。

7 kill

在Linux系统中,kill命令用于向进程发送信号以终止其运行。例如:

1
$ kill 12445

这里的12445是目标进程的PID(进程标识符)。默认情况下,kill命令发送的是SIGTERM信号,请求进程正常终止并释放资源、保存状态。

也可以通过kill命令指定发送不同的信号,如使用-9选项发送SIGKILL信号来强制结束进程:

1
$ kill -9 12445

这将立即杀死进程,不给予任何清理机会。

常见信号及其区别

尽管以下信号听起来相似,但它们之间存在显著差异:

  • **SIGHUP (挂断)**:当控制终端关闭时发送给进程。例如,如果你关闭了一个正在运行某个进程的终端窗口,则该进程会收到一个SIGHUP信号。
  • **SIGINT (中断)**:可通过按下Ctrl-C触发,通知系统尝试优雅地终止进程。
  • **SIGTERM (软件终止)**:请求进程终止,并允许它进行必要的清理操作。
  • **SIGKILL (强制终止)**:无条件地终止进程,不做任何清理工作,常被称为“强制杀死”。
  • **SIGSTOP (暂停)**:停止或挂起进程的执行。

8 niceness

当计算机同时运行多个程序,如Chrome、Microsoft Word或Photoshop时,这些程序看似并行执行,但实际上它们是通过分时共享CPU来实现的。每个进程仅在短时间内——即所谓的时间片内使用CPU资源,然后暂停几毫秒以让位于其他进程。操作系统默认采用轮询调度算法分配时间片,确保所有进程均得到足够的处理时间。此过程由内核管理,并且大多数情况下能够高效地完成。

进程优先级与Nice值

进程无法自行决定何时以及多久可以占用CPU;若无干预,各进程大致会获得相等的CPU时间。然而,Linux系统允许用户通过设置“nice”值来影响内核的调度决策。“Nice”值决定了进程获取CPU资源的优先级:较高的数值意味着较低的优先级(更“友善”的进程),而较低甚至负数的值则表示较高优先级(较“不友善”的进程)。

查看进程的“nice”值可以通过top命令:

1
$ top

在显示的结果中,“NI”列表示的就是各个进程的“nice”级别。

调整进程优先级

使用nicerenice命令可调整进程的优先级:

  • nice命令用于启动新进程时设定其优先级:

    1
    $ nice -n 5 apt upgrade
  • renice命令则用于修改已存在进程的优先级:

    1
    $ renice 10 -p 3245

9 进程状态

执行ps aux命令后,在“STAT”列中可以看到各种状态代码,它们代表Linux进程中可能处于的不同状态。以下是常见的状态代码及其含义:

  • R:运行或可运行状态。这意味着进程正在运行或等待CPU处理。
  • S:中断休眠状态。进程正在等待某个事件完成,例如来自终端的输入。
  • D:不可中断休眠状态。这些进程无法被信号杀死或中断,通常需要通过重启系统或解决问题来清除。
  • Z:僵尸状态。这是指已终止但仍在等待其状态被收集的进程。
  • T:已停止状态。表示进程已被挂起或停止。

10 /proc 文件系统

Linux中的进程与/proc文件系统

在Linux中,所有事物都被视为文件,这包括进程。进程信息存储在一个特殊的文件系统:/proc文件系统中。

执行以下命令列出/proc目录下的内容:

1
$ ls /proc

该目录下包含多个子目录,每个运行中的进程ID(PID)都有一个对应的子目录。通过查看ps命令的输出,可以找到特定进程的PID,并在/proc目录下对应地访问其信息。

例如,要查看PID为12345的进程状态,可以使用cat命令读取其状态文件:

1
$ cat /proc/12345/status

这将展示有关该进程的状态信息及更详细的资料。通过/proc目录,内核提供了对系统状态的视图,因此这里的信息比ps命令显示的更为详尽和全面。

11 job control

在单一终端窗口中管理长时间运行的进程

当在单一终端窗口中执行一个耗时较长的命令,该命令会占用整个shell直到完成。为了继续使用机器,可以将进程置于后台运行。以下是如何通过作业(jobs)控制进程运行的方式:

将任务发送到后台

在命令后添加一个&符号,可以让命令在后台运行,同时释放shell以便进行其他操作。示例如下:

1
2
3
$ sleep 1000 &
$ sleep 1001 &
$ sleep 1002 &

查看所有后台作业

要查看刚刚发送到后台的任务,可以使用jobs命令:

1
$ jobs

输出示例:

1
2
3
[1]    Running     sleep 1000 &
[2]- Running sleep 1001 &
[3]+ Running sleep 1002 &

这显示了每个作业的ID、状态及所运行的命令。带有+号标记的作业是最新的后台作业,而-号标记的则是次新作业。

将现有作业发送到后台

对于已经启动但未设为后台运行的任务,无需终止它重新开始。首先通过Ctrl-Z暂停作业,然后使用bg命令将其置于后台:

1
2
3
4
5
pete@icebox ~ $ sleep 1003
^Z
[4]+ Stopped sleep 1003
pete@icebox ~ $ bg
[4]+ sleep 1003 &

再次使用jobs命令确认更新后的状态。

将后台作业移至前台

要将后台作业移到前台,指定相应的作业ID即可。如果直接运行fg不带参数,则默认将最新的后台作业(即带有+号标记的作业)带回前台:

1
$ fg %1

终止后台作业

类似地,可以通过指定作业ID来终止后台进程:

1
kill %1

[Linux]Processes
https://erlsrnby04.github.io/2025/03/21/Linux-Processes/
作者
ErlsrnBy04
发布于
2025年3月21日
许可协议