Featured image of post Linux 进程的生与死

Linux 进程的生与死

在 Linux 系统中,进程的生命周期是一个复杂而有趣的过程。本文将详细解析 Linux 进程的创建、运行、死亡以及相关机制。

在 Linux 系统中,进程的生命周期是一个复杂而有趣的过程。本文将详细解析 Linux 进程的创建、运行、死亡以及相关机制。

进程的诞生

每当一个新进程被创建时,实际上是通过 forkclone 系统调用将现有进程分裂成两个独立的进程。通常,新创建的进程会立即调用 execve 系统调用来替换当前正在执行的二进制文件。例如,当你在 Bash 中运行 ls 命令时,Bash 首先会使用 fork 创建一个子进程,然后子进程通过 execve 调用将自身转变为 ls 命令。ls 执行完毕后,子进程会结束,返回到原来的 Bash 进程。

在实际应用中,程序几乎不会直接调用这些系统调用,而是使用 libc 封装函数或类似 system 这样的 libc 函数。这些函数在底层使用 forkexecve 或它们的变体。

Image

进程的死亡

进程通常通过调用 exitexit_group 系统调用来结束自身。如果程序员没有显式调用 exit 而是从 main 返回,编译器会自动调用 exit。如果程序没有使用 libc 编译且没有显式调用 exit,从 main 返回会导致段错误或其他关键信号,因为返回操作会试图从堆栈中弹出非法的返回地址。

此外,进程也可能通过信号(如 SIGTERMSIGKILL)来终止。最后一种终止进程的方式是直接关闭计算机。

进程的身份与僵尸进程

每个进程都有一个唯一的 PID(进程标识符),直到内核在进程结束后回收该 PID。在一个进程的 PID 被回收之前,父进程需要调用 waitwaitpidwaitid 来等待子进程结束。如果父进程不等待子进程,子进程将成为僵尸进程,继续占用内核的进程表资源。如果父进程本身结束,子进程将重新分配给 PID 为 1 的进程(通常是 init 进程),init 会自动调用 wait 以释放子进程资源。

线程:次要进程

Image

在 Linux 中,线程实际上是共享相同内存和一些其他资源的独立进程。线程通过调用带有适当标志的 clone 系统调用创建。在内核术语中,每个线程都有自己独特的 PID,但同一进程中的所有线程共享相同的 TGID(线程组 ID),这等同于第一个线程的 PID。因此,从内核的角度来看,PID 实际上标识线程,TGID 标识进程,对于单线程进程,PID 等于 TGID。

进程管理

管理 Linux 进程涉及多种技术和工具。系统管理员需要了解如何创建、监视和终止进程,以确保系统的稳定性和效率。

  1. 创建进程:使用 forkclone 创建新进程,通常结合 execve 执行新程序。
  2. 监视进程:使用 pstophtop 等工具监视系统中的活动进程。ps 命令可以显示当前进程的快照,而 tophtop 提供动态实时视图。
  3. 终止进程:使用 kill 命令发送信号终止进程。kill 可以发送各种信号,但最常用的是 SIGTERM(请求进程终止)和 SIGKILL(强制进程终止)。

进程间通信

Linux 提供了多种进程间通信(IPC)机制,包括信号、管道、消息队列、共享内存和信号量。每种机制都有其特定的用途和适用场景。

  • 信号:用于通知进程某些事件的发生。
  • 管道:用于在父子进程之间传输数据。
  • 消息队列:允许进程以消息的形式交换数据。
  • 共享内存:提供最快的 IPC 方法,因为进程可以直接访问共同的内存区域。
  • 信号量:用于进程间同步,防止资源竞争。

进程优先级与调度

Linux 使用调度程序来决定进程的执行顺序和时间。进程的优先级可以通过 nicerenice 命令进行调整。优先级值越低,进程的优先级越高。

  • 实时优先级:用于实时任务,确保关键任务在规定时间内执行。
  • 普通优先级:用于一般任务,系统根据优先级和调度策略分配 CPU 时间。

进程状态

Image

一个进程在其生命周期中会经历多个状态,包括:

  • 运行中:进程正在使用 CPU 。
  • 睡眠中:进程正在等待某个事件(如 I/O 操作完成)。
  • 停止:进程被暂停,通常是因为收到了 SIGSTOP 信号。
  • 僵尸:进程已经终止,但其退出状态信息尚未被父进程读取。

了解进程的生命周期和管理技术对于有效使用 Linux 系统至关重要。通过掌握这些知识,系统管理员和开发者可以优化系统性能,确保应用程序的稳定运行。

位旅人路过 次翻阅 初次见面