ChCore—实验 3:进程与线程、异常处理 部分记录
思考题 1: 内核从完成必要的初始化到用户态程序的过程是怎么样的?尝试描述一下调用关系。
内核启动到用户程序启动的流程:
main
├── uart_init
├── mm_init
├── arch_interrupt_init
├── create_root_thread
│ ├── create_root_cap_group
│ ├── __create_root_thread
│ └── switch_to_thread
└── eret_to_thread
└── switch_context
- Chcore 启动后会依次初始化
uart模块、内存管理模块、中断模块 - 然后调用
create_root_thread创建一个根进程,创建进程的cap_group结构体并初始化,包括分配一块虚拟地址空间vmspace和slot_table的初始化,__create_root_thread会先从磁盘中载入 ELF 文件,为进程创建一个主线程,最后将线程root_thread放入根进程中切换到线程执行; eret_to_thread则switch_context完成从内核模式到用户模式的切换,并在用户模式下开始运行用户代码。
sys_create_cap_group()
这段代码是一个系统调用函数,名为 sys_create_cap_group,用于创建一个新的能力组(cap_group),并将其分配给指定的进程(pid)。
在这个函数中,首先检查当前的能力组是否为 ROOT_PID(即根能力组),如果不是,则返回错误码 -EPERM。
接下来,通过 obj_alloc 函数分配一个新的能力组结构体(new_cap_group),如果分配失败,则返回错误码 -ENOMEM。
然后,通过 cap_group_init 函数初始化新的能力组,将其与基本对象编号(BASE_OBJECT_NUM)和指定的进程 ID(pid)关联起来。
接着,通过 cap_alloc 函数在当前的能力组中分配一个新的能力(cap),并将其与新的能力组(new_cap_group)关联起来。如果分配失败,则返回错误码 -1。
然后,通过 cap_copy 函数将新的能力组(new_cap_group)复制到当前线程的能力组中,并将新的能力组设为第一个能力(cap[0])。
接下来,通过 obj_alloc 函数分配一个新的虚拟内存空间结构体(vmspace),如果分配失败,则返回错误码 -ENOMEM。
然后,通过 vmspace_init 函数初始化新的虚拟内存空间,并将其与指定的进程 ID(pid)关联起来。
接着,通过 cap_alloc 函数在新的能力组中分配一个新的能力(cap),并将其与新的虚拟内存空间(vmspace)关联起来。如果分配失败,则返回错误码 -1。
最后,通过 copy_from_user 函数将指定的能力组名称(cap_group_name)复制到新的能力组结构体(new_cap_group)中,并返回新的能力(cap)。
如果出现任何错误,则会释放之前分配的对象并返回相应的错误码。
Capability
Capability 可以理解为 Linux 下的文件描述符。它把一个资源对象和访问权限封装到了一起,并对外提供一个整形 cap 做访问的句柄(句柄就是对资源对象的指针或者引用的一种抽象)
ChCore 中每个 capability 都属于一个进程。cap 的值实际上就是对象在所属的 process 的 slot_table 中的下标。
示例代码:
// 仅为演示,删掉了部分异常处理的代码
// 分配 cap
int sys_create_pmo(u64 size, u64 type)
{
int cap;
struct pmobject *pmo;
pmo = obj_alloc(TYPE_PMO, sizeof(*pmo)); // 分配对象
pmo_init(pmo, type, size, 0); // 初始化
cap = cap_alloc(current_process, pmo, 0); // 挂载到进程上,分配 cap 编号
return cap;
}
// 使用 cap
int sys_map_pmo(u64 target_process_cap, u64 pmo_cap, u64 addr, u64 perm)
{
struct pmobject *pmo;
// 根据 cap 获取对象的指针
pmo = obj_get(current_process, pmo_cap, TYPE_PMO);
// 操作对象,省略之
// ......
// 声明自己操作结束,为了并发安全准备的。
obj_put(pmo);
}
下面可以看几个例子感受一下cap是如何被使用的。
创建object并分配cap
在thread.h的 create_thread函数中,我们需要创建线程,然后把线程加入到进程的slot_table中管理起来,同时要返回cap作为索引。其中核心的一句如下:
thread = obj_alloc(TYPE_THREAD, sizeof(*thread));
任何需要通过cap来管理的资源都是通过object来抽象的,所以需要先创建一个object对象。该函数的第二个参数是线程的大小,这是因为我们需要用这个大小来初始化object,使其能容纳我们需要的资源。
仔细看一下这个函数的定义:
void *obj_alloc(u64 type, u64 size)
{
u64 total_size;
struct object *object;
total_size = sizeof(*object) + size;
object = kmalloc(total_size);
if (!object)
return NULL;
object->type = type;
object->size = size;
object->refcount = 0;
/*
* If the cap of the object is copied, then the copied cap (slot) is
* stored in such a list.
*/
init_list_head(&object->copies_head);
return object->opaque;
}
在分配object的时候,kmalloc的大小为sizeof(*object)+size,最后返回的是opaque字段。此时object的内存布局如下:

