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

rCore-Tutorial-Book-v3/chapter4/6multitasking-based-on-as #50

Open
utterances-bot opened this issue Mar 23, 2021 · 30 comments
Open

rCore-Tutorial-Book-v3/chapter4/6multitasking-based-on-as #50

utterances-bot opened this issue Mar 23, 2021 · 30 comments
Labels
comments An area where readers can discuss related topics after every article.

Comments

@utterances-bot
Copy link

基于地址空间的分时多任务 — rCore-Tutorial-Book-v3 0.1 文档

https://rcore-os.github.io/rCore-Tutorial-Book-v3/chapter4/6multitasking-based-on-as.html

Copy link

说起来这里的 trap_from_kernel 的地址并不保证是 4 字节对齐的,毕竟虽然 Riscv 的一条指令是 4 字节,但是加入压缩指令集 C 扩展之后也会有 2 字节长的指令。而这可能就有一定几率导致一些奇怪的问题,比如在内核态产生异常之后直接跑飞了()。
由于 Rust 语言还未支持为函数指定对齐(参考 rust-lang/rust#75072 ),可以把这个函数挪到汇编里,再由汇编调用 Rust 函数产生 panic。
当然,也许这并不是一个影响很大的问题,毕竟大家的内核最终写完之后是不会在 S 态触发由 S 态处理的异常的()。

@wyfcyx
Copy link
Collaborator

wyfcyx commented Mar 23, 2021

@benpigchu 如果后面需要支持处理从内核态进来的Trap的话,需要额外再写一套不同的__alltraps和__restore,它们都是汇编,就可以调整对齐了。现在这样确实也是有点bug,可能导致触发异常之后不能正确panic而是死循环,不过还能满足当前内核的功能需求。

@wyfcyx wyfcyx added the comments An area where readers can discuss related topics after every article. label Mar 23, 2021
Copy link

why986 commented Mar 27, 2021

“跳板的实现”一节中“到这里,我们就全程在内核地址空间中完成了保存 Trap 上下文的工作。”,“内核地址空间”是否应该改为“应用地址空间”?

@wyfcyx
Copy link
Collaborator

wyfcyx commented Mar 27, 2021

@why986 确实如此,多谢指出。

Copy link

trap_return中的内联汇编:

        llvm_asm!("jr $0"
            :: "r"(restore_va), "{a0}"(trap_cx_ptr), "{a1}"(user_satp)
            :: "volatile"
        );

$0这是placeholder语法么? 但是看这个RFC, 好像不是这样?

Copy link

我觉得, TaskControlBlock中pub trap_cx_ppn: PhysPageNum这个字段并不必要, 它只用了一次, 就是在get_trap_cx这里, 而且本来获取它也很容易, 调用一下translate即可

@wyfcyx
Copy link
Collaborator

wyfcyx commented Mar 29, 2021

@Tokubara 大概就是一个空间和性能的权衡问题吧,毕竟手动查页表在没有TLB加持的情况下更不cache友好。

Copy link

MapArea的unmap方法没有被调用过, 但是我对它的实现有疑问, unmap最终调用的是PageTable的unmap方法, 这个方法只是回收了PPN, 但是并未将相应PTE重置为0.
比如说有一个虚拟地址所在的虚拟页被unmap了, CPU看到这个虚拟地址, 页表上的PTE的V位还是1, 就不会发现错误.
因此我觉得, 在回收一个物理页的时候, 也就是在StackFrameAllocator的dealloc方法中, 需要把这个物理页清0

@wyfcyx
Copy link
Collaborator

wyfcyx commented Mar 29, 2021

@Tokubara 在PageTable::unmap的时候重置了页表项:

pub fn unmap(&mut self, vpn: VirtPageNum) {
    let pte = self.find_pte_create(vpn).unwrap();
    assert!(pte.is_valid(), "vpn {:?} is invalid before unmapping", vpn);
    *pte = PageTableEntry::empty();
}

注意最后一行。

Copy link

第 33~46 行,用上面的信息来创建并返回任务控制块实例 task_control_block;这里说的好像是33~36行

Copy link
Collaborator

问一下为什么单核+静态的 KERNEL_SPACE 需要 Arc?

Copy link

请问为什么trap.S中的__alltraps中的上下文不直接保存在应用对应的内核栈中而是保存在TrapContext对应的内存中

Copy link

dzwduan commented Apr 11, 2022

使用Rc会报错线程不安全

Copy link

dzwduan commented Apr 11, 2022

asm!("sfence.vma" :::: "volatile");直接用会报错,我改成了 asm!("volatile", "sfence.vma");

Copy link

重新映射的trap.S好难调试啊,gdb跟到地址空间切换就报异常了卡了好久检查代码,发现好像是gdb跟踪不到新地址空间了代码全速运行是好的,不知道有啥办法能更好的调试没有

@jiangshengdev
Copy link

重新映射的trap.S好难调试啊,gdb跟到地址空间切换就报异常了卡了好久检查代码,发现好像是gdb跟踪不到新地址空间了代码全速运行是好的,不知道有啥办法能更好的调试没有

csrw satp, a1
sfence.vma

遇到同样的问题,执行了 sfence.vma 之后,GDB就无法继续调试了

@jiangshengdev
Copy link

jiangshengdev commented May 7, 2022

重新映射的trap.S好难调试啊,gdb跟到地址空间切换就报异常了卡了好久检查代码,发现好像是gdb跟踪不到新地址空间了代码全速运行是好的,不知道有啥办法能更好的调试没有

在lab4之前,对内存访问没有什么限制,但是在lab4启动虚拟内存之后,无法越权访问内存
应该是断点处的内存地址不可访问导致的,使用delete+断点编号删除断点即可
切换到用户程序后,使用
file ../user/target/riscv64gc-unknown-none-elf/debug/00power_3
加载用户程序调试信息,使用b syscall设置用户态断点
切换系统用户/内核状态之前需要清除断点
待切换回内核态时,再使用
file target/riscv64gc-unknown-none-elf/debug/os
重新加载内核调试信息,使用b *0xfffffffffffff000等设置内核断点

以上方法可以调试ecall/sret这样手动切换状态,但是对时钟这样的自动中断好像还不好调试

Copy link

jiangshengdev commented May 9, 2022

为了方便调试用户应用程序,可以对项目进行适当修改
将/user/src/linker.ld文件/DISCARD/段中的*(.debug*)移除,即保留程序的debug信息
由于保留debug信息后,程序文件体积较大,还需要对内存大小进行修改
将/os/src/config.rs文件中的MEMORY_END = 0x80800000修改为0x84000000
这样gdb调试时进入到用户程序代码后,切换gdb file文件后,即可显示应用程序所对应的源代码,以提升调试体验,效果截图如下:

https://i.v2ex.co/Y2TRAMNY.png

Copy link

最后 sys_write 里面,如果有 utf-8 字符跨页的话会从中间截断,所以似乎不能这么简单处理...?

Copy link

我们为何将应用的 Trap 上下文放到应用地址空间的次高页面而不是内核地址空间中的内核栈中呢?原因在于,在保存 Trap 上下文到内核栈中之前,我们必须完成两项工作:1)必须先切换到内核地址空间,这就需要将内核地址空间的 token 写入 satp 寄存器;2)之后还需要保存应用的内核栈栈顶的位置,这样才能以它为基址保存 Trap 上下文。这两步需要用寄存器作为临时周转,然而我们无法在不破坏任何一个通用寄存器的情况下做到这一点。因为事实上我们需要用到内核的两条信息:内核地址空间的 token ,以及应用的内核栈栈顶的位置,RISC-V却只提供一个 sscratch 寄存器可用来进行周转。所以,我们不得不将 Trap 上下文保存在应用地址空间的一个虚拟页面中,而不是切换到内核地址空间去保存。

