diff --git a/.github/workflows/github-pages.yml b/.github/workflows/github-pages.yml index 9ad5c8f7..a6fbd126 100644 --- a/.github/workflows/github-pages.yml +++ b/.github/workflows/github-pages.yml @@ -1,9 +1,12 @@ name: GitHub Pages on: push: + tags-ignore: + - '**' paths: - "Pages/**" - "README.md" + - "CHANGELOG.md" workflow_dispatch: concurrency: diff --git a/.github/workflows/lab1.yml b/.github/workflows/lab1.yml index a098e494..3e488681 100644 --- a/.github/workflows/lab1.yml +++ b/.github/workflows/lab1.yml @@ -4,7 +4,10 @@ on: push: paths: - Lab1/** + tags-ignore: + - '**' workflow_dispatch: + workflow_call: permissions: contents: read diff --git a/.github/workflows/lab2.yml b/.github/workflows/lab2.yml index 856aecd0..24ec34ac 100644 --- a/.github/workflows/lab2.yml +++ b/.github/workflows/lab2.yml @@ -4,7 +4,10 @@ on: push: paths: - Lab2/** + tags-ignore: + - '**' workflow_dispatch: + workflow_call: permissions: contents: read diff --git a/.github/workflows/lab3.yml b/.github/workflows/lab3.yml index 4252e9d1..039a7272 100644 --- a/.github/workflows/lab3.yml +++ b/.github/workflows/lab3.yml @@ -4,7 +4,10 @@ on: push: paths: - Lab3/** + tags-ignore: + - '**' workflow_dispatch: + workflow_call: permissions: contents: read diff --git a/.github/workflows/lab4.yml b/.github/workflows/lab4.yml index 5448dcd5..3c84a20c 100644 --- a/.github/workflows/lab4.yml +++ b/.github/workflows/lab4.yml @@ -4,7 +4,10 @@ on: push: paths: - Lab4/** + tags-ignore: + - '**' workflow_dispatch: + workflow_call: permissions: contents: read diff --git a/.github/workflows/lab5.yml b/.github/workflows/lab5.yml index d0dec370..f2a4d6a7 100644 --- a/.github/workflows/lab5.yml +++ b/.github/workflows/lab5.yml @@ -4,7 +4,10 @@ on: push: paths: - Lab5/** + tags-ignore: + - '**' workflow_dispatch: + workflow_call: permissions: contents: read diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..d3daec73 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,53 @@ +name: Release + +on: + push: + tags: + - "*" + +jobs: + lab1_check: + uses: ./.github/workflows/lab1.yml + lab2_check: + uses: ./.github/workflows/lab2.yml + lab3_check: + uses: ./.github/workflows/lab3.yml + lab4_check: + uses: ./.github/workflows/lab4.yml + lab5_check: + uses: ./.github/workflows/lab5.yml + create_release: + runs-on: ubuntu-latest + needs: [lab1_check, lab2_check, lab3_check, lab4_check, lab5_check] + steps: + - name: Checkout repository with submodules + uses: actions/checkout@v3 + with: + submodules: true + fetch-depth: 0 + + - name: Download git-archive-all.sh script + run: | + curl -o git-archive-all.sh https://raw.githubusercontent.com/fabacab/git-archive-all.sh/master/git-archive-all.sh + chmod +x git-archive-all.sh + + - name: Create archive with submodules + run: | + ./git-archive-all.sh --prefix "${GITHUB_REPOSITORY##*/}/" --format tar.gz release.tar.gz + + - name: Create release + id: create_release + uses: softprops/action-gh-release@v2 + if: startsWith(github.ref, 'refs/tags/') + with: + body_path: ./CHANGELOG.md + + - name: Upload archive to release + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./release.tar.gz + asset_name: "${{ github.event.repository.name }}-${{ github.ref_name }}.tar.gz" + asset_content_type: application/gzip diff --git a/CHANGELOG.md b/CHANGELOG.md index a0cf709b..56ddbec9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +1,8 @@ -# CHANGELOG +# v24.09 更新内容 + +- 集中化统一的目录统一 +- 完成Lab1-Lab5的迁移设置 +- 更新基于vscode的lab的开发环境 +- 更新lsp设置 +- 修复多个构建以及运行时问题 +- 迁移文档至mdbook diff --git a/Lab3/scores.json b/Lab3/scores.json index 8ae2da2a..c1ce3d30 100644 --- a/Lab3/scores.json +++ b/Lab3/scores.json @@ -10,7 +10,7 @@ "proposed": 20 }, { - "capture": "Hello userland!", + "capture": "Hello Userland!", "msg": "Userland", "proposed": 20, "userland": true @@ -22,7 +22,7 @@ "userland": true }, { - "capture": "Hello ChCore", + "capture": "Hello ChCore!", "msg": "Userland App", "proposed": 20, "userland": true diff --git a/Lab4/kernel/ipc/connection.c.dbg.obj b/Lab4/kernel/ipc/connection.c.dbg.obj deleted file mode 100644 index e40d64dd..00000000 Binary files a/Lab4/kernel/ipc/connection.c.dbg.obj and /dev/null differ diff --git a/Lab4/kernel/ipc/connection.c.obj b/Lab4/kernel/ipc/connection.c.obj deleted file mode 100644 index bd22004b..00000000 Binary files a/Lab4/kernel/ipc/connection.c.obj and /dev/null differ diff --git a/Lab4/scores.json b/Lab4/scores.json index 63aca615..856f4f7b 100644 --- a/Lab4/scores.json +++ b/Lab4/scores.json @@ -18,13 +18,13 @@ "capture": "Cooperative Scheduling Test Done!", "msg": "Cooperative Scheduling", "proposed": 20, - "userland": true + "userland": true }, { "capture": "Preemptive Scheduling Test Done!", - "msg": "Preemptive Scheduling", - "proposed": 20, - "userland": true + "msg": "Preemptive Scheduling", + "proposed": 20, + "userland": true }, { "capture": "Test IPC finished!", diff --git a/Lab4/user/system-services/system-servers/procmgr/procmgr.c b/Lab4/user/system-services/system-servers/procmgr/procmgr.c index 2bcb889d..8d583795 100644 --- a/Lab4/user/system-services/system-servers/procmgr/procmgr.c +++ b/Lab4/user/system-services/system-servers/procmgr/procmgr.c @@ -701,7 +701,7 @@ void *thread_routine1(void *arg) printf("Hello from thread 2\n"); usys_yield(); } - printf("Cooperative Schedluing Test Done!\n"); + printf("Cooperative Scheduling Test Done!\n"); return NULL; } @@ -729,7 +729,7 @@ void test_sched(void) pthread_create(&tid, NULL, thread_routine2, NULL); usys_yield(); printf("Thread 1 successfully regains the control!\n"); - printf("Preemptive Schedluing Test Done!\n"); + printf("Preemptive Scheduling Test Done!\n"); } int main(int argc, char *argv[], char *envp[]) diff --git a/Lab6/chbuild b/Lab6/chbuild index 0a0decb5..f8f464c7 100755 --- a/Lab6/chbuild +++ b/Lab6/chbuild @@ -278,7 +278,7 @@ _docker_run() { docker run -i $use_tty --rm \ -u $(id -u ${USER}):$(id -g ${USER}) \ -v $(pwd):$(pwd) -w $(pwd) \ - ipads/chcore_builder:v1.9.0 \ + ipads/oslab:24.09 \ $self $@ fi } diff --git a/Pages/Lab3/thread.md b/Pages/Lab3/thread.md index 77da2d40..1be7d654 100644 --- a/Pages/Lab3/thread.md +++ b/Pages/Lab3/thread.md @@ -13,6 +13,9 @@ ChCore 中仅使用了其中的两个异常级别:EL0 和 EL1。其中,EL1 > [!CODING] 练习题1 > 在 `kernel/object/cap_group.c` 中完善 `sys_create_cap_group`、`create_root_cap_group` 函数。在完成填写之后,你可以通过 Cap create pretest 测试点。 +> [!SUCCESS] CapGroup +> 完成`create_root_cap_group`函数后并通过测试后,你可以得到20分。 + > [!HINT] > 可以阅读 `kernel/object/capability.c` 中各个与 cap 机制相关的函数以及参考文档。 @@ -55,4 +58,4 @@ ChCore 中仅使用了其中的两个异常级别:EL0 和 EL1。其中,EL1 --- > [!SUCCESS] -> 以上为Lab3 Part1 的所有内容,完成后你将获得20分 +> 以上为Lab3 Part1 的所有内容,完成后你将获得40分 diff --git a/Pages/Lab4/IPC.md b/Pages/Lab4/IPC.md index 9be5a2ca..dd0fb1f1 100644 --- a/Pages/Lab4/IPC.md +++ b/Pages/Lab4/IPC.md @@ -20,34 +20,34 @@ ChCore的IPC接口不是传统的send/recv接口。其更像客户端/服务器 ## 具体流程 为了实现ChCore IPC的功能,首先需要在Client与Server端创建起一个一对一的IPC Connection。该Connection保存了IPC Server的服务线程(即上图中IPC handler Thread)、Client与Server的共享内存(用于存放IPC通信的内容)。同一时刻,一个Connection只能有一个Client接入,并使用该Connection切换到Server的处理流程。ChCore提供了一系列机制,用于创建Connection以及创建每个Connection对应的服务线程。下面将以具体的IPC注册到调用的流程,详细介绍ChCore的IPC机制: -1. IPC服务器的主线程调用: `ipc_register_server` (user/chcore-libc/musl-libc/src/chcore-port/ipc.c中)来声明自己为IPC的服务器端。 +1. IPC服务器的主线程调用: `ipc_register_server` (`user/chcore-libc/musl-libc/src/chcore-port/ipc.c`中)来声明自己为IPC的服务器端。 * 参数包括server_handler和client_register_handler,其中server_handler为服务端用于提供服务的回调函数(比如上图中IPC handler Thread的入口函数`ipc_dispatcher`);client_register_handler为服务端提供的用于注册的回调函数,该函数会创建一个注册回调线程。 - * 随后调用ChCore提供的的系统调用:`sys_register_server`。该系统调用实现在kernel/ipc/connection.c当中,该系统调用会分配并初始化一个`struct ipc_server_config`和一个`struct ipc_server_register_cb_config`。之后将调用者线程(即主线程)的general_ipc_config字段设置为创建的`struct ipc_server_config`,其中记录了注册回调线程和IPC服务线程的入口函数(即图中的`ipc_dispatcher`)。将注册回调线程的general_ipc_config字段设置为创建的`struct ipc_server_register_cb_config`,其中记录了注册回调线程的入口函数和用户态栈地址等信息。 + * 随后调用ChCore提供的的系统调用:`sys_register_server`。该系统调用实现在`kernel/ipc/connection.c`当中,该系统调用会分配并初始化一个`struct ipc_server_config`和一个`struct ipc_server_register_cb_config`。之后将调用者线程(即主线程)的general_ipc_config字段设置为创建的`struct ipc_server_config`,其中记录了注册回调线程和IPC服务线程的入口函数(即图中的`ipc_dispatcher`)。将注册回调线程的general_ipc_config字段设置为创建的`struct ipc_server_register_cb_config`,其中记录了注册回调线程的入口函数和用户态栈地址等信息。 -2. IPC客户端线程调用`ipc_register_client`(定义在user/chcore-libc/musl-libc/src/chcore-port/ipc.c中)来申请建立IPC连接。 +2. IPC客户端线程调用`ipc_register_client`(定义在`user/chcore-libc/musl-libc/src/chcore-port/ipc.c`中)来申请建立IPC连接。 * 该函数仅有一个参数,即IPC服务器的主线程在客户端进程cap_group中的capability。该函数会首先通过系统调用申请一块物理内存作为和服务器的共享内存(即图中的Shared Memory)。 - * 随后调用`sys_register_client`系统调用。该系统调用实现在kernel/ipc/connection.c当中,该系统调用会将刚才申请的物理内存映射到客户端的虚拟地址空间中,然后调用`create_connection`创建并初始化一个`struct ipc_connection`类型的内核对象,该内核对象中的shm字段会记录共享内存相关的信息(包括大小,分别在客户端进程和服务器进程当中的虚拟地址和capability)。 + * 随后调用`sys_register_client`系统调用。该系统调用实现在`kernel/ipc/connection.c`当中,该系统调用会将刚才申请的物理内存映射到客户端的虚拟地址空间中,然后调用`create_connection`创建并初始化一个`struct ipc_connection`类型的内核对象,该内核对象中的shm字段会记录共享内存相关的信息(包括大小,分别在客户端进程和服务器进程当中的虚拟地址和capability)。 * 之后会设置注册回调线程的栈地址、入口地址和第一个参数,并切换到注册回调线程运行。 -3. 注册回调线程运行的入口函数为主线程调用`ipc_register_server`是提供的client_register_handler参数,一般会使用默认的`DEFAULT_CLIENT_REGISTER_HANDLER`宏定义的入口函数,即定义在user/chcore-libc/musl-libc/src/chcore-port/ipc.c中的`register_cb`。 +3. 注册回调线程运行的入口函数为主线程调用`ipc_register_server`是提供的client_register_handler参数,一般会使用默认的`DEFAULT_CLIENT_REGISTER_HANDLER`宏定义的入口函数,即定义在`user/chcore-libc/musl-libc/src/chcore-port/ipc.c`中的`register_cb`。 * 该函数首先分配一个用来映射共享内存的虚拟地址,随后创建一个服务线程。 * 随后调用`sys_ipc_register_cb_return`系统调用进入内核,该系统调用将共享内存映射到刚才分配的虚拟地址上,补全`struct ipc_connection`内核对象中的一些元数据之后切换回客户端线程继续运行,客户端线程从`ipc_register_client`返回,完成IPC建立连接的过程。 -4. IPC客户端线程调用`ipc_create_msg`和`ipc_set_msg_data`向IPC共享内存中填充数据,然后调用`ipc_call`(user/chcore-libc/musl-libc/src/chcore-port/ipc.c中)发起IPC请求。 +4. IPC客户端线程调用`ipc_create_msg`和`ipc_set_msg_data`向IPC共享内存中填充数据,然后调用`ipc_call`(`user/chcore-libc/musl-libc/src/chcore-port/ipc.c`中)发起IPC请求。 - * `ipc_call`中会发起`sys_ipc_call`系统调用(定义在kernel/ipc/connection.c中),该系统调用将设置服务器端的服务线程的栈地址、入口地址、各个参数,然后迁移到该服务器端服务线程继续运行。由于当前的客户端线程需要等待服务器端的服务线程处理完毕,因此需要更新其状态为TS_WAITING,且不要加入等待队列。 + * `ipc_call`中会发起`sys_ipc_call`系统调用(定义在`kernel/ipc/connection.c`中),该系统调用将设置服务器端的服务线程的栈地址、入口地址、各个参数,然后迁移到该服务器端服务线程继续运行。由于当前的客户端线程需要等待服务器端的服务线程处理完毕,因此需要更新其状态为TS_WAITING,且不要加入等待队列。 5. IPC服务器端的服务线程在处理完IPC请求之后使用`ipc_return`返回。 * `ipc_return`会发起`sys_ipc_return`系统调用,该系统调用会迁移回到IPC客户端线程继续运行,IPC客户端线程从`ipc_call`中返回。 > [!CODING] 练习题 7 -> 在user/chcore-libc/musl-libc/src/chcore-port/ipc.c与kernel/ipc/connection.c中实现了大多数IPC相关的代码,请根据注释补全kernel/ipc/connection.c中的代码。之后运行ChCore可以看到 “[TEST] Test IPC finished!” 输出,你可以通过 Test IPC 测试点。 +> 在`user/chcore-libc/musl-libc/src/chcore-port/ipc.c与kernel/ipc/connection.c`中实现了大多数IPC相关的代码,请根据注释补全`kernel/ipc/connection.c`中的代码。之后运行ChCore可以看到 “[TEST] Test IPC finished!” 输出,你可以通过 Test IPC 测试点。 --- diff --git a/Pages/Lab4/scheduler.md b/Pages/Lab4/scheduler.md index 69e5d7ca..6fc0a3ee 100644 --- a/Pages/Lab4/scheduler.md +++ b/Pages/Lab4/scheduler.md @@ -11,7 +11,7 @@ ChCore中与调度相关的函数与数据结构定义在`kernel/include/sched/s ``` sched_ops是用于抽象ChCore中调度器的一系列操作。它存储指向不同调度操作的函数指针,以支持不同的调度策略。 cur_sched_ops则是一个sched_ops的实例,其在内核初始化过程中(main函数)调用sched_init进行初始化。 -ChCore用在 kernel/include/sched/sched.h 中定义的静态函数封装对cur_sched_ops的调用。sched_ops中定义的调度器操作如下所示: +ChCore用在 `kernel/include/sched/sched.h` 中定义的静态函数封装对cur_sched_ops的调用。sched_ops中定义的调度器操作如下所示: * sched_init:初始化调度器。 * sched:进行一次调度。即将正在运行的线程放回就绪队列,然后在就绪队列中选择下一个需要执行的线程返回。 @@ -19,16 +19,16 @@ ChCore用在 kernel/include/sched/sched.h 中定义的静态函数封装对cur_s * sched_dequeue:从调度器的就绪队列中取出一个线程。 * sched_top:用于debug,打印当前所有核心上的运行线程以及等待线程的函数。 -在本部分将实现一个基本的Round Robin(时间片轮转)调度器,该程序调度在同一CPU核心上运行的线程,因此内核初始化过程调用sched_init时传入了&rr作为参数。该调度器的调度操作(即对于sched_ops定义的各个函数接口的实现)实现在kernel/sched/policy_rr.c中,这里简要介绍其涉及的数据结构: +在本部分将实现一个基本的Round Robin(时间片轮转)调度器,该程序调度在同一CPU核心上运行的线程,因此内核初始化过程调用sched_init时传入了&rr作为参数。该调度器的调度操作(即对于sched_ops定义的各个函数接口的实现)实现在`kernel/sched/policy_rr.c`中,这里简要介绍其涉及的数据结构: `current_threads`是一个数组,分别指向每个CPU核心上运行的线程。而`current_thread`则利用`smp_get_cpu_id`获取当前运行核心的id,从而找到当前核心上运行的线程。 -`struct queue_meta`定义了round robin调度器使用的就绪队列,其中`queue_head`字段是连接该就绪队列上所有等待线程的队列,`queue_len`字段是目前该就绪队列的长度,`queue_lock`字段是用于保证该队列并发安全的锁。 kernel/sched/policy_rr.c定义了一个全局变量`rr_ready_queue_meta`,该变量是一个`struct queue_meta`类型的数组,数组大小由`PLAT_CPU_NUM`定义,即代表每个CPU核心都具有一个就绪队列。运行的CPU核心可以通过`smp_get_cpu_id`获取当前运行核心的id,从而在该数组中找到当前核心对应的就绪队列。 +`struct queue_meta`定义了round robin调度器使用的就绪队列,其中`queue_head`字段是连接该就绪队列上所有等待线程的队列,`queue_len`字段是目前该就绪队列的长度,`queue_lock`字段是用于保证该队列并发安全的锁。 `kernel/sched/policy_rr.c`定义了一个全局变量`rr_ready_queue_meta`,该变量是一个`struct queue_meta`类型的数组,数组大小由`PLAT_CPU_NUM`定义,即代表每个CPU核心都具有一个就绪队列。运行的CPU核心可以通过`smp_get_cpu_id`获取当前运行核心的id,从而在该数组中找到当前核心对应的就绪队列。 ## 调度队列初始化 -内核初始化过程中会调用`sched_init`初始化调度相关的元数据,`sched_init`定义在kernel/sched/sched.c中,该函数首先初始化idle_thread(每个CPU核心拥有一个idle_thread,当调度器的就绪队列中没有等待线程时会切换到idle_thread运行),然后会初始化`current_threads`数组,最后调用`struct sched_ops rr`中定义的sched_init函数,即`rr_sched_init`。 +内核初始化过程中会调用`sched_init`初始化调度相关的元数据,`sched_init`定义在`kernel/sched/sched.c`中,该函数首先初始化idle_thread(每个CPU核心拥有一个idle_thread,当调度器的就绪队列中没有等待线程时会切换到idle_thread运行),然后会初始化`current_threads`数组,最后调用`struct sched_ops rr`中定义的sched_init函数,即`rr_sched_init`。 > [!CODING] 练习题 1 -> 在 kernel/sched/policy_rr.c 中完善 `rr_sched_init` 函数,对 `rr_ready_queue_meta` 进行初始化。在完成填写之后,你可以看到输出“Scheduler metadata is successfully initialized!”并通过 Scheduler metadata initialization 测试点。 +> 在 `kernel/sched/policy_rr.c` 中完善 `rr_sched_init` 函数,对 `rr_ready_queue_meta` 进行初始化。在完成填写之后,你可以看到输出“Scheduler metadata is successfully initialized!”并通过 Scheduler metadata initialization 测试点。 > [!HINT] Tip > sched_init 只会在主 CPU 初始化时调用,因此 rr_sched_init 需要对每个 CPU 核心的就绪队列都进行初始化。 @@ -49,17 +49,17 @@ ChCore用在 kernel/include/sched/sched.h 中定义的静态函数封装对cur_s `rr_sched_choose_thread`内部会调用`find_runnable_thread`从当前CPU核心的就绪队列中选取一个可以运行的线程并调用`__rr_sched_dequeue`将其从就绪队列中移除。 > [!CODING] 练习 3 -> 在 kernel/sched/sched.c 中完善 `find_runnable_thread` 函数,在就绪队列中找到第一个满足运行条件的线程并返回。 在 kernel/sched/policy_rr.c 中完善 `__rr_sched_dequeue` 函数,将被选中的线程从就绪队列中移除。 +> 在 kernel/sched/sched.c 中完善 `find_runnable_thread` 函数,在就绪队列中找到第一个满足运行条件的线程并返回。 在 `kernel/sched/policy_rr.c` 中完善 `__rr_sched_dequeue` 函数,将被选中的线程从就绪队列中移除。 > [!SUCCESS] > 在完成填写之后,运行 ChCore 将可以成功进入用户态,你可以看到输出“Enter Procmgr Root thread (userspace)”并通过 Schedule Enqueue 测试点。 ## 协作式调度 -顾名思义,协作式调度需要线程主动放弃CPU。为了实现该功能,我们提供了`sys_yield`这一个系统调用(syscall)。该syscall可以主动放弃当前CPU核心,并调用上述的`sched`接口完成调度器的调度工作。kernel/sched/policy_rr.c中定义的`rr_sched`函数中,如果当前运行线程的状态为`TS_RUNNING`,即还处于可以运行的状态,我们应该将其重新加入到就绪队列当中,这样该线程在之后才可以被再次调度执行。 +顾名思义,协作式调度需要线程主动放弃CPU。为了实现该功能,我们提供了`sys_yield`这一个系统调用(syscall)。该syscall可以主动放弃当前CPU核心,并调用上述的`sched`接口完成调度器的调度工作。`kernel/sched/policy_rr.c`中定义的`rr_sched`函数中,如果当前运行线程的状态为`TS_RUNNING`,即还处于可以运行的状态,我们应该将其重新加入到就绪队列当中,这样该线程在之后才可以被再次调度执行。 > [!CODING] 练习 4 -> 在kernel/sched/sched.c中完善系统调用`sys_yield`,使用户态程序可以主动让出CPU核心触发线程调度。 -> 此外,请在kernel/sched/policy_rr.c 中完善`rr_sched`函数,将当前运行的线程重新加入调度队列中。 +> 在`kernel/sched/sched.c`中完善系统调用`sys_yield`,使用户态程序可以主动让出CPU核心触发线程调度。 +> 此外,请在`kernel/sched/policy_rr.c` 中完善`rr_sched`函数,将当前运行的线程重新加入调度队列中。 > [!SUCCESS] > 在完成填写之后,运行 ChCore 将可以成功进入用户态并创建两个线程交替执行,你可以看到输出“Cooperative Schedluing Test Done!”并通过 Cooperative Schedluing 测试点。 @@ -68,7 +68,7 @@ ChCore用在 kernel/include/sched/sched.h 中定义的静态函数封装对cur_s 使用刚刚实现的协作式调度器,ChCore能够在线程主动调用`sys_yield`系统调用让出CPU核心的情况下调度线程。然而,若用户线程不想放弃对CPU核心的占据,内核便只能让用户线程继续执行,而无法强制用户线程中止。 因此,在这一部分中,本实验将实现抢占式调度,以帮助内核定期重新获得对CPU核心的控制权。 -ChCore启动的第一个用户态线程(执行user/system-services/system-servers/procmgr/procmgr.c的`main`函数)将创建一个“自旋线程”,该线程在获得CPU核心的控制权后便会执行无限循环,进而导致无论是该程序的主线程还是ChCore内核都无法重新获得CPU核心的控制权。就保护系统免受用户程序中的错误或恶意代码影响而言,这一情况显然并不理想,任何用户应用线程均可以如该“自旋线程”一样,通过进入无限循环来永久“霸占”整个CPU核心。 +ChCore启动的第一个用户态线程(执行`user/system-services/system-servers/procmgr/procmgr.c`的`main`函数)将创建一个“自旋线程”,该线程在获得CPU核心的控制权后便会执行无限循环,进而导致无论是该程序的主线程还是ChCore内核都无法重新获得CPU核心的控制权。就保护系统免受用户程序中的错误或恶意代码影响而言,这一情况显然并不理想,任何用户应用线程均可以如该“自旋线程”一样,通过进入无限循环来永久“霸占”整个CPU核心。 为了处理“自旋线程”的问题,ChCore内核必须具有强行中断一个正在运行的线程并夺回对CPU核心的控制权的能力,为此我们必须扩展ChCore以支持处理来自物理时钟的外部硬件中断。 @@ -82,10 +82,10 @@ ChCore启动的第一个用户态线程(执行user/system-services/system-serv * CNTP_TVAL_EL0: 是一个32位寄存器,操作系统可以写入 TVAL,处理器会在内部读取当前的系统计数,加上写入的值,然后填充 CVAL。 * CNTP_CTL_EL0: 物理时钟的控制寄存器,第0位ENABLE控制时钟是否开启,1代表enble,0代表disable;第1位IMASK代表是否屏蔽时钟中断,0代表不屏蔽,1代表屏蔽。 -对物理时钟进行初始化的代码位于kernel/arch/aarch64/plat/raspi3/irq/timer.c的`plat_timer_init`函数。 +对物理时钟进行初始化的代码位于`kernel/arch/aarch64/plat/raspi3/irq/timer.c`的`plat_timer_init`函数。 > [!CODING] 练习 5 -> 请根据代码中的注释在kernel/arch/aarch64/plat/raspi3/irq/timer.c中完善`plat_timer_init`函数,初始化物理时钟。需要完成的步骤有: +> 请根据代码中的注释在`kernel/arch/aarch64/plat/raspi3/irq/timer.c`中完善`plat_timer_init`函数,初始化物理时钟。需要完成的步骤有: > * 读取 CNTFRQ_EL0 寄存器,为全局变量 cntp_freq 赋值。 > * 根据 TICK_MS(由ChCore决定的时钟中断的时间间隔,以ms为单位,ChCore默认每10ms触发一次时钟中断)和cntfrq_el0 (即物理时钟的频率)计算每两次时钟中断之间 system count 的增长量,将其赋值给 cntp_tval 全局变量,并将 cntp_tval 写入 CNTP_TVAL_EL0 寄存器! > * 根据上述说明配置控制寄存器CNTP_CTL_EL0。 @@ -95,12 +95,12 @@ ChCore启动的第一个用户态线程(执行user/system-services/system-serv **物理时钟中断与抢占** -我们在lab3中已经为ChCore配置过异常向量表(kernel/arch/aarch64/irq/irq_entry.S),当收到来自物理时钟的外部中断时,内核会进入`handle_irq`中断处理函数,该函数会调用平台相关的`plat_handle_irq`来进行中断处理。`plat_handle_irq`内部如果判断中断源为物理时钟,则调用`handle_timer_irq`。 +我们在lab3中已经为ChCore配置过异常向量表(`kernel/arch/aarch64/irq/irq_entry.S`),当收到来自物理时钟的外部中断时,内核会进入`handle_irq`中断处理函数,该函数会调用平台相关的`plat_handle_irq`来进行中断处理。`plat_handle_irq`内部如果判断中断源为物理时钟,则调用`handle_timer_irq`。 ChCore记录每个线程所拥有的时间片(`thread->thread_ctx->sc->budget`),为了能够让线程之间轮转运行,我们应当在处理时钟中断时递减当前运行线程的时间片,并在当前运行线程的时间片耗尽时进行调度,选取新的线程运行。 > [!CODING] 练习 6 -> 请在kernel/arch/aarch64/plat/raspi3/irq/irq.c中完善`plat_handle_irq`函数,当中断号irq为INT_SRC_TIMER1(代表中断源为物理时钟)时调用`handle_timer_irq`并返回。 请在kernel/irq/irq.c中完善`handle_timer_irq`函数,递减当前运行线程的时间片budget,并调用sched函数触发调度。 请在kernel/sched/policy_rr.c中完善`rr_sched`函数,在将当前运行线程重新加入就绪队列之前,恢复其调度时间片budget为DEFAULT_BUDGET。 +> 请在`kernel/arch/aarch64/plat/raspi3/irq/irq.c`中完善`plat_handle_irq`函数,当中断号irq为INT_SRC_TIMER1(代表中断源为物理时钟)时调用`handle_timer_irq`并返回。 请在kernel/irq/irq.c中完善`handle_timer_irq`函数,递减当前运行线程的时间片budget,并调用sched函数触发调度。 请在`kernel/sched/policy_rr.c`中完善`rr_sched`函数,在将当前运行线程重新加入就绪队列之前,恢复其调度时间片budget为DEFAULT_BUDGET。 > [!SUCCESS] > 在完成填写之后,运行 ChCore 将可以成功进入用户态并打断创建的“自旋线程”让内核和主线程可以拿回CPU核心的控制权,你可以看到输出`"Hello, I am thread 3. I'm spinning."`和`“Thread 1 successfully regains the control!”`并通过 `Preemptive Scheduling` 测试点。 diff --git a/README.md b/README.md index b2bc57e2..ec0ea66c 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,6 @@ [![forthebadge](Assets/docs.svg)](https://sjtu-ipads.github.io/OS-Course-Lab/) [![forthebadge cc-nc-sa](http://ForTheBadge.com/images/badges/cc-nc-sa.svg)](https://creativecommons.org/licenses/by-nc-sa/4.0) -> [!WARNING] -> 本实验仓库正在进行重构升级,请优先使用各个分支下的代码进行实验,main分支的代码目前并不保证运行正常。本条目将在维护完成后删除。 - 本仓库包含上海交通大学IPADS实验室设计的操作系统课程系列实验。每个实验位于独立的目录。 课程教材: @@ -56,7 +53,7 @@ Tutorial: 该实验关注虚拟文件系统(Virtual File System,VFS), VFS抽象层使得不同类型的文件系统可以在应用程序层面以统一的方式进行访问。 -## Lab6:GUI +## Lab6:GUI (Optional) 该实验将详细介绍ChCore上基于Wayland的GUI系统的运行原理,包括Wayland通信协议和Wayland Compositor,并且要求读者在了解基于Wayland的GUI系统运行原理的基础上,基于ChCore的GUI框架编写自己的具有GUI界面的APP。