object
语句:thread = obj_alloc(TYPE_THREAD, sizeof(*thread));,相当于是thread = malloc(sizeof(struct thread)),并且额外地,在头部加上了点别的信息,这样组成了一个object。这样一个函数调用完成了thread空间的分配和object的初始化。
这里最精妙的地方就是这个opaque的类型,是个数组,因此最后返回这个数组名的时候,实际上返回的是指向第一个元素的指针,即第二个参数size分配的额外的内存空间的起始地址。如果换成u64*指针类型的话就没有这种效果了。
然后我们通过cap = cap_alloc(process, thread, 0);,将线程加入到进程的slot_table中,并且返回cap,最终返回给用户,
根据cap获取对应的被管理的对象(线程,pmo等)
如何根据线程的cap来获取线程本身?在cap_group函数中有如下语句:root_thread = obj_get(root_process, thread_cap, TYPE_THREAD);
PMO(Physical Memory Object)
物理内存对象
memory.c中包含对PMO操作的各种方法
ELF
https://paper.seebug.org/papers/Archive/refs/elf/Understanding_ELF.pdf
解析 ELF 文件,并将其内容加载到新线程的用户内存空间中。
struct elf_file {
struct elf_header header;
struct elf_program_header *p_headers;
struct elf_section_header *s_headers;
};

| Field | Purpose |
|---|---|
| p_type | 此数据成员说明了本程序头所描述的段的类型,或者如何解析本程序头的信 息。 |
| p_flags | Segment-dependent flags (position for 64-bit structure). |
| p_offset | Offset of the segment in the file image.文件镜像中该段的偏移量。 |
| p_vaddr | Virtual address of the segment in memory.该段在内存中的虚拟地址。 |
| p_paddr | On systems where physical address is relevant, reserved for segment's physical address.在物理地址相关的系统中,为段的物理地址保留。 |
| p_filesz | Size in bytes of the segment in the file image. May be 0.文件镜像中段的大小,以字节为单位。可以是0。 |
| p_memsz | Size in bytes of the segment in memory. May be 0.内存中段的大小,以字节为单位。可以是0。 |
| p_flags | Segment-dependent flags (position for 32-bit structure). |
| p_align | 0 and 1 specify no alignment. Otherwise should be a positive, integral power of 2, with p_vaddr equating p_offset modulus p_align.对于可装载的段来说,其 p_vaddr 和 p_offset 的值至少要向内存页面大小对 齐。此数据成员指明本段内容如何在内存和文件中对齐。如果该值为 0 或 1,表明 没有对齐要求;否则,p_align 应该是一个正整数,并且是 2 的幂次数。p_vaddr 和 p_offset 在对 p_align 取模后应该相等。 |
PT_LOAD 1
此类型表明本程序头指向一个可装载的段。段的内容会被从文件中拷贝到 内存中。如前所述,段在文件中的大小是 p_filesz,在内存中的大小是 p_memsz。如果 p_memsz 大于 p_filesz,在内存中多出的存储空间应填 0 补 充,也就是说,段在内存中可以比在文件中占用空间更大;而相反,p_filesz 永远不应该比 p_memsz 大,因为这样的话,内存中就将无法完整地映射段的 内容。在程序头表中,所有 PT_LOAD 类型的程序头按照 p_vaddr 的值做升序排列。
┌──────┬──────┬──────┬──────┬──────┬──────┐
│ PAGE │ PAGE │ PAGE │ PAGE │ PAGE │ PAGE │
└──────┴──────┴──────┴──────┴──────┴──────┘
| |-------p_memsz------| |
| p_vddr |
|----------seg_map_sz-------|
异常向量表
- mrs用于将程序状态寄存器的内容传送到通用寄存器中
主要关注同步异常处理:

