Menu Close

Linux 进程的构成

进程是 Unix 和 Linux 系统中对正在运行中的应用程序的抽象,通过它可以管理和监视程序对内存、处理器时间和 I / O资源的使用。程序被触发后,执行者的权限与属性、程序的程序代码与所需数据等都会被加载内存中,操 作系统并给予这个内存内的单元一个标识符 (PID),可以说,进程就是一个正在运作中的程序。

进程和程序的对比
进程和程序的对比

进程空间管理图片

进程空间管理图片
进程空间管理图片

 

一)Linux 进程的主要构成部分

Linux 中,进程是程序执行的实例。每个进程都有一系列的属性和资源来支持其运行。以下是 Linux 进程的主要构成部分:

1)进程 ID(PID)

进程 ID(Process ID,PID)是 Linux 操作系统为每个进程分配的唯一标识号。PID 由内核在进程创建时分配,用于管理和跟踪进程

  • 每个进程都有一个唯一的标识符,称为进程 ID(PID)。
  • 它用于标识进程,以便操作系统能够管理和控制进程
  • 默认情况下,PID 的范围为 0 到 32768,可以通过 /proc/sys/kernel/pid_max 查看或修改。
  • PID 0 通常代表 swapper(调度器进程,它负责 CPU 空闲时的任务调度。
  • PID 1 是 initsystemd 进程,负责启动和管理所有其他进程
  • PID 2 代表 kthreadd,是 Linux 内核的线程管理进程
  • 当一个进程结束后,其 PID 可能会被新的进程重新使用。

2)父进程 ID(PPID)

进程 ID(Parent Process ID,PPID) 是指创建当前进程进程进程 ID(PID)。在 Linux 中,所有进程(除了 initsystemd 进程)都有一个父进程。PPID 用于建立进程之间的层级关系。每个进程都有一个父进程 ID(PPID),表示它是由哪个进程创建的。

3)进程状态

Linux 操作系统中,每个进程都有一个状态,表示其当前的执行情况。进程的状态反映了它当前的活动或生命周期阶段,进程状态决定了 CPU 何时调度该进程执行,或者它是否在等待某些事件发生。

状态 缩写 说明
运行(Running) R 进程正在运行或准备运行(等待 CPU 调度)。
可中断睡眠(Sleeping) S 进程在等待某个事件(如 I/O)完成,可以被信号唤醒。
不可中断睡眠(Uninterruptible Sleep) D 进程等待不可中断的事件(如磁盘 I/O),不会被信号唤醒。
停止(Stopped) T 进程被暂停,通常由 SIGSTOPSIGTSTP 信号触发。
僵尸(Zombie) Z 进程已终止,但父进程未回收,仍占用进程表。

如何处理不同进程状态

状态 处理方式
运行(R) 正常进程,无需处理
可中断睡眠(S) 可使用 kill 终止
不可中断睡眠(D) 可能是 I/O 问题,需检查硬件或驱动
停止(T) 可用 kill -CONT <PID> 恢复执行
僵尸(Z) 终止父进程kill <PPID>) 或让父进程调用 wait()
  • Linux 进程有多种状态,最常见的是 运行(R)睡眠(S)
  • 不可中断睡眠(D) 进程可能因 I/O 操作卡住,需检查系统状态。
  • 僵尸进程(Z) 需要父进程回收,或者终止父进程释放资源。
  • 使用 pstop/proc 文件系统可以查看进程状态,使用 kill 终止进程fg/bg 控制进程运行。
READ  Linux 进程的详细解释

4)内存映像

Linux 操作系统中,每个进程在运行时都会占用一定的内存空间,这部分内存称为进程的内存映像(Memory Image)。进程的内存映像由多个不同的区域组成,包括代码段(Text Segment)、数据段(Data Segment)、堆(Heap)、栈(Stack) 等。

4.1) 进程内存布局(Memory Layout)

Linux 进程的虚拟地址空间通常分为以下几个主要部分(从低地址到高地址):

