|
661 | 661 |
|
662 | 662 | 于是,我们就将通用资源分配器和三种软硬件资源的分配和回收机制介绍完了,这也是线程机制中最关键的一个部分。 |
663 | 663 |
|
664 | | -线程控制块 |
| 664 | +进程和线程控制块 |
665 | 665 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
666 | 666 |
|
667 | | -在内核中,每个线程的执行状态和线程上下文等均保存在一个被称为任务控制块 (TCB, Task Control Block) 的结构中,它是内核对线程进行管理的核心数据结构。在内核看来,它就等价于一个线程。 |
668 | | - |
| 667 | +在引入线程机制之后,线程就代替进程成为了 CPU 资源的调度单位——任务。因此,代码执行有关的一些内容被分离到任务(线程)控制块中,其中包括线程状态、各类上下文和线程独占的一些资源等。线程控制块 ``TaskControlBlock`` 是内核对线程进行管理的核心数据结构。在内核看来,它就等价于一个线程。 |
669 | 668 |
|
670 | 669 | .. code-block:: rust |
671 | | - :linenos: |
| 670 | + :linenos: |
| 671 | +
|
| 672 | + // os/src/task/task.rs |
672 | 673 |
|
673 | 674 | pub struct TaskControlBlock { |
674 | 675 | // immutable |
|
686 | 687 | pub exit_code: Option<i32>, |
687 | 688 | } |
688 | 689 |
|
| 690 | +线程控制块中的不变量有所属进程的弱引用和自身的内核栈。在可变的 inner 里面则保存了线程资源集合 ``TaskUserRes`` 和 Trap 上下文。任务上下文 ``TaskContext`` 仍然保留在线程控制块中,这样才能正常进行线程切换。此外,还有线程状态 ``TaskStatus`` 和线程退出码 ``exit_code`` 。 |
689 | 691 |
|
690 | | -线程控制块就是任务控制块(TaskControlBlock),主要包括在线程初始化之后就不再变化的元数据:线程所属的进程和线程的内核栈,以及在运行过程中可能发生变化的元数据: ``UPSafeCell<TaskControlBlockInner>`` 。大部分的细节放在 ``TaskControlBlockInner`` 中: |
691 | | - |
692 | | -之前进程中的定义不存在的: |
693 | | - |
694 | | -- ``res : TaskUserRes`` 指出了用户态的线程代码执行需要的信息,这些在线程初始化之后就不再变化: |
| 692 | +进程控制块中则保留进程内所有线程共享的资源: |
695 | 693 |
|
696 | 694 | .. code-block:: rust |
697 | | - :linenos: |
698 | | -
|
699 | | - pub struct TaskUserRes { |
700 | | - pub tid: usize, |
701 | | - pub ustack_base: usize, |
702 | | - pub process: Weak<ProcessControlBlock>, |
703 | | - } |
704 | | -
|
705 | | -
|
706 | | -- tid:线程标识符 |
707 | | -- ustack_base:线程的栈顶地址 |
708 | | -- process:线程所属的进程 |
709 | | - |
710 | | -与之前进程中的定义相同/类似的部分: |
711 | | - |
712 | | -- ``trap_cx_ppn`` 指出了应用地址空间中线程的 Trap 上下文被放在的物理页帧的物理页号。 |
713 | | -- ``task_cx`` 保存暂停线程的线程上下文,用于线程切换。 |
714 | | -- ``task_status`` 维护当前线程的执行状态。 |
715 | | -- ``exit_code`` 线程退出码。 |
716 | | - |
717 | | - |
718 | | -包含线程的进程控制块 |
719 | | -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
720 | | - |
721 | | -把线程相关数据单独组织成数据结构后,进程的结构也需要进行一定的调整: |
| 695 | + :linenos: |
| 696 | + :emphasize-lines: 18, 19 |
722 | 697 |
|
723 | | -.. code-block:: rust |
724 | | - :linenos: |
| 698 | + // os/src/task/process.rs |
725 | 699 |
|
726 | 700 | pub struct ProcessControlBlock { |
727 | 701 | // immutable |
|
731 | 705 | } |
732 | 706 |
|
733 | 707 | pub struct ProcessControlBlockInner { |
734 | | - ... |
| 708 | + pub is_zombie: bool, |
| 709 | + pub memory_set: MemorySet, |
| 710 | + pub parent: Option<Weak<ProcessControlBlock>>, |
| 711 | + pub children: Vec<Arc<ProcessControlBlock>>, |
| 712 | + pub exit_code: i32, |
| 713 | + pub fd_table: Vec<Option<Arc<dyn File + Send + Sync>>>, |
| 714 | + pub signals: SignalFlags, |
735 | 715 | pub tasks: Vec<Option<Arc<TaskControlBlock>>>, |
736 | 716 | pub task_res_allocator: RecycleAllocator, |
| 717 | + ... |
737 | 718 | } |
738 | 719 |
|
739 | | -从中可以看出,进程把与处理器执行相关的部分都移到了 ``TaskControlBlock`` 中,并组织为一个线程控制块向量中,这就自然对应到多个线程的管理上了。而 ``RecycleAllocator`` 是对之前的 ``PidAllocator`` 的一个升级版,即一个相对通用的资源分配器,可用于分配进程标识符(PID)和线程的内核栈(KernelStack)。 |
| 720 | +其中 ``pid`` 为进程标识符,它在进程创建后的整个生命周期中不再变化。可变的 inner 中的变化如下: |
740 | 721 |
|
741 | | -.. chyyuu 加一个PidAllocator的链接??? |
| 722 | +- 第 18 行在进程控制块里面设置一个向量保存进程下所有线程的任务控制块; |
| 723 | +- 第 19 行是进程为进程内的线程分配资源的通用资源分配器 ``RecycleAllocator`` 。 |
742 | 724 |
|
743 | | -线程与处理器管理结构 |
| 725 | +任务管理器与处理器管理结构 |
744 | 726 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
745 | 727 |
|
746 | | -线程管理的结构是线程管理器,即任务管理器,位于 ``os/src/task/manager.rs`` 中,其数据结构和方法与之前章节中进程的任务管理器完全一样,只不过管理单位从之前的任务(进程)换成了线程。而处理器管理结构 ``Processor`` 负责维护 CPU 状态、调度和特权级切换等事务。其数据结构与之前章节中进程的处理器管理结构完全一样。但在相关方法上面,由于多个线程有各自的用户栈和跳板页,所以有些不同,下面会进一步分析。 |
| 728 | +任务管理器 ``TaskManager`` 和处理器管理结构 ``Processor`` 分别在 ``task/manager.rs`` 和 ``task/processor.rs`` 中。它们的接口和功能和之前基本上一致,但是由于任务控制块 ``TaskControlBlock`` 和进程控制块 ``ProcessControlBlock`` 和之前章节的语义不同,部分接口略有改动。 |
| 729 | + |
| 730 | + |
| 731 | +.. 线程管理的结构是线程管理器,即任务管理器,位于 ``os/src/task/manager.rs`` 中,其数据结构和方法与之前章节中进程的任务管理器完全一样,只不过管理单位从之前的任务(进程)换成了线程。而处理器管理结构 ``Processor`` 负责维护 CPU 状态、调度和特权级切换等事务。其数据结构与之前章节中进程的处理器管理结构完全一样。但在相关方法上面,由于多个线程有各自的用户栈和跳板页,所以有些不同,下面会进一步分析。 |
747 | 732 |
|
748 | 733 | .. chyyuu 加一个taskmanager,processor的链接??? |
749 | 734 |
|
|
0 commit comments