异常症状寄存器(Exception Syndrome Register)ESR_ELx:存储有 关异常原因的信息。有关此寄存器中各个位的含义,请参阅《ARMv8 程 序员指南》
错误地址寄存器(Fault Address Register)FAR_ELx:存储所有同步 指令中止、同步数据中止和对齐异常所对应的虚拟地址。如发生缺页异 常时,触发该异常的页的虚拟地址即存储在该寄存器中。
异常链接寄存器(Exception Link Register)ELR_ELx:该异常的首选 返回地址。对于某些同步异常(例如 SVC),它指向异常生成指令的下一 条指令的地址。对于其他的同步异常,它指向发生异常的指令,以便于 重新执行。对于由中断等导致的异步异常,ELR_ELx 指向尚未执行或未 完全执行的第一条指令的地址。
处理过程示例:
假设 AArch64 处理器正在用户线程中执行代码,并且遇到了一条指令集中未定义的指令。此 时,处理器会发生未定义指令异常,并执行如下主要操作:
- 处理器将异常原因放入 ESR_EL1 中,并将返回地址(即未定义指令的 地址)放入 ELR_EL1 中。
- 处理器检查 VBAR_EL1 以获得 EL1 中使用的异常向量表的地址。由于 当前异常是来自 AArch64 模式中 EL0 特权级的同步异常,因此处理器将 选择条目 VBAR_EL1+0x400。
- 处理器将特权级切换到 EL1。这一过程中,包含了如保存 PSTATE、使 用 SP_EL1 作为栈指针等内容,从而完成了从用户栈到内核栈的切换。
- 处理器执行 VBAR_EL1+0x400 处的代码,在 ChCore 中,这是一条跳转 到异常处理程序的b指令。
ChCore—实验 3:进程与线程、异常处理 部分记录的更多相关文章
- ChCore Lab3 用户进程和异常处理 实验笔记
本文为上海交大 ipads 研究所陈海波老师等人所著的<现代操作系统:原理与实现>的课程实验(LAB)的学习笔记的第三篇:用户进程与异常处理.所有章节的笔记可在此处查看:chcore | ...
- Python 第八篇:异常处理、Socket语法、SocketServer实现多并发、进程和线程、线程锁、GIL、Event、信号量、进程间通讯
本节内容: 异常处理.Socket语法.SocketServer实现多并发.进程和线程.线程锁.GIL.Event.信号量.进程间通讯.生产者消费者模型.队列Queue.multiprocess实例 ...
- Windbg 进程与线程 《第三篇》
Windbg既可以显示进程和线程列表,又可以显示指定进程或线程的详细信息.调试命令可以提供比taskmgr更详尽的进程资料,在调试过程中不可或缺. 一.进程命令 进程命令包括这些内容:显示进程列表.进 ...
- Python学习笔记——进阶篇【第八周】———进程、线程、协程篇(Socket编程进阶&多线程、多进程)
本节内容: 异常处理 Socket语法及相关 SocketServer实现多并发 进程.线程介绍 threading实例 线程锁.GIL.Event.信号量 生产者消费者模型 红绿灯.吃包子实例 mu ...
- 进程,线程,GIL,Python多线程,生产者消费者模型都是什么鬼
1. 操作系统基本知识,进程,线程 CPU是计算机的核心,承担了所有的计算任务: 操作系统是计算机的管理者,它负责任务的调度.资源的分配和管理,统领整个计算机硬件:那么操作系统是如何进行任务调度的呢? ...
- Python进程与线程
进程与线程:*进程: 进程是系统中程序执行和资源分配的基本单元, 每个进程都有自己的数据段(存储数据).代码段(存储代码).堆栈段(对象和变量). # 全局变量等资源在多个进程中不能 ...
- Day035--Python--管道, Manager, 进程池, 线程切换
管道 #创建管道的类: Pipe([duplex]):在进程之间创建一条管道,并返回元组(conn1,conn2),其中conn1,conn2表示管道两端的连接对象,强调一点:必须在产生Process ...
- python开发学习-day08(socket高级、socketserver、进程、线程)
s12-20160305-day08 *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: ...
- Unix进程和线程管理及其异同
Unix进程和线程管理及其异同 一,进程 1,什么是进程 在最初的单处理器系统中,系统中的多道程序按照一定规则切换而实现多任务处理,后来发现多个程序并发导致系统资源被共享,为了描述和管理程序对共享资源 ...
- c#Winform程序调用app.config文件配置数据库连接字符串 SQL Server文章目录 浅谈SQL Server中统计对于查询的影响 有关索引的DMV SQL Server中的执行引擎入门 【译】表变量和临时表的比较 对于表列数据类型选择的一点思考 SQL Server复制入门(一)----复制简介 操作系统中的进程与线程
c#Winform程序调用app.config文件配置数据库连接字符串 你新建winform项目的时候,会有一个app.config的配置文件,写在里面的<connectionStrings n ...
随机推荐
- 小白学k8s(11)-k8s中Secret理解
理解Secret 什么是Secret Secret的类型 Opaque Secret Opaque Secret的使用 将Secret挂载到Volume中 挂载的Secret会被自动更新 将Secre ...
- EXPLAIN分析pgsql的性能
EXPLAIN分析pgsql的性能 前言 EXPLAIN命令 EXPLAIN -- 显示一个语句的执行计划 命令详解 EXPLAIN输出结果展示 analyze buffers 全表扫描 索引扫描 位 ...
- SqlSugar分页查询
同步分页 int pagenumber= 1; // pagenumber是从1开始的不是从零开始的 int pageSize = 20; int totalCount=0; //单表分页 ...
- LeetCode刷题日记 2020/8/23
题目描述 给定范围 [m, n],其中 0 <= m <= n <= 2147483647,返回此范围内所有数字的按位与(包含 m, n 两端点). 示例 1: 输入: [5,7] ...
- SpringBoot自动化配置
SpringBoot自动化配置 一.SpringBoot自动化配置原理 1.SpringBoot引导类注解介绍 在SpringBoot的启动引导类上有一个@SpringBootApplication注 ...
- 【可观测性系列】 Opentelemetry 介绍
作者简介:大家好,我是蓝胖子 ️博客首页:博客园主页蓝胖子的编程梦 每日一句:人生的烦恼,多在于明白的太多,而做的太少 大家好,我是蓝胖子,随着微服务的流行,服务的可观测性概念被越来越多人提及到,究竟 ...
- 小知识:如何修改TFA下的OSW数据保留时间
在Oracle社区可以搜索到这样的问题: How to change oswatcher retention when running under TFA 但很遗憾该问题目前也没有给出确切答复. 其实 ...
- Spring Boot整合Postgres实现轻量级全文搜索
有这样一个带有搜索功能的用户界面需求: 搜索流程如下所示: 这个需求涉及两个实体: "评分(Rating).用户名(Username)"数据与User实体相关 "创建日期 ...
- 计算机算法设计与分析(第5版)PDF
<计算机算法设计与分析(第5版)>是2018年电子工业出版社出版的图书,作者是王晓东. 整本书的结构是:先介绍算法设计策略思想,然后从解决经典算法问题来学习,通过实践的方式去学习算法. 网 ...
- Activiti7+SpringBoot
1. 版本问题 1.1. Activiti版本 7.1.0-M6是最后一个支持JDK1.8的版本,此后的版本都要求JDK11以上 目前,Activiti最新版本是7.6.0,它是用JDK11编译的,因 ...