关于这点没记错的话window里面的做法是,所有进程的页表在内核地址那一段都是指向的同一份真实的内核地址空间的页表的,这样在trap进内核的时候是可以直接使用内核空间的地址的不需要发生satp寄存器的切换,这样即保证了连续性也减小了satp切换导致的tlb缓存刷新的性能问题

@YdrMaster
Copy link
Collaborator

@AlwaysKing 参见 wiki。这样的设计也有需求,虽然确实导致性能下降

Copy link

hongjil commented Nov 29, 2022

在本章练习中,我们需要重写sys_get_time()函数,但是不像sys_write() 我们可以方便地用多个切片的方式去重复执行;我能想到的一种比较好的方式是先用一个本地变量去存储TimeVal,然后再拷贝到对应的切片上。

换句话说,如果有这种需要os/kernel去填充用户地址的情况,我们可能没有一个很好的办法做到zero-copy?

另外有人知道内核与应用地址空间的隔离在linux或者其他耳熟能详的OS中都是采纳的哪种方式?

Copy link

xukp20 commented Mar 4, 2023

改进sys_write一节中translated_byte_buffer的实现似乎没有更新为源码仓库中最新的版本,文档中的版本对于跨物理页end_va的offset为0的情况会出现push区间为空?
文档:

v.push(&ppn.get_bytes_array()[start_va.page_offset()..end_va.page_offset()]);

仓库:

if end_va.page_offset() == 0 {
    v.push(&mut ppn.get_bytes_array()[start_va.page_offset()..]);
} else {
    v.push(&mut ppn.get_bytes_array()[start_va.page_offset()..end_va.page_offset()]);
}

@wyfcyx
Copy link
Collaborator

wyfcyx commented Mar 4, 2023

@xukp20 多谢,已更新。

Copy link

TD-Sky commented Apr 13, 2023

syscall::fs::sys_write内,如何保证不会有字符的前后几个字节散布不同的两个物理页内?

@YdrMaster
Copy link
Collaborator

syscall::fs::sys_write内,如何保证不会有字符的前后几个字节散布不同的两个物理页内?

如果我没理解错的话😂不能保证。只不过测试都短,不会写满一页的。操作系统,很神奇吧。

@wyfcyx
Copy link
Collaborator

wyfcyx commented Apr 28, 2023

现在是只支持ASCII的,就没什么问题。如果输出中文或者一些奇怪的多字节码确实有可能有问题,但是暂时还不考虑支持。

@kayoch1n
Copy link

kayoch1n commented Jun 3, 2023

请问为什么trap.S中的__alltraps中的上下文不直接保存在应用对应的内核栈中而是保存在TrapContext对应的内存中

我猜是因为 __alltraps 刚进入的时候还是在应用空间。应用的内核栈是要在内核空间才能访问的,所以得先从 trap context 装载对应的 satp,而 trap context 在应用空间。这应该也是内核栈 跟 trap context分别在不同的 page的原因。

Copy link

我有个问题,在改进 Trap 处理的实现时,通过set_kernel_trap_entry函数将S态的异常处理地址设置成了trap_from_kernel函数,那时钟中断也会进入这个函数来处理吧,按照之前分时多任务的处理逻辑,当检测到是时钟中断时会进入_alltraps函数然后跳转到trap_handler来分发,从而进行调度。而在这里修改后,调度的过程是怎么发生的,我没想明白

Copy link

mm中的memory_set.rs中的from_elf函数运行时出现了Did not find ELF magic number该怎么处理呢?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
comments An area where readers can discuss related topics after every article.
Projects
None yet
Development

No branches or pull requests