Open WU-SUNFLOWER opened 1 month ago
我是外校的学生,为了上手方便我仍然使用贵校开发的基于c语言实现的uCore进行练习。我不是很清楚贵校开发团队是否还在维护这个项目的c语言版本,也不了解贵校开发团队的rCore是否也存在类似的问题,如有打扰还请谅解!
以下是我个人给出的临时修复办法:
uCore官方实现的提供给用户程序使用的cprintf函数实现是非原子的。这会导致什么问题呢?
cprintf
举个例子,假设有两个进程A和B,如果进程A想打印字符串"Hello World",进程B想打印字符串"Thanks for You",则可能最终会在设备屏幕上输出"Hello Thanks for World"或者其他的混乱结果。这显然会影响我们正常开展本次实验,以及导致无法使用make grade对我们的代码进行评测!
make grade
这是因为uCore中用户进程每打印一个ASCII字符,都需要请求一次SYS_putc系统调用,因此虽然每次系统调用的执行是原子的,但这个用户程序在逐个打印字符的过程中仍然可能会被时钟中断,转而执行其他进程,从而导致屏幕输出结果的混乱。
SYS_putc
这里我采用最简单粗暴的办法来修复这个bug,即直接给cprintf函数上一把大锁。
首先,由于lab6中uCore还没有实现锁机制,我们需要在内核代码中自行封装一个自旋锁。
代码如下,其中原子操作__sync_lock_test_and_set和__sync_lock_release由gcc编译器提供,我们无需关心其具体实现:
__sync_lock_test_and_set
__sync_lock_release
// kern/sync/my_spin_lock.h // 这个结构体用于表示自旋锁 typedef struct { volatile int locked; } spinlock_t; void spin_lock(spinlock_t* lock); void spin_unlock(spinlock_t* lock); extern spinlock_t global_print_lock; // kern/sync/my_spin_lock.c #include <my_spin_lock.h> #include <sched.h> // 加锁函数 void spin_lock(spinlock_t *lock) { while (__sync_lock_test_and_set(&(lock->locked), 1)) { schedule(); } } // 解锁函数 void spin_unlock(spinlock_t *lock) { __sync_lock_release(&(lock->locked)); } // 初始化一个全局的自旋锁 spinlock_t global_print_lock = {0}; // 全局锁初始化为未锁定状态
然后,我们把加锁/解锁函数封装成系统调用,以便用户程序使用:
// libs/unistd.h #define SYS_print_lock 100 #define SYS_print_unlock 101
// kern/syscall/syscall.c #include <my_spin_lock.h> static int sys_print_lock(uint32_t arg[]) { spin_lock(&global_print_lock); return 0; } static int sys_print_unlock(uint32_t arg[]) { spin_unlock(&global_print_lock); return 0; } static int (*syscalls[])(uint32_t arg[]) = { // ... [SYS_print_lock] sys_print_lock, [SYS_print_unlock] sys_print_unlock, };
添加用户程序库中的系统调用接口:
// user/libs/syscall.h void sys_print_lock(void); void sys_print_unlock(void); // user/libs/syscall.c void sys_print_lock(void) { syscall(SYS_print_lock); } void sys_print_unlock(void) { syscall(SYS_print_unlock); }
最后,修改cprintf函数的底层代码:
// user/libs/stdio.c int vcprintf(const char *fmt, va_list ap) { int cnt = 0; sys_print_lock(); // 加锁 vprintfmt((void*)cputch, &cnt, fmt, ap); sys_print_unlock(); // 解锁 return cnt; }
我是外校的学生,为了上手方便我仍然使用贵校开发的基于c语言实现的uCore进行练习。我不是很清楚贵校开发团队是否还在维护这个项目的c语言版本,也不了解贵校开发团队的rCore是否也存在类似的问题,如有打扰还请谅解!
以下是我个人给出的临时修复办法:
uCore官方实现的提供给用户程序使用的
cprintf
函数实现是非原子的。这会导致什么问题呢?举个例子,假设有两个进程A和B,如果进程A想打印字符串"Hello World",进程B想打印字符串"Thanks for You",则可能最终会在设备屏幕上输出"Hello Thanks for World"或者其他的混乱结果。这显然会影响我们正常开展本次实验,以及导致无法使用
make grade
对我们的代码进行评测!这是因为uCore中用户进程每打印一个ASCII字符,都需要请求一次
SYS_putc
系统调用,因此虽然每次系统调用的执行是原子的,但这个用户程序在逐个打印字符的过程中仍然可能会被时钟中断,转而执行其他进程,从而导致屏幕输出结果的混乱。这里我采用最简单粗暴的办法来修复这个bug,即直接给
cprintf
函数上一把大锁。首先,由于lab6中uCore还没有实现锁机制,我们需要在内核代码中自行封装一个自旋锁。
代码如下,其中原子操作
__sync_lock_test_and_set
和__sync_lock_release
由gcc编译器提供,我们无需关心其具体实现:然后,我们把加锁/解锁函数封装成系统调用,以便用户程序使用:
添加用户程序库中的系统调用接口:
最后,修改
cprintf
函数的底层代码: