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

实验四(上)题目:clone 线程时无法实现安全的栈拷贝 #106

Open
losfair opened this issue Jul 24, 2020 · 10 comments
Open
Assignees
Labels
bug Something isn't working

Comments

@losfair
Copy link

losfair commented Jul 24, 2020

涉及文件

https://rcore-os.github.io/rCore-Tutorial-deploy/docs/lab-4/practice-1.html

相关段落

实验四(上)对 clone() 的要求是:

实验:实现线程的 clone()。目前的内核线程不能进行系统调用,所以我们先简化地实现为“按 C 进行 clone”。clone 后应当为目前的线程复制一份几乎一样的拷贝,新线程与旧线程同属一个进程,公用页表和大部分内存空间,而新线程的栈是一份拷贝。

遇到问题

新线程的栈和寄存器内都可能有对栈上地址的引用,拷贝栈不会更新这些引用。于是在新栈上继续执行的代码对局部变量的访问会错误地引用旧栈上的地址。

建议做以下修改之一:

  1. 定义 clone() 为拷贝当前进程而非线程。
  2. 要求显式指定新线程的入口地址,并重新分配空栈而非拷贝栈。
@losfair losfair added the bug Something isn't working label Jul 24, 2020
@yukiiiteru
Copy link
Contributor

yukiiiteru commented Jul 24, 2020

我认为实验是没有问题的,我已经完成了对线程的 clone,并没有遇到这方面的问题

开辟新的栈,然后修改栈顶指针 sp 即可,不会用到 unsafe 的代码

我对该 issue 的理解不太正确,实验的确有问题

@yunwei37
Copy link
Contributor

yunwei37 commented Jul 24, 2020

这应当是 fork 和 clone 语义的区别,如果是拷贝当前进程的话应当是 fork 而非 clone。如果是复制进程的话,应该可以使该进程的虚拟地址与原进程的虚拟地址相同,但对应于不同的物理地址页,此时复制栈之后就不会出现引用旧线程变量的问题,也不需要重新分配空栈,这样应该是可行的;重新分配空栈的话,应该也对应于新创建线程的 create 语义而不是 clone。

@losfair
Copy link
Author

losfair commented Jul 24, 2020

@wfly1998

未定义的行为并不总会导致可见的问题。

考虑以下代码(未测试):

let x: i32 = 42;
let x_ref = &x;
if clone_thread() == 0 {
    assert!(&x as *const _ == x_ref as *const _, "path 1");
} else {
    assert!(&x as *const _ == x_ref as *const _, "path 2");
}

在不开启任何编译优化的情况下,你认为是否会有一个 assert! 命中呢?

@losfair
Copy link
Author

losfair commented Jul 24, 2020

@yunwei37

是的,拷贝进程会完整复制虚拟地址空间,应该就没问题了

@chibinz
Copy link
Contributor

chibinz commented Jul 24, 2020

直接在同一地址空间复制栈还有一个危险,即使引用没有失效,也有可能出现两个mutable reference。悄无声息的发生data race,是一种implicit的unsafe。所以应该只有@losfair提出的两种方法可行,拷贝进程或者重新开一个栈……😢

@yukiiiteru
Copy link
Contributor

yukiiiteru commented Jul 24, 2020

@losfair

抱歉,是我理解的错误,我以为你说的无法实现安全的栈拷贝指的是会用到 unsafe 代码块,是我没有考虑到这一点

我查阅了 rCore 对 sys_clone 的实现,我发现 sys_clone 传入的参数中有新的线程指针 newtls 还有新的堆栈指针 newsp,这说明新的线程并不是拷贝了原线程,而是启动了一条新线程并执行了其它函数

@Tuyixiang
Copy link
Collaborator

感谢指出问题,这确实是我们在沿用以前版本的实验题目时没有考虑到新版本框架的不同

题目会改为实现进程的 fork,而如果大家做了“并不安全的 clone”也不需要重新再做一次 fork。

@yukiiiteru
Copy link
Contributor

突然意识到,改成 fork 进程后,实验六的挑战实验是不是就做不成了啊
我认为 clone 的缺陷是可以规避的,比如尽可能早地 clone,并且在 clone 前不使用引用

Tuyixiang added a commit that referenced this issue Jul 25, 2020
@Tuyixiang
Copy link
Collaborator

突然意识到,改成 fork 进程后,实验六的挑战实验是不是就做不成了啊
我认为 clone 的缺陷是可以规避的,比如尽可能早地 clone,并且在 clone 前不使用引用

实验六会要求做 sys_fork 复制进程,同时也就会复制文件描述符,我认为没有问题

@freheit889
Copy link

如果分配空栈 实验六的sys_open就会报错 ,所以第二种方法并不可取

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

6 participants