Eonix内核的任务管理系统通过线程、进程、进程组、会话和信号机制,构建了一个高效、灵活的多任务管理框架。以下是对其任务管理部分的全面介绍:
任务管理是内核调度和进程间通信的基础,其核心组件包括:
Thread
): 是调度的基本单位。Process
): 是资源分配的基本单位,包含线程的集合。ProcessGroup
): 组织和管理一组相关进程,通常用于信号广播。Session
): 包含多个进程组,为作业控制和终端管理提供支持。Eonix的任务管理通过统一的接口和模块化设计,支持复杂的任务调度、资源管理和进程间交互。
线程是Eonix内核调度的基本单位,由Thread
结构体表示,每个线程包含独立的内核栈(KernelStack
)、文件系统上下文和文件描述符表等资源。线程的状态管理(如Running
、ISleep
、USleep
等)通过调度器接口完成,支持线程在用户态和内核态之间高效切换。线程中有进程的指针,这样可以做到多个线程共享一部分资源。通过InterruptContext
和TaskContext
实现上下文保存与切换,确保任务运行的连续性和调度的高效性。
每个线程由Thread
结构体表示,包含:
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
保存线程的寄存器状态,支持线程在内核和用户态间切换。
进程是资源分配的基本单位,由Process
结构体表示。每个进程具有唯一的PID,并包含线程集合、父子关系、内存管理信息(MMList
)以及所属的进程组和会话。进程的创建通过克隆现有进程实现,支持资源继承,同时初始进程(如init
进程或闲置进程)通过专门方法生成。进程支持多线程资源共享,通过WaitList
等待子进程状态变化(如退出或收到信号),并结合信号机制实现进程间通信和同步操作。进程的多线程安全性由锁机制保障。
每个进程由Process结构体表示,包含:
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
实现线程安全访问。关键操作包括:
add_process
和remove_process
方法实现进程的动态添加与移除。在移除进程时,还会同步清理其线程、会话和进程组信息。try_find_process
、try_find_pgroup
等方法,支持高效查找进程和相关结构。
调度整合: 结合调度器Scheduler
,支持线程和进程的动态调度,确保高效的多任务管理。信号是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
定义,支持以下三种模式:
SIGCHLD
默认不会影响进程运行。信号分发可以针对单个线程、进程组或会话的前台进程组进行广播。通过SignalList::raise
方法,信号被加入目标对象的挂起信号队列,并根据优先级等待处理。
信号处理与上下文切换紧密结合。当线程被调度器切换到运行状态时,内核会检测其挂起信号列表,并调用相应的信号处理器。信号处理器通过SignalAction::handle
加载到用户堆栈中,修改InterruptContext
返回地址,将执行跳转到用户态的信号处理函数中。
对于不可屏蔽信号(如SIGKILL
、SIGSTOP
),内核会立即终止或暂停目标进程,不会经过挂起队列或信号处理器。
信号是进程间通信的重要机制,支持通过进程组和会话范围实现信号广播。例如:
ProcessGroup::raise
将信号发送给整个进程组。Session::raise_foreground
向会话的前台进程组发送信号,实现作业控制。信号分发结合进程组和会话机制,为任务管理中的状态同步和协作提供了基础设施。
Eonix内核的信号管理模块通过模块化设计和POSIX标准兼容,实现了灵活的进程间通信和异常处理。挂起信号、屏蔽机制和优先级队列确保信号的高效处理,自定义处理器扩展了信号的适用场景。信号与上下文切换和调度器紧密结合,为系统的异常响应和作业控制提供了强大支持,同时通过进程组和会话的广播功能增强了信号的可扩展性。
Eonix内核中的会话和进程组机制是任务管理系统的重要组成部分,为进程的组织管理、信号广播和作业控制提供了基础。会话通过将多个进程组归为一个集合,结合控制终端(Terminal
)实现用户与作业的交互。作业控制支持前台/后台作业切换、暂停与恢复等操作,使内核能够高效管理任务协作。
会话由Session
结构体表示,通过SessionJobControl
管理前台进程组和控制终端,使用读写信号量(RwSemaphore
)确保多线程安全访问。包含以下核心组件:
sid
): 由会话领导进程的PID决定,是会话的唯一标识。leader
): 会话的创建者,负责初始化会话并管理其生命周期。foreground
): 当前与用户交互的进程组,用户输入和信号优先发送到此进程组。control_terminal
): 绑定到会话的终端设备,用于用户与前台进程组的交互。groups
): 存储会话中的所有进程组,支持动态添加和移除。进程组由ProcessGroup
结构体表示,是进程的集合,主要用于信号广播和作业管理。支持成员的动态添加和移除,并通过所属会话与控制终端关联。。每个进程组包含以下部分:
pgid
): 用于唯一标识进程组,通常等于其领导进程的PID。leader
): 进程组的创建者,负责管理该组的资源和信号。session
): 进程组所属的会话,确保跨进程组的协作一致性。processes
): 使用BTreeMap
存储进程组内的所有进程,支持快速查找和广播信号。作业控制是通过会话的前台进程组和控制终端实现的,支持以下功能:
Session::set_foreground_pgid
设置前台进程组,使用户输入和信号优先发送到新的作业。Session::raise_foreground
将信号发送给前台进程组,常用于暂停(SIGSTOP
)或恢复(SIGCONT
)前台作业。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 标准中的线程。