Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

uCore Lab6中提供给用户应用程序使用的cprintf函数是非原子的 #95

Open
WU-SUNFLOWER opened this issue Sep 2, 2024 · 0 comments

Comments

@WU-SUNFLOWER
Copy link

我是外校的学生,为了上手方便我仍然使用贵校开发的基于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;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant