Linux-----进程、线程、协程的生命周期、调度器slab

发布时间 2023-09-25 22:29:10作者: 不会笑的孩子

Linux进程、线程、协程的区别

进程

进程是操作系统中的一个独立执行单元。
每个进程都有自己的独立内存空间,包括代码段、数据段、堆栈等。
进程之间通常需要通过进程间通信(IPC)来交换数据和信息。
进程启动和销毁开销较大,因为需要分配和释放独立的内存空间。
进程之间隔离度高,一个进程的崩溃不会直接影响其他进程。

线程

进程内的一个执行单元,共享进程的内存空间。
可以看作是轻量级的进程,它们之间共享相同的地址空间和资源。
之间可以更轻松地通信,因为它们共享相同的内存。
线程的启动和销毁开销相对较小,因为它们共享进程的资源。
线程之间共享进程的文件描述符和打开文件等。

协程

是一种轻量级的执行单元,不同于线程和进程,它由程序员显式控制,而不是由操作系统调度。
通常运行在同一个线程内,不涉及线程切换或进程切换。
可以被看作是用户态线程,它们由应用程序自己管理,不依赖于操作系统的调度。
通常用于处理高并发、异步编程或需要大量I/O操作的情况,因为它们可以避免线程切换的开销。
的切换由程序员控制,更容易理解和调试

  • 开销:
    创建和销毁进程通常比线程开销更大,因为进程需要分配独立的地址空间和资源。这使得进程的创建和销毁相对较慢。
    线程的创建和销毁开销较小,因为它们共享相同的进程资源。线程的切换也比进程的切换快速。
  • 通信:

进程间通信(IPC)通常需要额外的系统调用,如fork()、exec()、wait()等,或者使用专门的IPC机制。
线程之间通信更容易,可以直接共享进程内存,通过全局变量或互斥锁****等机制进行数据共享和同步。

  • 容错性:

进程之间是相互独立的,一个进程的崩溃通常不会影响其他进程。
线程共享相同的地址空间,一个线程的错误可能导致整个进程崩溃,因此线程之间的容错性较差。
并行性:

进程通常用于实现多个独立的任务,可以在多核处理器上并行执行。
线程更适合执行并行任务,因为它们共享相同的资源和地址空间,更容易实现数据共享和协同工作。
资源管理:
进程之间的资源管理较为独立,一个进程的资源不会直接影响其他进程。
线程之间的资源共享需要谨慎管理,以避免竞态条件和死锁等问题。
总的来说,进程和线程都有各自的优势和适用场景。进程适合处理相对独立的任务,而线程适合实现并行计算和更轻量级的任务划分。在实际应用中,通常需要根据具体需求和性能要求来选择使用进程还是线程。

进程的描述及task_struct(include/linux/sched.h&&task.h&&kernel/sched/sched.h&&include/fork.c&&kernel/exit.c)

  • include/linux/sched.h:task_struct 结构的核心定义通常位于这个文件中。这是进程调度和管理的核心头文件,包含了进程管理的数据结构和函数原型。

  • kernel/fork.c:进程的创建和复制过程通常在这个文件中实现

  • kernel/exit.c:进程的退出和销毁过程也会涉及到 task_struct 结构,相关的处理通常在这个文件中。

说明task_struct

因为它是 Linux 内核的核心数据结构之一,各个子系统和模块都需要访问它

#include <linux/types.h>
#include <linux/sched.h>
#include <linux/mm_types.h>

struct task_struct {
    pid_t pid;                     // 进程标识符  用于在系统中唯一标识一个进程

    long state;                    // 进程状态  表示进程的当前状态,如运行、就绪、休眠等。具体的状态由宏定义表示,例如 TASK_RUNNING、TASK_INTERRUPTIBLE 等。

    int static_prio;               // 静态优先级   是在进程创建时分配

    int normal_prio;               // 动态优先级   在运行时根据进程的行为和响应时间进行调整

    struct thread_info *thread_info; // 线程信息   指向与进程相关的线程信息。线程信息包括了线程栈等信息。

    unsigned long flags;           // 进程状态标志,包含了与进程状态相关的各种标志位,例如是否被追踪、是否在内核态等。

    struct files_struct *files;    // 文件描述符表 指向进程的文件描述符表,用于管理打开的文件和文件操作

