LearningOS/rcore_step_by_step

ZYR:rcore_step_by_step-流水帐式记录

DeathWish5 opened this issue · 5 comments

概述

2019/7/16

配置好了rust windows开发环境。完成猜数字程序。

由于实习原因进度较慢,不定期更新。

问题

  • 玄学问题:Blocking waiting for file lock on package cache lock 无限等待。

    解决:重启电脑后解决。但尚未断定原因,可能由于电脑开启太多应用导致进程未知错误。

  • 问题:直接使用 rand::random::<>() 与使用 rand::thread_rng().gen::<>() 有什么区别吗?

  • 其他问题:在某些linux内核中,不可以使用浮点操作。为何在用户态可以使用,内核反而不可以?这么设计是出于那些考虑?假定在不考虑效率的情况下,有什么简单的方法或者库(比如编译选项)可以用整形模拟浮点吗?
    内核编译模块时自带gcc参数 -mno-sse -mno-sse2,这直接导致了内核模块不能使用SSE,SSE2指令,但是这好像并不是说不能使用所有的浮点操作。

  • Blocking waiting for file lock on package cache lock 无限等待。

    一般是另一个 cargo 程序正在运行,可能是 IDE 在后台自动执行 cargo。
    可尝试 kill 掉所有 cargo 进程再试。

  • 直接使用 rand::random::<>() 与使用 rand::thread_rng().gen::<>() 有什么区别吗?

    rand::random 的文档中表示:

    This is simply a shortcut for thread_rng().gen().

    所以看上去是一样的。

  • 在某些 linux 内核中,不可以使用浮点操作。为何在用户态可以使用,内核反而不可以?这么设计是出于那些考虑?

    浮点运算需要 FPU 协处理器的支持,上古时代的 CPU 没有这个东西,所以那个时候的 OS 也不支持硬浮点。
    另一方面,一般 OS 内核不会用到浮点计算。并且引入浮点寄存器后,上下文切换会有更多开销。因此内核中往往不会使用硬浮点,而是用整数模拟。

    假定在不考虑效率的情况下,有什么简单的方法或者库(比如编译选项)可以用整形模拟浮点吗?

    可以通过编译选项设置。gcc 我不太清楚,Rust 是在 target 描述文件中加入以下内容来设置的:

    "features": "-mmx,-sse,+soft-float"

概述

2019/7/17

学习了所有权、引用、切片。

问题

  • 引用的生命周期

    参考如下代码

     fn main() {
         let mut s = String::from("hello");
         let rs = &mut s;
         // 正常编译运行
         rs.push_str(", world");
         s.push_str("! I'm tom.");
     }
      fn main() {
          let mut s = String::from("hello");
          let rs = &mut s;
          // 报错:cannot borrow `s` as mutable more than once at a time
          // 以下两行注释掉任意一行或者交换顺序,正常运行
          s.push_str("! I'm tom.");
          rs.push_str(", world");
      }

    这种现象该怎么理解呢?操作可变引用与直接操作原值有什么限制吗?

  • 引用的本质(先写下来,好像可以通过学习8、15章解决)

    很自然想到对比C++的引用。在C++中,引用本质上和指针类似,汇编层面上通过传递地址实现(好像是,没考证)。直觉上,rust的引用应该也是这样实现的,但是:

    fn main() {
        let mut s = String::from("hello");
        let rs = &mut s;
        rs.push_str(", world");
        (*rs).push_str("! I'm tom.");
        println!("{ }", s);
    
        let mut a : i32 = 3;
        let ra = &mut a;
        // ra += 2; 不能这样写
        *ra += 2;
        println!("{ }", a);
    }

    一方面,引用可以直接解引用之后操作原值,另一方面,对于String类型,引用不需要 ->运算符就可以直接和原值一样操作,但是对于 i32 就不可以,这仅仅是因为 i32 是一个标量吗?

  • 引用的生命周期

    fn main() {
        let mut s = String::from("hello");
        let rs = &mut s;
        rs.push_str(", world");
        // 根据 Rust 最新的 NLL(Non Lexical Lifetime) 规则
        // `rs` 在此处之后不再使用,生命周期在此终结,进而对 `s` 的可变借用在此释放 
        // 于是下面可以继续可变借用 `s`
        s.push_str("! I'm tom.");
    }
    fn main() {
        let mut s = String::from("hello");
        let rs = &mut s;
        s.push_str("! I'm tom.");  // 此处 `s` 已经被 `rs` 可变借用,不能再次借用,故报错
        rs.push_str(", world");    // `rs` 在此被使用,因此其生命周期持续到这里
    }
  • 引用的本质

    这里的区别是 调用方法运算符
    如果改成 += 运算符,一样会报错:

    rs.push_str(", world"); // ok
    rs += ", world";        // x
    *rs += ", world";       // ok

    调用方法时编译器会自动解引用,直到找到一个存在的方法。
    但使用运算符时就需要手动解引用。
    注:关于自动解引用可参考这里,不过好像跟这个例子关系不大……

概述

2019/7/18-19

结构体、枚举,模块,常见集合,错误处理。

实践较少,没发现有价值的问题,明天好好看看例子,应该有启发。

问题

  • 内核异常与用户态异常的区别

    ​ 应用程序panic,会产生中断,切换到内核态执行异常处理例程,panic的话一般直接杀死进程然后调度。感觉上,内核的非致命进程发生错误,应该可以用同样的流程处理,甚至不用发生切换。但是今天在虚拟机上挂载了一个自定义的内核模块(TCP包过滤程序,不核心),发生了除0错(迁移到用户态后发现),系统的表现有两种:1.直接死机 2.出错进程停止工作失去响应(由于查不到进程号,不能人工kill),过一会后计算机开始轰鸣。那么内核的进程出错后的处理与用户态有何本质区别呢?为何我的虚拟机没有直接杀死错误进程?

概述

2019/7/18-19

结构体、枚举,模块,常见集合,错误处理。

实践较少,没发现有价值的问题,明天好好看看例子,应该有启发。

问题

  • 内核异常与用户态异常的区别
    ​ 应用程序panic,会产生中断,切换到内核态执行异常处理例程,panic的话一般直接杀死进程然后调度。感觉上,内核的非致命进程发生错误,应该可以用同样的流程处理,甚至不用发生切换。但是今天在虚拟机上挂载了一个自定义的内核模块(TCP包过滤程序,不核心),发生了除0错(迁移到用户态后发现),系统的表现有两种:1.直接死机 2.出错进程停止工作失去响应(由于查不到进程号,不能人工kill),过一会后计算机开始轰鸣。那么内核的进程出错后的处理与用户态有何本质区别呢?为何我的虚拟机没有直接杀死错误进程?

U worked so hard, so as to become a master of rust ! Respect professor Zhang !