区域 作用 特点
代码段(Text Segment) 存放程序的可执行代码 只读、共享
数据段(Data Segment) 存放已初始化的全局变量和静态变量 读写
BSS 段(BSS Segment) 存放未初始化的全局变量和静态变量 初始值为 0
堆(Heap) 用于动态内存分配(malloc/new 向高地址扩展
共享库(Shared Libraries) 存放动态链接库(*.so 文件) 共享、多进程复用
栈(Stack) 存放函数调用、局部变量、返回地址 向低地址扩展

4.2) 进程内存布局示意图

Process 进程内存布局示意图
Process 进程内存布局示意图

4.3) 进程的内存区域详解

每个进程都拥有自己独立的内存空间,其中包括以下几个区域:

  • 代码段(Text Segment)
  • 数据段(Data Segment)
  • 堆(Heap)
  • 栈(Stack):用于存储函数的局部变量和函数调用的返回地址。
  • BSS段:存储未初始化的全局变量。

4.3.1)代码段(Text Segment)- 存储程序的可执行代码。存放程序代码。正文段具有只读的属性。

  • 代码段存放可执行代码,即编译后的指令集。
  • 该段通常是只读的,防止进程无意中修改指令。
  • 代码段可以被多个进程共享,从而减少内存使用。

例如

int main() {
    printf("Hello, world!\n");
    return 0;
}

 

4.3.2)数据段(Data Segment)

用户数据段(user segment):是进程在运行过程中处理数据的集合,它们是 进程直接进行操作的所有数据(包括全部变量在内),以及进程使用的进程堆栈, 系统数据段(system segment):存放着进程的控制信息,即进程控制块(PCB ,Processing Control Block),名字为task_struct的数据结构。 存储静态变量和初始化的全局变量。

  • 数据段存放已初始化的全局变量和静态变量
  • 该段是可读写的,进程运行时可以修改其中的值。
  • 用户数据段
    Linux系统把进程的数据段又划分成三部分:

1、用户栈区(供用户程序使用的信息区);
2、用户数据区(包括用户工作数据和非可重入的程序段);
3、系统数据区(包括系统变量和对换信息)

  • 正文段
    程序段是可重入的程序,能被若干进程共享。为了管理可共享的正文段,Linux设置了一张正文表,每个正文段都占用一个表目,用来指出该正文段在内存和磁盘上的位置、段的大小以及调用该段的进程数等情况。

process进程的正文段(text)和用户数据段

例如

int global_var = 10;  // 存放在数据段
static int static_var = 20;  // 也存放在数据段

4.3.3) BSS 段(未初始化数据段)

  • BSS(Block Started by Symbol)存放未初始化的全局变量和静态变量
  • BSS 段的内容默认初始化为 0,但不会占用可执行文件的实际空间(仅在运行时分配)。

例如

int global_var_uninit;  // 存放在 BSS 段,值默认为 0
static int static_var_uninit;  // 也存放在 BSS 段

4.3.4) 堆(Heap)

  • 堆用于动态内存分配(如 malloc()calloc()new)。
  • 向高地址扩展,如果堆空间不足,会触发 brk()mmap() 系统调用扩展堆空间。
  • 堆分配的内存需要手动释放,否则可能会导致内存泄漏

 示例

#include <stdlib.h>

int main() {
    int *p = (int *)malloc(sizeof(int) * 10);  // 在堆上分配 10 个 int
    free(p);  // 释放内存,避免内存泄漏
    return 0;
}

malloc() 申请的内存来自free() 释放后归还给系统。

4.3.5) 共享库(Shared Libraries)

  • 进程运行时使用的动态链接库(*.so 被映射到此区域。
  • 多个进程可以共享同一个共享库,提高内存利用率。
  • 通过 ldd 命令可以查看进程依赖的共享库:
ldd /bin/ls

输出

linux-vdso.so.1 =>  (0x00007fff5d1d3000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f0cbb24a000)
/lib64/ld-linux-x86-64.so.2 (0x00007f0cbb60a000)

4.3.6) 栈(Stack)

