### Eonix内核的任务管理

Eonix内核的任务管理系统通过线程、进程、进程组、会话和信号机制，构建了一个高效、灵活的多任务管理框架。以下是对其任务管理部分的全面介绍：

---

### 1. 核心设计概述
任务管理是内核调度和进程间通信的基础，其核心组件包括：
- 线程（`Thread`）： 是调度的基本单位。
- 进程（`Process`）： 是资源分配的基本单位，包含线程的集合。
- 进程组（`ProcessGroup`）： 组织和管理一组相关进程，通常用于信号广播。
- 会话（`Session`）： 包含多个进程组，为作业控制和终端管理提供支持。
- 信号机制： 负责进程间通信、异常处理和任务状态控制。
  
Eonix的任务管理通过统一的接口和模块化设计，支持复杂的任务调度、资源管理和进程间交互。

---

### 2. 线程管理

线程是Eonix内核调度的基本单位，由`Thread`结构体表示，每个线程包含独立的内核栈（`KernelStack`）、文件系统上下文和文件描述符表等资源。线程的状态管理（如`Running`、`ISleep`、`USleep`等）通过调度器接口完成，支持线程在用户态和内核态之间高效切换。线程中有进程的指针，这样可以做到多个线程共享一部分资源。通过`InterruptContext`和`TaskContext`实现上下文保存与切换，确保任务运行的连续性和调度的高效性。

每个线程由`Thread`结构体表示，包含：
- TID（`tid`）： 唯一标识。
- 线程状态（`state`）： 包括`Preparing`、`Running`、`Ready`、`Zombie`等多种状态，用于调度器管理。
- 内核栈（`kstack`）： 为每个线程分配一个独立的内核栈，用于上下文切换和异常处理。
- 文件系统上下文（`fs_context`）和文件描述符表（`files`）： 提供线程的独立资源管理。

#### 线程的生命周期

线程的创建方面，初始线程通过`Thread::new_for_init`创建。像在`clone`或`exec`等中复制的线程通过`Thread::new_cloned`创建，和父线程一起共享一个进程的资源。状态管理通过调度器（`Scheduler`）接口完成，支持从`Running`切换到`ISleep`或`USleep`等状态。 上下文切换使用`InterruptContext`和`TaskContext`保存线程的寄存器状态，支持线程在内核和用户态间切换。

---

### 3. 进程管理

进程是资源分配的基本单位，由`Process`结构体表示。每个进程具有唯一的PID，并包含线程集合、父子关系、内存管理信息（`MMList`）以及所属的进程组和会话。进程的创建通过克隆现有进程实现，支持资源继承，同时初始进程（如`init`进程或闲置进程）通过专门方法生成。进程支持多线程资源共享，通过`WaitList`等待子进程状态变化（如退出或收到信号），并结合信号机制实现进程间通信和同步操作。进程的多线程安全性由锁机制保障。

#### 进程的基本结构
每个进程由Process结构体表示，包含：

- PID（`pid`）： 每个进程都有一个唯一的标识符，用于区分其他进程。
- 线程集合（`threads`）： 用`BTreeMap`储存一个包含属于该进程的所有线程的列表。
- 子进程（`children`）： 使用`BTreeMap`存储该进程的所有子进程，支持高效查找和管理。
- 内存管理信息（`mm_list`）： 使用`MMList`表示进程的内存映射，管理用户态和内核态的地址空间。
- 父进程（`parent`）： 引用父进程，支持动态更新和安全访问。
- 所属进程组（`pgroup`）： 每个进程都属于一个进程组，用于协作管理和信号广播。
- 会话（`session`）： 进程所属的会话，支持终端绑定和作业控制。

#### 进程的生命周期

进程创建通过`Process::new_cloned`方法实现新进程的创建，子进程会继承父进程的大部分资源（如内存和会话）。对于系统初始化的特殊进程（如init进程），通过`Process::new_for_init`创建。

进程终止时，调用`ProcessList::do_kill_process`方法将进程标记为`Zombie`状态，并清理相关资源，包括用户态内存、子进程管理以及从进程组和会话中移除。如果终止的是会话领导进程，关联的控制终端也会被释放。同时，其没有退出的所有子进程都会被收容到init进程中，确保每个进程都可以正常地被回收。

#### 进程管理与多线程支持

`ProcessList`是管理所有进程的核心结构，通过全局实例`GLOBAL_PROC_LIST`维护。`ProcessList`使用`BTreeMap`存储所有的进程、线程、进程组和会话，并通过`RwSemaphore`实现线程安全访问。关键操作包括：
1. 添加与移除： 通过`add_process`和`remove_process`方法实现进程的动态添加与移除。在移除进程时，还会同步清理其线程、会话和进程组信息。
2. 查询： 提供`try_find_process`、`try_find_pgroup`等方法，支持高效查找进程和相关结构。
调度整合： 结合调度器`Scheduler`，支持线程和进程的动态调度，确保高效的多任务管理。

---

### 4. 信号管理

信号是Eonix内核进程间通信和异常处理的核心机制，用于实现进程控制、任务协作和异常处理。信号通过`Signal`结构体表示，并结合`SignalList`和`SignalAction`实现挂起、屏蔽和处理机制。信号的设计符合POSIX标准，支持多种信号类型（如`SIGKILL`、`SIGSTOP`等），并通过进程组和会话实现信号广播和作业控制。

#### 信号的基本结构

信号由`Signal`结构体表示，包含信号类型，即每个信号通过唯一的数字标识，支持常见的POSIX信号类型，包括：
- 终止信号： 如`SIGTERM`、`SIGKILL`，用于立即终止进程。
- 停止信号： 如`SIGSTOP`、`SIGTSTP`，用于暂停进程。
- 用户自定义信号： 如`SIGUSR1`、`SIGUSR2`，用于用户进程间通信。
- 忽略信号： 如`SIGCHLD`，默认行为是被内核忽略。
- 核心转储信号： 如`SIGSEGV`、`SIGFPE`，会生成核心转储文件并终止进程。

#### 信号管理与挂起

信号管理由`SignalList`完成，负责信号的挂起、屏蔽和优先级管理。挂起信号存储在`BinaryHeap`中，按照优先级排序，以确保高优先级信号优先处理。通过`SignalList::mask`和`SignalList::unmask`动态管理信号掩码，决定哪些信号会被屏蔽。对于线程或进程接收到的信号，会首先判断其屏蔽状态，屏蔽信号会进入挂起队列，等待后续处理。

#### 信号处理与分发

信号的处理方式由`SignalAction`定义，支持以下三种模式：
- 默认行为： 系统为每种信号定义的默认处理方式，如终止进程、暂停进程或忽略信号。
- 自定义Handler： 用户可以为特定信号设置自定义处理器，在用户态执行信号处理逻辑。
- 忽略信号： 指定某些信号被忽略，如`SIGCHLD`默认不会影响进程运行。

信号分发可以针对单个线程、进程组或会话的前台进程组进行广播。通过`SignalList::raise`方法，信号被加入目标对象的挂起信号队列，并根据优先级等待处理。

#### 信号与上下文切换

信号处理与上下文切换紧密结合。当线程被调度器切换到运行状态时，内核会检测其挂起信号列表，并调用相应的信号处理器。信号处理器通过`SignalAction::handle`加载到用户堆栈中，修改`InterruptContext`返回地址，将执行跳转到用户态的信号处理函数中。

对于不可屏蔽信号（如`SIGKILL`、`SIGSTOP`），内核会立即终止或暂停目标进程，不会经过挂起队列或信号处理器。

#### 信号的进程间通信

信号是进程间通信的重要机制，支持通过进程组和会话范围实现信号广播。例如：
- 通过`ProcessGroup::raise`将信号发送给整个进程组。
- 使用`Session::raise_foreground`向会话的前台进程组发送信号，实现作业控制。

信号分发结合进程组和会话机制，为任务管理中的状态同步和协作提供了基础设施。