    struct mm_struct *mm;          // 地址空间 指向进程的地址空间描述符,用于管理进程的虚拟内存。

    struct task_struct *parent;    // 父进程 指向父进程的 task_struct 结构,用于表示进程的父子关系。

    struct list_head children;     // 子进程链表 用于维护进程的层次结构关系。

    struct list_head sibling;      // 兄弟进程链表 用于维护进程的层次结构关系。

    struct pt_regs *thread;        // 执行上下文  用于实现 wait 等系统调用。

    struct sched_entity se;        // 调度信息  包含了进程的调度信息,用于进程的调度。

    wait_queue_head_t wait_chldexit; // 子进程退出等待队列

    struct signal_struct *signal;  // 信号处理器包  含了与信号处理相关的信息,用于管理进程接收到的信号。

    struct user_struct *user;      // 用户信息  包含了与用户相关的信息,如资源限制等。

    char comm[TASK_COMM_LEN];      // 进程名字 进程的名字,通常是进程的可执行文件名。

    spinlock_t alloc_lock;         // 进程锁 用于同步操作,以确保多线程环境下的数据一致性。

};

其中pid_t pid、longstate、sturct mm_struct_mm、struct list_head children 和 struct list_head sibling、struct sched_entity、struct signal_struct signal、char comm[TASK_COMM_LEN]最重要

linux进程slab分配器&slub介绍(mm/slab.c)

  • SLAB 是一种用于高效内存分配和管理的内存分配算法和数据结构
  • 它的核心思想是将内存划分为多个大小相同或相近的块,然后按需分配和释放这些块,以减少内存分配和释放的开销。

最重要的算法和数据结构

  • 双向链表(Doubly Linked List): SLAB 内存分配器使用双向链表来管理空闲内存块。每个 Cache 中的 Slab 包含一个 Free List,这是一个双向链表,用于存储可用的内存块。这允许在内存分配和释放时高效地添加和删除内存块。

  • 哈希表(Hash Table): 为了快速查找适当的 Cache,SLAB 内存分配器使用哈希表。哈希表将不同大小的对象映射到相应的 Cache,以便快速定位。
    实现 O(1) 时间复杂度

  • Buddy 系统(Buddy Allocation 二叉树): SLAB 内存分配器通常依赖于物理内存管理机制,例如 Buddy 系统,来分配 Slab Pages。Buddy 系统使用二叉树来管理物理内存块,并能够高效地分配和释放连续的内存块。 该算法的核心思想是将可用内存块划分为大小相等的块

  • LRU 算法(Least Recently Used 最近最少使用): SLAB 内存分配器可能使用 LRU 算法来管理缓存中的 Slabs。LRU 算法用于确定哪个 Slab 应该被回收,以便在需要时重新使用。
    它的核心思想是保留最近被访问或使用过的数据,而淘汰最长时间未被使用的数据。

  • 分配和释放算法: SLAB 内存分配器使用自定义的分配和释放算法,以确保高效的内存分配和释放。这些算法通常包括从 Free List 中获取内存块、将内存块返回到 Free List、Slab 的回收和再分配等

  • 自旋锁(Spin Lock): 为了在多线程或多核环境中保护共享数据结构,SLAB 内存分配器可能使用自旋锁来实现同步。自旋锁允许线程在等待资源变得可用时自旋(忙等待),以避免进入睡眠状态。

  • 形成链表->变成二叉树->形成哈希:pid->task_struct

进程声明周期

  • 就绪状态(Ready):

进程处于就绪状态时,它已经准备好执行,但尚未分配到CPU时间片。多个就绪状态的进程等待操作系统的调度,以便在CPU上执行。

  • 运行状态(Running):

    进程进入

  • 睡眠状态(Sleeping):

    进程可能会由于等待某些事件(例如I/O操作完成、信号等)而进入睡

  • 停止状态(Stopped):

    进程可能会被显式地停止,通常由用户或其他进程发出停止信号(如

  • 僵死状态(Zombie):
    当一个进程已经终止但其父进程尚未调用wait()或waitpid()来收回其终止状态时,该进程会进入僵死状态。僵死进程不占用系统资源,但仍然保留在进程表中,以供父进程查询其退出状态。