用于存储函数的局部变量和函数调用的返回地址。

  • 栈用于函数调用、局部变量存储,包括:
    • 函数的返回地址
    • 局部变量
    • 参数
  • 向低地址扩展,如果栈溢出,会触发 segmentation fault
  • 每个线程都有自己的栈。
READ  什么是进程的文件描述符(File Descriptors)?

例如,

void func() {
    int x = 10;  // 局部变量 x 存放在栈上
}

x 变量分配在上,函数返回后被自动释放。

4.4)进程的内存映射

Linux 允许进程使用 mmap() 系统调用直接映射文件或设备到进程地址空间。可以通过 /proc/<PID>/maps 查看进程的内存映射情况。

例如

cat /proc/$(pgrep bash)/maps

结果

00400000-00452000 r-xp 00000000 08:01 1234567  /bin/bash
00651000-00652000 r--p 00051000 08:01 1234567  /bin/bash
00652000-00653000 rw-p 00052000 08:01 1234567  /bin/bash
  • r-xp:可读、可执行、私有(代码段)。
  • rw-p:可读、可写、私有(数据段、堆、栈)。

4.5)进程内存查看工具

  • pmap进程内存映射)
  • top / htop(监控进程内存使用)
  • free(查看系统内存)

 

五)文件描述符(File Descriptors)

Linux 和 Unix 系统中,文件描述符(File Descriptor,FD)进程用于访问文件、管道、套接字、设备等的一种抽象标识符。

本质上,它是一个非负整数,用于标识进程打开的文件或 I/O 资源。

  • 每个进程都有一个文件描述符表,存储该进程打开的所有文件或 I/O 资源。
  • 文件描述符是进程级别的,每个进程都有自己独立的描述符表。
  • 文件描述符从 0 开始分配,默认有三个标准输入/输出/错误:
文件描述符 名称 作用 默认设备
0 标准输入(stdin) 读取输入数据 键盘
1 标准输出(stdout) 输出正常信息 终端
2 标准错误(stderr) 输出错误信息 终端

一个文件描述符的生命周期包括:

  1. 分配进程使用 open()socket()pipe() 等系统调用打开文件或资源,系统返回一个文件描述符。
  2. 使用进程通过 read()write()send()recv() 等操作读写文件描述符指向的资源。
  3. 关闭进程调用 close(fd) 释放文件描述符,防止资源泄漏。

六)进程权限

    • 每个进程都有与之关联的用户 ID(UID)和组 ID(GID)。
    • UID 表示进程的所有者,GID 表示进程所属的组。
    • 这些权限决定了进程可以访问哪些资源和执行哪些操作。

七)进程之间的通信信号(Signals)

Linux 和 Unix 系统中,信号(Signal) 是一种进程间通信(IPC)机制,用于通知进程发生了某种事件。信号是一种异步的通知方式,操作系统或其他进程可以随时向目标进程发送信号,以让其执行特定的操作,如终止、暂停、继续运行等。

  • 信号是进程间的异步通知机制,可以用于通知进程某个事件发生,如中断、终止、挂起等。
  • 进程可以接收、忽略、处理信号(但某些信号无法被捕获或忽略,如 SIGKILL)。
  • 信号是由 内核、用户、硬件 触发的。
  • 进程可以使用 kill 命令或 kill() 系统调用向其他进程发送信号。
Linux支持的信号signal列表
Linux支持的信号signal列表

八)调度信息

    • 进程调度是 Linux 内核管理 CPU 时间片分配的机制,进程的优先级(或称为调度策略)决定了它在 CPU 上的执行顺序。

九)上下文信息

十)PCB进程控制块

Linux成功fork进程后,会在系统中创建一个task_struct(也称PCB, process control block),用来描述当前进程的状态、进程间关系、优先级和资源等信息。

PCB(进程控制块) 结构体 task struct,负责管理进程的所有资源,它的成员 mm_struct 指向这个进程相关的内存资源,mm_struct指向一个结构体,包括:

  • 栈 :给局部变量(自动变量)分配空间的地方
  • 堆 :使用malloc、new… 分配的空间(也叫自由区)
  • BSS段
  • 数据段
  • 代码段:代码区是只读的,程序代码会被读入此区,程序执行期间执行的就是代码区中的代码。
  • 进程控制块(PCB)是操作系统用来保存进程信息的数据结构。
  • PCB 包含进程的所有信息,如 PID、父进程 PID、进程状态、寄存器值、内存管理信息等。
    READ  Linux系统的构成
    在Linux成功fork进程后,会在系统中创建一个task_struct(也称PCB, process control block),用来描述当前进程的状态、进程间关系、优先级和资源等信息
    Linux成功fork进程后,会在系统中创建一个task_struct(也称PCB, process control block),用来描述当前进程的状态、进程间关系、优先级和资源等信息
    • 标识符: 与进程相关的唯一标识符,用来区别正在执行的进程和其他进程
    • 状态: 描述进程的状态,因为进程有挂起,阻塞,运行等好几个状态,所以都有个标识符来记录进程的执行状态。
    • 优先级: 如果有好几个进程正在执行,就涉及到进程被执行的先后顺序的问题,这和进程优先级这个标识符有关。
    • 程序计数器: 程序中即将被执行的下一条指令的地址。
    • 内存指针: 程序代码和进程相关数据的指针。
    • 上下文数据: 进程执行时处理器的寄存器中的数据。
    • I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表等。
    • 记账信息: 包括处理器的时间总和,记账号等等。

    PCB中包含4个部分

    1.进程标识信息:用于唯一地标识一个进程,一个进程通常有两种标识符:

    • 内部标志符: 由操作系统赋予每个进程的一个唯一的数字标识符,它通常为一个进程的序号,方便了系统使用。
    • 外部标识符: 由创建者产生,是由字母和数字组成的字符串,为用户进程访问该进程提供方便。
    • 为了描述进程间的家族关系,通常还设有父进程标识和子进程标识,以表示进程间的家族关系。
    • 此外,还设有用户名或用户标识号表示该进程属于哪个用户。

    2.处理机状态

    • 处理机状态信息主要由处理机的各个寄存器内的信息组成。 进程运行时的许多信息均存放在处理机的各种寄存器中。其中 程序状态字(PSW) 是相当重要的,处理机根据程序状态寄存器中的PSW来控制程序的运行。

    3.进程调度信息

    • 进程状态: 标识进程的当前状态(就绪、运行、阻塞),作为进程调度的依据。
    • 进程优先级: 表示进程获得处理机的优先程度。
    • 进程调度算法提供依据的其他信息:例如,进程等待时间、进程已经获得处理器的总时间和进程占用内存的时间等。
    • 事件: 是指进程由某一状态转变为另一状态所等待发生的事件。(比如等待I/O释放)

    4.进程控制信息

    • 程序和数据地址: 是指组成进程的程序和数据所在内存或外存中的首地址,以便在调度该进程时能从其PCB中找到相应的程序和数据。
    • 进程同步和通信机制: 指实现进程同步和通信时所采取的机制,如消息队列指针和信号量等,他们可以全部或部分存在PCB中。
    • 资源清单: 列出了进程所需的全部资源 及 已经分配给该进程的资源,但不包括CPU.
    • 链接指针: 它给出了处于同一队列中的下一个PCB的首地址。

    一个 Linux 进程由多个部分组成,包括进程 ID、内存映像、文件描述符、权限、调度信息等。操作系统利用这些信息管理和控制进程的创建、执行、暂停、终止等各个生命周期步骤。

    除教程外,本网站大部分文章来自互联网,如果有内容冒犯到你,请联系我们删除!

    发表回复

    您的邮箱地址不会被公开。 必填项已用 * 标注

    Leave the field below empty!

    Posted in 进程

    Related Posts