Eonix内核的信号管理模块通过模块化设计和POSIX标准兼容，实现了灵活的进程间通信和异常处理。挂起信号、屏蔽机制和优先级队列确保信号的高效处理，自定义处理器扩展了信号的适用场景。信号与上下文切换和调度器紧密结合，为系统的异常响应和作业控制提供了强大支持，同时通过进程组和会话的广播功能增强了信号的可扩展性。

---

### 5. 会话、进程组与作业控制

Eonix内核中的会话和进程组机制是任务管理系统的重要组成部分，为进程的组织管理、信号广播和作业控制提供了基础。会话通过将多个进程组归为一个集合，结合控制终端（`Terminal`）实现用户与作业的交互。作业控制支持前台/后台作业切换、暂停与恢复等操作，使内核能够高效管理任务协作。

#### 会话的基本结构

会话由`Session`结构体表示，通过`SessionJobControl`管理前台进程组和控制终端，使用读写信号量（`RwSemaphore`）确保多线程安全访问。包含以下核心组件：

- SID（`sid`）： 由会话领导进程的PID决定，是会话的唯一标识。
- 领导进程（`leader`）： 会话的创建者，负责初始化会话并管理其生命周期。
- 前台进程组（`foreground`）： 当前与用户交互的进程组，用户输入和信号优先发送到此进程组。
- 控制终端（`control_terminal`）： 绑定到会话的终端设备，用于用户与前台进程组的交互。
- 进程组集合（`groups`）： 存储会话中的所有进程组，支持动态添加和移除。

#### 进程组的基本结构

进程组由`ProcessGroup`结构体表示，是进程的集合，主要用于信号广播和作业管理。支持成员的动态添加和移除，并通过所属会话与控制终端关联。。每个进程组包含以下部分：

- 进程组ID（`pgid`）： 用于唯一标识进程组，通常等于其领导进程的PID。
- 领导进程（`leader`）： 进程组的创建者，负责管理该组的资源和信号。
- 所属会话（`session`）： 进程组所属的会话，确保跨进程组的协作一致性。
- 成员进程（`processes`）： 使用`BTreeMap`存储进程组内的所有进程，支持快速查找和广播信号。

#### 作业控制

作业控制是通过会话的前台进程组和控制终端实现的，支持以下功能：

1. 前后台作业切换： 通过`Session::set_foreground_pgid`设置前台进程组，使用户输入和信号优先发送到新的作业。
2. 信号广播： 通过`Session::raise_foreground`将信号发送给前台进程组，常用于暂停（`SIGSTOP`）或恢复（`SIGCONT`）前台作业。
3. 终端绑定： 使用`Session::set_control_terminal`将终端绑定到会话，确保用户输入输出只影响绑定的会话。会话领导进程拥有设置和释放控制终端的权限。

会话与进程组的管理由`ProcessList`模块实现，新会话通过`Session::new`创建，进程组通过`Session::new_group`添加到会话。移除进程组时，如果会话中无其他进程组，会自动解除与控制终端的绑定。`ProcessList`提供方法查找会话和进程组，如`try_find_session`和`try_find_pgroup`。信号通过`ProcessGroup::raise`广播到整个进程组，或通过`Session::raise_foreground`广播到前台进程组。

会话和进程组与信号机制紧密协作，提供多层次的任务控制能力。会话通过前台进程组分发信号，实现广播，可以控制用户当前交互作业。用户可以通过绑定的终端发送信号（如`SIGSTOP`或`SIGCONT`）控制作业状态切换。当会话领导进程终止时，内核会自动释放绑定的控制终端，并将会话中所有进程移至后台。

## 实现中遇到的问题

当前实现中，进程中对于其子线程、子进程，以及会话和进程组中对于所有成员的引用的设计可能会造成不一致。比如我们在移除一个进程的时候，需要将这个进程从其父进程的子进程表中删除，还需要将其从进程组和会话的成员列表中删除。同时在添加进程的时候要注意把这个进程加到这些表里。我们在这些过程当中有多个位置都要注意，一不小心就会导致内核崩溃。想到的一个解决办法就是用侵入式链表改良，将这几个过程合起来，降低复杂性。

还需要实现`clone`系统调用，用于实现 *POSIX* 标准中的线程。
