进程与线程的数据结构

1. 进程的数据结构

在Linux内核中,进程(Process)使用 task_struct 数据结构来表示。该结构包含了进程的所有信息,下面列出一些关键字段:

  • PID: 进程ID。
  • state: 进程当前的状态,比如运行中(TASK_RUNNING)、休眠(TASK_INTERRUPTIBLE)等。
  • mm_struct: 进程的内存管理信息,如内存映射、虚拟地址空间等。
  • signal_struct: 信号处理相关的信息。
  • task_list: 用于将进程链接到进程链表中的指针,形成进程的调度队列。
  • sched_class: 调度类,用于定义进程的调度策略,如CFS(Completely Fair Scheduler)。
  • thread_info: 保存与线程相关的特定信息,比如栈指针、寄存器状态等。

一个进程的生命周期从 fork() 系统调用开始,创建一个新进程时,父进程会复制自身的某些信息给子进程,例如文件描述符、内存映射等。

2. 线程的数据结构

线程(Thread)在Linux中是轻量级进程,其本质上与进程共享同一地址空间。在Linux中,线程和进程的管理是通过 task_struct 统一管理的,但与进程不同的是,线程共享一部分资源,如地址空间、文件描述符等。

  • 共享资源:多个线程之间共享内存、打开的文件、信号处理器等。
  • 独立资源:每个线程有自己独立的堆栈和寄存器集。

在Linux中,线程的创建通过 clone() 系统调用,它允许指定新线程共享的资源。

二、进程与线程的创建流程

1. 进程的创建流程

进程的创建通常由 fork() 系统调用完成,其流程如下:

  • 父进程调用fork(): 父进程调用 fork() 系统调用。
  • 内核分配资源: 内核为新进程分配 task_struct 并复制父进程的所有信息。
  • PID分配: 内核为新进程分配一个唯一的进程ID。
  • 内存结构复制: 子进程的内存空间是父进程的一个“写时复制”(copy-on-write),即子进程在第一次写操作时才会真正复制父进程的内存区域。
  • 调度: 子进程会被加入到调度队列,等待被内核调度运行。

另一个常用的创建进程的系统调用是 exec(),它允许替换进程的地址空间,从而运行新的程序。

2. 线程的创建流程

线程的创建通常使用 pthread_create(),而底层则是使用 clone() 系统调用。线程创建的步骤如下:

  • 用户空间调用pthread_create(): 应用程序调用 pthread_create() 来创建一个新线程。
  • 内核调用clone(): 内核调用 clone() 系统调用,在创建线程时,可以通过参数指定哪些资源需要共享(如地址空间、文件描述符等)。
  • 创建线程控制块: 内核会为新线程分配 task_struct,但新线程与父线程共享内存空间和其他资源。
  • 加入调度队列: 新线程被加入到调度队列,等待被调度执行。

三、进程与线程的调度

1. 调度器的概念

Linux内核中的调度器负责决定哪个进程或线程在何时执行。调度器的核心是基于优先级、时间片和调度策略。调度的主要目标是实现多任务处理,提升系统响应速度和吞吐量。

2. 调度策略

Linux的调度器有多种策略,常见的有:

  • SCHED_OTHER: 默认的时间共享调度策略,使用CFS(完全公平调度器)。
  • SCHED_FIFO: 实时调度策略,基于先入先出,优先级高的进程先调度,且不会被时间片剥夺。
  • SCHED_RR: 实时轮转调度,每个进程分配固定的时间片,轮流调度。
  • SCHED_IDLE: 低优先级进程,只有在系统空闲时才会被调度。

3. CFS(完全公平调度器)

CFS 是Linux默认的调度器,它通过维护一个红黑树来追踪所有进程的“虚拟运行时间”,虚拟运行时间最短的进程被优先调度。

CFS 主要的机制包括:

  • vruntime: 每个进程都有一个 vruntime(虚拟运行时间),表示进程运行的时间。CFS 调度器通过比较 vruntime 来选择最优先的进程。
  • 时间片: CFS根据进程的优先级分配时间片,优先级高的进程会获得更多的运行时间。

4. 进程调度流程

  • 准备调度: 当进程的时间片耗尽,或有一个优先级更高的进程进入可运行状态时,当前进程会被挂起。
  • 选择进程: 调度器遍历任务队列或红黑树,选择下一个要执行的进程。
  • 上下文切换: 内核进行上下文切换,保存当前进程的CPU状态,加载新进程的状态,并让新进程获得CPU的控制权。

5. 线程调度流程

线程调度与进程调度在Linux内核中几乎相同。线程也是通过 task_struct 进行管理和调度的,线程会像进程一样根据调度策略和优先级来分配时间片。

6. 多核处理器调度

在多核系统中,Linux内核的调度器支持同时调度多个进程。每个CPU都有一个独立的运行队列,调度器通过负载均衡机制来平衡每个CPU上的任务负载。

负载均衡: 调度器定期检查各个CPU的负载情况,如果发现某个CPU负载过重,会将任务从繁忙的CPU迁移到空闲的CPU上,以实现更均衡的负载分配。

总结来说,Linux的进程和线程在内核中的管理和调度机制高度统一,核心数据结构 task_struct 扮演了关键角色。而调度器则基于调度策略、优先级、时间片等来实现对进程和线程的高效管理。