-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Open
Description
我是外校的学生,为了上手方便我仍然使用贵校开发的基于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编译器提供,我们无需关心其具体实现:
// 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;
}
Metadata
Metadata
Assignees
Labels
No labels