提交 5b5c636a 编写于 作者: lean2's avatar lean2

change structure

上级 61964eb1
.DS_Store
\ No newline at end of file
.DS_Store
# Added by cargo
/target
# 2021 rCore夏令营
进度更新在notion中,以下是notion链接
[总记录]("https://www.notion.so/xuebling/rCore-bf1f5369c3f840aab22485415e55e0e6")
[第一周记录]("https://www.notion.so/xuebling/7-1-7-7-f3392fd94608422382f970b4b93914cf")
[第二周记录]("https://www.notion.so/xuebling/7-8-7-14-bef87401b4fc496399888317078f7dd2")
\ No newline at end of file
# 7月1日-7月7
学习内容: 做Rust编程语言练习题
虽然以前已经写过一些rust项目,但是做下练习也挺不错的,以下是对做练习时学到东西的一些总结
## 初级项目:
### rustlings
1. struct 更新语法,以前忽略了,很实用的语法糖
```bash
let your_order = Order{name: String::from("Hacker in Rust"), count: 1, ..order_template};
```
2. 宏可以定义多个语法表达式
3. 为集合创建迭代器通常有三种方法
```bash
iter(), which iterates over &T.
iter_mut(), which iterates over &mut T.
into_iter(), which iterates over T.
```
4. 自定义error只需要创建一个结构体,derive Debug trait,在impl Display和Error这两个trait就可以了
![rustling已完成](../pics/rustling_done.png)
### [32 Rust Quizes](https://dtolnay.github.io/rust-quiz/1)
## Rust中级练习题
1. 287 寻找重复数:
给定一个包含 n + 1 个整数的数组 nums ,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。
假设 nums 只有 一个重复的整数 ,找出 这个重复的数 。
你设计的解决方案必须不修改数组 nums 且只用常量级 O(1) 的额外空间。
```rust
impl Solution {
//转化成有环链表寻找环的起点的问题
pub fn find_duplicate(nums: Vec<i32>) -> i32 {
let mut s = 0;
let mut f = 0;
loop {
s = nums[s as usize];
f = nums[nums[f as usize] as usize];
if s == f { break; }
}
s = 0;
while s != f {
s = nums[s as usize];
f = nums[f as usize];
}
s
}
}
```
```java
class Solution {
public int findDuplicate(int[] nums) {
int slow = 0, fast = 0;
do {
slow = nums[slow];
fast = nums[nums[fast]];
} while (slow != fast);
slow = 0;
while (slow != fast) {
slow = nums[slow];
fast = nums[fast];
}
return slow;
}
}
```
总结:
- 这道题没想明白,看了题解做出来的,题目还是挺有意思的,竟然能够转化为链表的问题。
- rust数组不能使用i32作为索引,在刷题时总是有点不方便。
- 模式识别:以后遇到给一个数组A[i,j],数组内的数均 i<x<j 的这种情况,就可以考虑从链表的角度去解决问题了,
- 这道题也可以用二分法和位运算去做
## 本周总结
这周事情有点多,所以只把rustling给做了,外加做了一道算法题,下周要把rust练习题全部搞定,然后剩下两周来做rCore实验。
\ No newline at end of file
# 7月8日-7月14日
学习内容: rust 编程基础
继续用rust 刷leetcode
1. 198. 打家劫舍
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
```rust
use std::cmp;
impl Solution {
pub fn rob(nums: Vec<i32>) -> i32 {
let len = nums.len();
let mut dp = Vec::with_capacity(len);
dp.push(nums[0]);
if len > 1 {
dp.push(cmp::max(nums[0],nums[1]));
}
if len<=2 {
return dp[dp.len()-1];
}
let mut i = 2;
while i<len {
dp.push(cmp::max(dp[i-2]+nums[i],dp[i-1]));
i+=1;
}
dp[dp.len()-1]
}
}
```
```java
class Solution {
public int rob(int[] nums) {
if (nums == null || nums.length == 0) {
return 0;
}
int length = nums.length;
if (length == 1) {
return nums[0];
}
int[] dp = new int[length];
dp[0] = nums[0];
dp[1] = Math.max(nums[0], nums[1]);
for (int i = 2; i < length; i++) {
dp[i] = Math.max(dp[i - 2] + nums[i], dp[i - 1]);
}
return dp[length - 1];
}
}
```
总结:通过观察题目可知满足最优子结构,因此使用动态规划来解决,列出状态转移方程
dp[i] = cmp::max( nums[i] + dp[i-2], dp[i-1] ),接下来只需要愉快的写代码就可以啦,由于状态只跟dp[i-2], dp[i-1]有关,因此可以优化为大小为2的数组,实现O(1)空间复杂度。
2. 200. 岛屿数量
给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。
岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。
此外,你可以假设该网格的四条边均被水包围。
```rust
impl Solution {
pub fn num_islands(mut grid: Vec<Vec<char>>) -> i32 {
if grid.len() == 0 || grid[0].len() == 0 {
return 0
}
let mut ans = 0;
let mut s = Vec::new();
for x in 0..grid.len() {
for y in 0..grid[x].len() {
if grid[x][y] == '1'{
s.push((x, y));
while let Some((i, j)) = s.pop() {
if grid[i][j] == '1' {
grid[i][j] = '0';
if j + 1 < grid[0].len() {
s.push((i, j + 1))
}
if j > 0 {
s.push((i, j - 1))
}
if i + 1 < grid.len() {
s.push((i + 1, j))
}
if i > 0 {
s.push((i - 1, j))
}
}
}
ans += 1;
}
}
}
ans
}
}
```
```java
//Java 并查集解法
class Solution {
class UnionFind {
int count;
int[] parent;
int[] rank;
public UnionFind(char[][] grid) {
count = 0;
int m = grid.length;
int n = grid[0].length;
parent = new int[m * n];
rank = new int[m * n];
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
if (grid[i][j] == '1') {
parent[i * n + j] = i * n + j;
++count;
}
rank[i * n + j] = 0;
}
}
}
public int find(int i) {
if (parent[i] != i) parent[i] = find(parent[i]);
return parent[i];
}
public void union(int x, int y) {
int rootx = find(x);
int rooty = find(y);
if (rootx != rooty) {
if (rank[rootx] > rank[rooty]) {
parent[rooty] = rootx;
} else if (rank[rootx] < rank[rooty]) {
parent[rootx] = rooty;
} else {
parent[rooty] = rootx;
rank[rootx] += 1;
}
--count;
}
}
public int getCount() {
return count;
}
}
public int numIslands(char[][] grid) {
if (grid == null || grid.length == 0) {
return 0;
}
int nr = grid.length;
int nc = grid[0].length;
int num_islands = 0;
UnionFind uf = new UnionFind(grid);
for (int r = 0; r < nr; ++r) {
for (int c = 0; c < nc; ++c) {
if (grid[r][c] == '1') {
grid[r][c] = '0';
if (r - 1 >= 0 && grid[r-1][c] == '1') {
uf.union(r * nc + c, (r-1) * nc + c);
}
if (r + 1 < nr && grid[r+1][c] == '1') {
uf.union(r * nc + c, (r+1) * nc + c);
}
if (c - 1 >= 0 && grid[r][c-1] == '1') {
uf.union(r * nc + c, r * nc + c - 1);
}
if (c + 1 < nc && grid[r][c+1] == '1') {
uf.union(r * nc + c, r * nc + c + 1);
}
}
}
}
return uf.getCount();
}
}
```
总结: 这题属于搜索类题目, 因此深搜和宽搜任选其一就可解决, 注意剪枝条件一般就没啥问题, 由于宽搜是基于栈的, 比基于递归的深搜效率稍高点, 所以选择了宽搜解法. 另外贴了一个Java的并查集解法, 不是自己写的, 一直以来觉得并查集好麻烦,没有写过,打算之后用rust写一个试试
3. 207. 课程表
你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1 。
在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisites[i] = [ai, bi] ,表示如果要学习课程 ai 则 必须 先学习课程  bi 。
例如,先修课程对 [0, 1] 表示:想要学习课程 0 ,你需要先完成课程 1 。
请你判断是否可能完成所有课程的学习?如果可以,返回 true ;否则,返回 false 。
```rust
impl Solution {
pub fn can_finish(num_courses: i32, prerequisites: Vec<Vec<i32>>) -> bool {
let num_courses = num_courses as usize;
let mut p = vec![vec![]; num_courses];
let mut c = vec![0; num_courses];
for v in prerequisites {
let (i, j) = (v[0] as usize, v[1] as usize);
p[j].push(i);
c[i] += 1;
}
let mut q = (0..num_courses).filter(|&i| c[i] == 0).collect::<Vec<_>>();
while !q.is_empty() {
let mut t = vec![];
for i in q {
for &j in &p[i] {
c[j] -= 1;
if c[j] == 0 {
t.push(j)
}
}
}
q = t;
}
c.into_iter().all(|j| j == 0)
}
}
```
```java
class Solution {
List<List<Integer>> edges;
int[] visited;
boolean valid = true;
public boolean canFinish(int numCourses, int[][] prerequisites) {
edges = new ArrayList<List<Integer>>();
for (int i = 0; i < numCourses; ++i) {
edges.add(new ArrayList<Integer>());
}
visited = new int[numCourses];
for (int[] info : prerequisites) {
edges.get(info[1]).add(info[0]);
}
for (int i = 0; i < numCourses && valid; ++i) {
if (visited[i] == 0) {
dfs(i);
}
}
return valid;
}
public void dfs(int u) {
visited[u] = 1;
for (int v: edges.get(u)) {
if (visited[v] == 0) {
dfs(v);
if (!valid) {
return;
}
} else if (visited[v] == 1) {
valid = false;
return;
}
}
visited[u] = 2;
}
}
```
总结: rust的iter太好用啦! 跟以前java的迭代器很像, 但是由于rust有range, 所以比java更好用! 这道题也是搜索类题目, 所以用了宽搜来做, 但也可以用拓扑排序的思路做
4. 337. 打家劫舍 III
在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为“根”。 除了“根”之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果两个直接相连的房子在同一天晚上被打劫,房屋将自动报警。
计算在不触动警报的情况下,小偷一晚能够盗取的最高金额。
```rust
use std::rc::Rc;
use std::cell::RefCell;
impl Solution {
pub fn rob(root: Option<Rc<RefCell<TreeNode>>>) -> i32 {
let res=dfs(root);
res.0.max(res.1)
}
}
fn dfs(root: Option<Rc<RefCell<TreeNode>>>)->(i32,i32) {
if let Some(o)=root {
let l = dfs(o.borrow_mut().left.clone());
let r = dfs(o.borrow_mut().right.clone());
(o.borrow().val + l.1 + r.1, (l.0).max(l.1) + (r.0).max(r.1))
}else{(0,0)}
}
```
```java
class Solution {
Map<TreeNode, Integer> f = new HashMap<TreeNode, Integer>();
Map<TreeNode, Integer> g = new HashMap<TreeNode, Integer>();
public int rob(TreeNode root) {
dfs(root);
return Math.max(f.getOrDefault(root, 0), g.getOrDefault(root, 0));
}
public void dfs(TreeNode node) {
if (node == null) {
return;
}
dfs(node.left);
dfs(node.right);
f.put(node, node.val + g.getOrDefault(node.left, 0) + g.getOrDefault(node.right, 0));
g.put(node, Math.max(f.getOrDefault(node.left, 0), g.getOrDefault(node.left, 0)) + Math.max(f.getOrDefault(node.right, 0), g.getOrDefault(node.right, 0)));
}
}
```
总结: 终于用到了智能指针, 早听说rust写数据结构很麻烦, 现在看来, 确实有点麻烦, Rc每次都要borrow有点麻烦, 要是有语法糖能够简化一下就好了, 不过rust的抽象能力还是比java要高, 尤其是解构语法, 太讨喜了, 能少写好多模版代码.
5. 17. 合并二叉树
给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。
你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。
```rust
use std::rc::Rc;
use std::cell::RefCell;
impl Solution {
pub fn merge_trees(root1: Option<Rc<RefCell<TreeNode>>>, root2: Option<Rc<RefCell<TreeNode>>>) -> Option<Rc<RefCell<TreeNode>>> {
fn action(node0: &Option<Rc<RefCell<TreeNode>>>, node1: &Option<Rc<RefCell<TreeNode>>>) -> Option<Rc<RefCell<TreeNode>>> {
match (node0, node1) {
(Some(v0), Some(v1)) => {
let (b0, b1) = (v0.borrow(), v1.borrow());
let mut r = TreeNode::new(b0.val + b1.val);
r.left = action(&b0.left, &b1.left);
r.right = action(&b0.right, &b1.right);
Some(Rc::new(RefCell::new(r)))
},
(Some(v0), None) => Some(v0.clone()),
(None, Some(v1)) => Some(v1.clone()),
(None, None) => None
}
}
action(&root1, &root2)
}
}
```
```java
class Solution {
public TreeNode mergeTrees(TreeNode t1, TreeNode t2) {
if (t1 == null) {
return t2;
}
if (t2 == null) {
return t1;
}
TreeNode merged = new TreeNode(t1.val + t2.val);
merged.left = mergeTrees(t1.left, t2.left);
merged.right = mergeTrees(t1.right, t2.right);
return merged;
}
}
```
总结: 又找了一道树的题来折磨自己, 不得不说, rust写这种数据结构, 是真的麻烦啊😂, 试了好多种写法, 都不得劲, 总是要写很多unwarp()呀, borrow()之类的, 最后抄了别人的写法, 舒服多了. 这种写法还是运用了解构和模式匹配, 省去了大量的let a = b.unwarp()这样的代码, 如果没有解构和模式匹配, 我肯定不会学rust了....
6. 21. 合并两个有序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
```rust
impl Solution {
pub fn merge_two_lists(l1: Option<Box<ListNode>>, l2: Option<Box<ListNode>>) -> Option<Box<ListNode>> {
match (l1, l2) {
(Some(node1), None) => Some(node1),
(None, Some(node2)) => Some(node2),
(Some(mut node1), Some(mut node2)) => {
if node1.val < node2.val {
let n = node1.next.take();
node1.next = Solution::merge_two_lists(n, Some(node2));
Some(node1)
} else {
let n = node2.next.take();
node2.next = Solution::merge_two_lists(Some(node1), n);
Some(node2)
}
},
_ => None,
}
}
}
```
```java
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if (l1 == null) {
return l2;
} else if (l2 == null) {
return l1;
} else if (l1.val < l2.val) {
l1.next = mergeTwoLists(l1.next, l2);
return l1;
} else {
l2.next = mergeTwoLists(l1, l2.next);
return l2;
}
}
}
```
总结: 直接把上一题的代码copy过来, 稍微改改这题就出来了, 做了几道需要用模式匹配的题之后发现, 模式匹配比 if 强大太多了, 代码也比if else 好看很多
7. 647. 回文子串
给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。
具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。
```rust
impl Solution {
pub fn count_substrings(s: String) -> i32 {
let mut res = 0;
let s = s.chars().collect::<Vec<char>>();
let len = s.len();
let total = 2 * len - 1;
for i in 0..total {
let mut x = i / 2;
let mut y = i / 2 + i % 2;
while x < len && y < len && s[x] == s[y] {
x -= 1;
y += 1;
res += 1;
}
}
res
}
}
```
```java
class Solution6472 {
public int countSubstrings(String s) {
int ans = 0;
for (int center = 0; center < 2 * s.length() - 1; center++) {
int left = center / 2;
int right = left + center % 2;
while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
ans++;
left--;
right++;
}
}
return ans;
}
}
```
总结: 通过这道题来练习下rust的字符串处理
8. 219. 存在重复元素 II
给定一个整数数组和一个整数 k,判断数组中是否存在两个不同的索引 i 和 j,使得 nums [i] = nums [j],并且 i 和 j 的差的 绝对值 至多为 k。
```rust
use std::collections::HashMap;
impl Solution {
pub fn contains_nearby_duplicate(nums: Vec<i32>, k: i32) -> bool {
let len = nums.len();
let mut map = HashMap::new();
for i in 0..len {
if let Some(&j) = map.get(&nums[i]) {
if (i as i32 - j as i32) <= k {
return true;
}
}
map.insert(nums[i], i);
}
false
}
}
```
```java
public boolean containsNearbyDuplicate(int[] nums, int k) {
Set<Integer> set = new HashSet<>();
for (int i = 0; i < nums.length; ++i) {
if (set.contains(nums[i])) return true;
set.add(nums[i]);
if (set.size() > k) {
set.remove(nums[i - k]);
}
}
return false;
}
```
总结: 啊写什么呢, 又从别人的答案里学到一招: 解构引用. 万物皆可解构
9. 114. 二叉树展开为链表
给你二叉树的根结点 root ,请你将它展开为一个单链表:
展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null 。
展开后的单链表应该与二叉树 先序遍历 顺序相同。
```rust
use std::rc::Rc;
use std::cell::RefCell;
impl Solution {
pub fn flatten(root: &mut Option<Rc<RefCell<TreeNode>>>) {
if let Some(ref mut node) = root {
Self::flatten(&mut node.borrow_mut().left);
Self::flatten(&mut node.borrow_mut().right);
let right = node.borrow_mut().right.take();
let left = node.borrow_mut().left.take();
node.borrow_mut().right = left;
let mut right_clone = node.clone();
while right_clone.borrow().right.is_some() {
let current = right_clone.borrow().right.clone().unwrap();
right_clone = current;
}
right_clone.borrow_mut().right = right;
}
}
}
```
```java
class Solution {
public void flatten(TreeNode root) {
List<TreeNode> list = new ArrayList<TreeNode>();
Deque<TreeNode> stack = new LinkedList<TreeNode>();
TreeNode node = root;
while (node != null || !stack.isEmpty()) {
while (node != null) {
list.add(node);
stack.push(node);
node = node.left;
}
node = stack.pop();
node = node.right;
}
int size = list.size();
for (int i = 1; i < size; i++) {
TreeNode prev = list.get(i - 1), curr = list.get(i);
prev.left = null;
prev.right = curr;
}
}
}
```
### 本周总结 & Rust 编程语言学习总结
这两周在实习之余把rustling和10道leetcode题做完了, 我的实习内容是用rust写后端程序, 使用的框架是rocket, orm是rbatis, 可以说我每一天都是满满rust了.
用rust写了大概三千多行代码吧, 有点信心说自己入了rust的门了, 下图是后端程序的代码量
![rust代码量](../pics/code_lines_count.png)
在用rust写程序的过程中, 我有一些体会:
当经过一个较高的学习曲线之后, 我rust确实如人们所宣传的那样, 拥有高性能, 安全, 抽象度高的特点, 这些特点体现在:
1. 一个没有过度复杂的逻辑和长时间io的后端程序接口处理时间平均在20ms;
2. 只要编译通过, 把result和option处理好, 程序就不会panic, 不像之前写java, 经常做噩梦梦到NullPointerException;
3. 掌握了解构和模式匹配, trait, rust的抽象能力不亚于一众脚本语言, 极大的提高了开发效率, 后端程序的错误处理我使用thiserror和anyhow两个crate来简化, 基本不会用match来处理错误, 直接?抛出去, 最后将自定义的Error序列化返回给前端, 新开发一个api需要的时间非常短
但我也遇到过许多困惑, 或者说期待吧:
1. 要是rust的enum里能够使用常量就好了, 最好enum里的的struct, 也能使用常量
在网上搜了下, 这个回答很好的解决了我的疑问
[How can I create enums with constant values in Rust?](https://stackoverflow.com/questions/36928569/how-can-i-create-enums-with-constant-values-in-rust)
原来不支持是没必要支持, 于是我我写了代码进行了测试
```java
pub enum Test{
A,
B = 100,
}
fn main() {
println!("{}",Test::A);
println!("{}",Test::A as i32);
println!("{}",Test::B as i32);
}
```
第一个println直接报错了, 编译器告诉我, Test没有实现Display trait
第二个println竟然打印出了0?
第三个println打印出了100, 这是我想要的结果
为什么第二个println打印出了0呢, 我在这里找到了答案, 看来有问题去翻reference准没错的
[The Rust Reference](https://doc.rust-lang.org/reference/items/enumerations.html)
原来enum有一个Discriminant(翻译过来叫判别器?) 简单来说就是给enum里的item一个默认值, 第一项为0, 依次递增
这下我就明白了, 如果需要将enum中的一个item跟另一个值建立联系, 写一个函数来返回对应的值就好了, 如果真的需要给item赋值, 那么就只能给item赋一个isize, 现在不支持给item赋别的类型的值
但是根据我的理解, 这个Discriminant应该是item在enum中的序号,Discriminant 因此翻译为序号更合适, 这个序号在一些场景可能会很有用, 比如要将enum序列化的时候.
对于rust的学习, 结束了, 也开始了, 下周开始学rcore了, 进度有点慢, 要抓紧了, 实习太占时间的话, 可能会辞掉
\ No newline at end of file
[build]
target = "riscv64imac-unknown-none-elf"
[target.riscv64imac-unknown-none-elf]
rustflags = [
"-Clink-arg=-Tsrc/linker.ld", "-Cforce-frame-pointers=yes"
]
\ No newline at end of file
[package]
name = "os"
version = "0.1.0"
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
[profile.dev]
panic = "abort"
# panic 后直接调用panic_handler处理,禁止堆栈展开
[profile.release]
panic = "abort"
\ No newline at end of file
TARGET := riscv64imac-unknown-none-elf
MODE := debug
KERNEL_FILE := target/$(TARGET)/$(MODE)/os
BIN_FILE := target/$(TARGET)/$(MODE)/os.bin
BASE_ADDRESS:= 0x80200000
#BOOTLOADER := ../bootloader/rustsbi-qemu.bin
BOOTLOADER := default
OBJDUMP := rust-objdump --arch-name=riscv64
OBJCOPY := rust-objcopy --binary-architecture=riscv64
.PHONY: doc kernel build clean qemu run
# 默认 build 为输出二进制文件
build: $(BIN_FILE)
# 通过 Rust 文件中的注释生成 kernel 的文档
doc:
@cargo doc --document-private-items
# 编译 kernel
kernel:
@cargo build
# 生成 kernel 的二进制文件
$(BIN_FILE): kernel
@$(OBJCOPY) $(KERNEL_FILE) --strip-all -O binary $@
# 查看反汇编结果
asm:
@$(OBJDUMP) -d $(KERNEL_FILE) | less
# 清理编译出的文件
clean:
@cargo clean
# 运行 QEMU
qemu: build
@qemu-system-riscv64 \
-machine virt \
-nographic \
-bios $(BOOTLOADER) \
-device loader,file=$(BIN_FILE),addr=$(BASE_ADDRESS)
debug: build
@tmux new-session -d \
"qemu-system-riscv64 -machine virt -nographic -bios $(BOOTLOADER) -device loader,file=$(BIN_FILE),addr=$(BASE_ADDRESS) -s -S" && \
tmux split-window -h "riscv64-unknown-elf-gdb -ex 'file $(KERNEL_FILE)' -ex 'set arch riscv:rv64' -ex 'target remote localhost:1234'" && \
tmux -2 attach-session -d
# 一键运行
run: qemu
\ No newline at end of file
# 作用是用来装载操作系统,也就是
# 但是这个汇编文件是如何获取到linker.ld中声明的_start的呢
# linker.ld 该文件的关系是什么
# 我们在 linker.ld 中将程序入口设置为了 _start,因此在这里我们将填充这个标签
.section .text.entry
.globl _start
_start:
# 写入栈空间
la sp, boot_stack_top
# 调用操作系统主函数
call rust_main
# 回忆:bss 段是 ELF 文件中只记录长度,而全部初始化为 0 的一段内存空间
# 这里声明字段 .bss.stack 作为操作系统启动时的栈
.section .bss.stack
.global boot_stack
boot_stack:
# 16K 启动栈大小
.space 4096 * 16
.global boot_stack_top
boot_stack_top:
# 栈结尾
\ No newline at end of file
/* 作用是指导编译器设置内存布局,设置各个段的位置 */
/* 目标架构 */
OUTPUT_ARCH(riscv)
/* 执行入口,因此编译器会将_start这个函数作为程序的入口,而_start会调用rustmain,从而启动操作系统*/
ENTRY(_start)
/* 数据存放起始地址 */
BASE_ADDRESS = 0x80200000;
SECTIONS
{
/* . 表示当前地址(location counter) */
. = BASE_ADDRESS;
/* start 符号表示全部的开始位置 */
kernel_start = .;
text_start = .;
.text : {
/* 该段是在entry.asm中声明的 */
*(.text.entry)
*(.text .text.*)
}
rodata_start = .;
.rodata : {
*(.rodata .rodata.*)
}
data_start = .;
.data : {
*(.data .data.*)
}
bss_start = .;
.bss : {
*(.sbss .bss .bss.*)
}
kernel_end = .;
}
\ No newline at end of file
#![no_std]
#![no_main]
#![feature(global_asm)]
#![feature(llvm_asm)]
use core::panic::PanicInfo;
global_asm!(include_str!("entry.asm"));
pub fn console_putchar(ch: u8) {
let _ret: usize;
let arg0: usize = ch as usize;
let arg1: usize = 0;
let arg2: usize = 0;
let which: usize = 1;
unsafe {
llvm_asm!("ecall"
: "={x10}" (_ret)
: "{x10}" (arg0), "{x11}" (arg1), "{x12}" (arg2), "{x17}" (which)
: "memory"
: "volatile"
);
}
}
#[no_mangle]
pub extern "C" fn rust_main() -> !{
loop{}
}
#[panic_handler]
fn panic(info: &PanicInfo) -> !{
loop {}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册