diff --git a/README.md b/README.md index 71022aa..3088d4b 100644 --- a/README.md +++ b/README.md @@ -1,110 +1,55 @@ -# 01 Lab 赛道 -Repo to store 2025 Hackthon contest entries. -> Ask: samuka007@dragonos.org -## 背景 -关键词:operating system, computer network +## 基本信息 -提供给大家一个学习的机会和契机,鼓励大家尝试自己尚未有机会开始接触的lab! +赛道:mit6.1810 -> [!WARNING] -> 本赛道有五个赛题,请选择其中一个赛题参赛! +姓名:林润新 -## 规定 -- 1 人一组,完成一项 Lab 赛题 -- 最终lab学习成果及文档需要提交 PR 到当前仓库 - 或提交至 https://gitea.scutosc.cn/ 并发送邮件告知评委 -- 选择 DragonOS 系的题目有加分 -## 综合评分细则 -1. 创新性与难度(25%) - 1. 完成的实验难度 - 2. 实验的新颖度 - 3. 对现实问题的指导意义 -2. 功能完备性(55%) - 1. 完成了实验 / 实现了预期功能(35%) - 2. 需要实践测试 - 1. 实践有启发性,实践的可复现性、应用性(15%) - 3. 需要写代码的 - 1. 运行性能(10%) - 2. 代码质量(5%) -3. 展示效果(20%) - 1. 文档展示 - 2. 路演效果 -## 操作系统方向 +## 完成的实验 -### 赛题一:MIT 6.S081: Operating System Engineering +[Lab traps: Traps](https://pdos.csail.mit.edu/6.828/2022/labs/traps.html) -#### 背景 -麻省理工学院大名鼎鼎的 PDOS 实验室开设的面向MIT本科生的操作系统课程。 +[Lab cow: Copy-on-write fork](https://pdos.csail.mit.edu/6.828/2022/labs/cow.html) -#### 任务 +[Lab fs: File system](https://pdos.csail.mit.edu/6.828/2022/labs/fs.html):完成了bigfile -参考: -- https://csdiy.wiki/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/MIT6.S081/ -- https://pdos.csail.mit.edu/6.828/2021/schedule.html +## 说明 +### 测试项目 +```lang +git clone https://github.com/Mico-rstar/01-lab-hackthon.git +``` -> [!NOTE] -> 要求:完成两个及以上的 Lab 题目 +### 目录结构 +```plaintext +docs +| +|-----lab-fs.md +| +|-----lab-cow.md +| +|-----lab-traps.md -题目见绿色 Assignment 字体后的链接 -![题目见绿色 Assignment 字体后的链接](https://github.com/user-attachments/assets/c348efcb-1c71-4472-bf03-e487aee96ce1) +README.md +``` +### 分支 +```C +// lab:traps +git checkout traps +// test +bttest +alarmtest -### 赛题二:DragonOS 系统调用 & 修复 +// lab:cow +git checkout cow +// test +cowtest -#### 背景 -DragonOS 是使用 Rust 编写的,以 Linux 架构作为参考的操作系统。 - -Repo: https://github.com/DragonOS-Community/DragonOS - -当前 DragonOS 内,有不少系统调用,由于各种原因 ( C 与 Rust 语言差异等 ) ,导致调用时常不符合语义/行为不一致。 - -#### 任务 -- 参考 https://bbs.dragonos.org.cn/t/topic/460 (以及最新引入的系统调用注册表, - 参考 https://github.com/DragonOS-Community/DragonOS/pull/1164 )在 DragonOS - 内添加一个新的系统调用,并在用户态成功调用 -- 寻找内核中行为与预期有出入的系统调用,建议寻找静态、接口不一致的错误 - -#### 参考 -一个内核中静态分析得出行为不一致的 Shutdown 错误: - -https://github.com/DragonOS-Community/DragonOS/issues/887 - -## 网络方向 - -### 赛题三:CS144: Introduction to Computer Networking - -#### 背景 -CS144 通过提前为网卡硬件、TCP 等组件定义抽象,提供了一套精巧设计的框架,为同学们理解网络模型提供了恰到好处的“题目”, -即,通过框架可以一窥整个计算机网络实现的意图与设计,又得以从重复的工作中抽离出来,将时间投入到理解网络模型与底层原理 -当中来。 - -#### 任务 -https://csdiy.wiki/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/CS144/ - -努力完成一个 TCP 协议栈吧! -- 实现至 checkpoint 3 及以上 - -### 赛题四:DragonOS Socket Implementation -#### 背景 -DragonOS 网络子系统中,Inet 协议簇依赖于 [smoltcp] 库实现。 -在Repo https://github.com/Samuka007/dragonos-berkeley-socket 中将网络子系统相关 -的框架实现抽离了出来,基于 tap 设备与 Linux Epoll 机制模拟中断,允许在用户空间测试 -[`Inet 协议簇`](https://github.com/Samuka007/dragonos-berkeley-socket/tree/master/src/socket/inet) -的功能。 - -#### 任务 -目前实现的协议栈涉及TCP多层状态机的转换,由于 Rust 的借用机制,以及 [smoltcp] 的 Socket 接口不完全与 -Berkeley 定义相一致,因此实现上会出现一些冗余的情况,让各层状态机之间的转换变得复杂。 - -本实验的任务是尝试基于已有的网卡抽象,参考现有的 Inet 协议簇 实现,基于 [smoltcp] 自己实现 TCP Socket, -并尝试在现有的 TCP 实现上予以优化。 - -## 其他方向 -自选实验,需要体现技术栈深度 - - -[smoltcp]: https://crates.io/crates/smoltcp +// lab: fs +git checkout fs +// test +bigfile +``` diff --git a/docs/assets/image-1.png b/docs/assets/image-1.png new file mode 100644 index 0000000..3925b2f Binary files /dev/null and b/docs/assets/image-1.png differ diff --git a/docs/assets/image-2.png b/docs/assets/image-2.png new file mode 100644 index 0000000..c866fb8 Binary files /dev/null and b/docs/assets/image-2.png differ diff --git a/docs/assets/image-3.png b/docs/assets/image-3.png new file mode 100644 index 0000000..2f6c909 Binary files /dev/null and b/docs/assets/image-3.png differ diff --git a/docs/assets/image-4.png b/docs/assets/image-4.png new file mode 100644 index 0000000..a084e85 Binary files /dev/null and b/docs/assets/image-4.png differ diff --git a/docs/assets/image-5.png b/docs/assets/image-5.png new file mode 100644 index 0000000..e160f00 Binary files /dev/null and b/docs/assets/image-5.png differ diff --git a/docs/assets/image-6.png b/docs/assets/image-6.png new file mode 100644 index 0000000..2da7d74 Binary files /dev/null and b/docs/assets/image-6.png differ diff --git a/docs/assets/image-7.png b/docs/assets/image-7.png new file mode 100644 index 0000000..eadebc6 Binary files /dev/null and b/docs/assets/image-7.png differ diff --git a/docs/assets/image-8.png b/docs/assets/image-8.png new file mode 100644 index 0000000..3c12e4c Binary files /dev/null and b/docs/assets/image-8.png differ diff --git a/docs/assets/image-9.png b/docs/assets/image-9.png new file mode 100644 index 0000000..d545e61 Binary files /dev/null and b/docs/assets/image-9.png differ diff --git a/docs/assets/image.png b/docs/assets/image.png new file mode 100644 index 0000000..4e4b4a9 Binary files /dev/null and b/docs/assets/image.png differ diff --git a/docs/lab-cow.md b/docs/lab-cow.md new file mode 100644 index 0000000..7872236 --- /dev/null +++ b/docs/lab-cow.md @@ -0,0 +1,133 @@ +## 题目 +https://pdos.csail.mit.edu/6.828/2022/labs/cow.html + + +## 思路 +![alt text](./assets/image-5.png) +1. fork时,直接将子进程页表映射到父进程的物理地址,将父子进程的写权限禁用,标记为cow页 +2. 进程写时,触发page fault后trap到内核,在usertrap中处理: + - 如果是cow页,申请新物理页,将内容拷贝到新物理页,修改页表,标记为非cow页,断掉旧的映射(unmap) + - 如果不是cow页,panic +3. 修改kfree,只有引用计数为1时才释放内存 +4. 修改kalloc,初始化引用计数=1 +5. 修改uvmcopy,引用计数++ + + +## 代码 +```C +// 将原来copy的逻辑注释掉,直接将子进程页表映射到父进程的物理地址,将父子进程的写权限禁用,标记为cow页 +int +uvmcopy(pagetable_t old, pagetable_t new, uint64 sz) +{ + pte_t *pte; + uint64 pa, i; + uint flags; + // char *mem; + + for(i = 0; i < sz; i += PGSIZE){ + if((pte = walk(old, i, 0)) == 0) + panic("uvmcopy: pte should exist"); + if((*pte & PTE_V) == 0) + panic("uvmcopy: page not present"); + + // disable the writing permission if it had, + // make the COW bit 1 if it could write + *pte = PTE_ONCOW(*pte); + pa = PTE2PA(*pte); + flags = PTE_FLAGS(*pte); + // if((mem = kalloc()) == 0) + // goto err; + // memmove(mem, (char*)pa, PGSIZE); + if(mappages(new, i, PGSIZE, pa, flags) != 0){ + //kfree(mem); + goto err; + } + incr(pa); + } + + return 0; + + err: + uvmunmap(new, 0, i / PGSIZE, 1); + return -1; +} +``` + +在trap处理入口,处理page fault +![alt text](./assets/image-6.png) +```c +// trap.c +else if(r_scause() == 12 || r_scause() == 15 ) + { + // page fault + uint64 va = r_stval(); + pte_t *pte; + if(va >= MAXVA) goto err; + if((pte = walk(p->pagetable, va, 0)) == 0) + { + printf("usertrap: walk\n"); + setkilled(p); + } + if((*pte) & PTE_COW) + { + //printf("before cow: pte = %p\n", *pte); + if(uvmcow(p->pagetable, va) < 0) + { + panic("usertrap: uvmcow"); + } + } else{ + printf("usertrap: page fault but not cow page\n"); + printf("name = %s\n", p->name); + printf("pte = %p\n", *pte); + printf("usertrap(): unexpected scause %p pid=%d\n", r_scause(), p->pid); + printf(" sepc=%p stval=%p\n", r_sepc(), r_stval()); + setkilled(p); + } + + } +``` +对于cow页,我们调用uvmcow方法,将cow页映射到新的物理页,并修改pte +```C +// this method can only use when (*pte & PTE_COW) != 0 +// unmap the old mapping +// alloc a new writable page for va, +// and map the new page to pgtbl +// return -1 while it is not a cow pte +// return -0 while succeed +int +uvmcow(pagetable_t pgtbl, uint64 va) +{ + va = PGROUNDDOWN(va); + pte_t *pte; + if((pte = walk(pgtbl, va, 0)) == 0) + panic("uvmunmap: walk"); + if((*pte) & PTE_COW) + { + uint64 pa = PTE2PA(*pte); + uint64 flags = PTE_FLAGS(*pte); + flags &= (~PTE_COW); + flags |= (PTE_W); + // alloc a new page + char *mem; + if((mem = kalloc()) == 0) + panic("uvmcow: not enough page"); + memmove(mem, (char*)pa, PGSIZE); + + uvmunmap(pgtbl, va, 1, 1); + + mappages(pgtbl, va, 1,(uint64) mem, flags); + } else + { + return -1; + } + return 0; +} +``` + + + + +## 测试结果 +![alt text](./assets/image-2.png) + + diff --git a/docs/lab-fs.md b/docs/lab-fs.md new file mode 100644 index 0000000..d0936a1 --- /dev/null +++ b/docs/lab-fs.md @@ -0,0 +1,201 @@ +## 题目 + +https://pdos.csail.mit.edu/6.828/2022/labs/fs.html + +## 思路 + +![alt text](./assets/image.png) + +## 代码 +**主要要修改bmap和itrunc函数** + +bmap原来只能处理直接映射和一级间接映射 +```c +bmap(struct inode *ip, uint bn) +{ + uint addr, *a; + struct buf *bp; + + if (bn < NDIRECT) + { + if ((addr = ip->addrs[bn]) == 0) + { + addr = balloc(ip->dev); + if (addr == 0) + return 0; + ip->addrs[bn] = addr; + } + return addr; + } + bn -= NDIRECT; + + // single level indirect block + if (bn < NINDIRECT) + { + // Load indirect block, allocating if necessary. + if ((addr = ip->addrs[NDIRECT]) == 0) + { + addr = balloc(ip->dev); + if (addr == 0) + return 0; + ip->addrs[NDIRECT] = addr; + } + bp = bread(ip->dev, addr); + a = (uint *)bp->data; + if ((addr = a[bn]) == 0) + { + addr = balloc(ip->dev); + if (addr) + { + a[bn] = addr; + log_write(bp); + } + } + brelse(bp); + return addr; + } + + bn -= NINDIRECT; + // double level indirect block + + panic("bmap: out of range"); +} +``` +我们写一个递归函数来处理一二级映射,也能很方便拓展成三级映射 +```c +// Return the disk block address of the nth block in inode ip. +static uint +bwalk(struct inode *ip, uint *cur, uint off, ushort level, uint clevel) +{ + uint addr; + // Load indirect block + if(*cur==0) + *cur = balloc(ip->dev); + if(*cur==0) return 0; + struct buf *bp = bread(ip->dev, *cur); + uint *a = (uint *)bp->data; + uint toff = off / power(NINDIRECT, level - clevel); + if ((addr = a[toff]) == 0) + { + + addr = balloc(ip->dev); + + if (addr) + { + a[toff] = addr; + log_write(bp); + } + } + brelse(bp); + if (clevel == level) + { + return addr; + } + else + { + if (a[toff]) + return bwalk(ip, &a[toff], off - toff * power(NINDIRECT, level - clevel), level, clevel + 1); + else + return 0; + } +} +``` + +bmap可以修改为 +```C +static uint +bmap(struct inode *ip, uint bn) +{ + uint addr; + if (bn < NDIRECT) + { + if ((addr = ip->addrs[bn]) == 0) + { + addr = balloc(ip->dev); + if (addr == 0) + return 0; + ip->addrs[bn] = addr; + } + return addr; + } + bn -= NDIRECT; + + // single level indirect block + if (bn < NINDIRECT) + { + return bwalk(ip, &(ip->addrs[NDIRECT]), bn, 1, 1); + } + bn -= NINDIRECT; + // double level indirect block + if (bn < NINDIRECT * NINDIRECT) + { + return bwalk(ip, &(ip->addrs[NDIRECT + 1]), bn, 2, 1); + } + + panic("bmap: out of range"); +} +``` + +itrunc的修改是类似的 +```C +void bwalkfree(struct inode *ip, uint *cur, uint level, uint clevel) +{ + uint j; + struct buf *bp = bread(ip->dev, *cur); + uint *a = (uint *)bp->data; + for (j = 0; j < NINDIRECT; j++) + { + if (level != clevel) + { + if (a[j]) + bwalkfree(ip, &a[j], level, clevel + 1); + } + else + { + if (a[j]) + bfree(ip->dev, a[j]); + + } + } + brelse(bp); + bfree(ip->dev, *cur); + *cur = 0; +} + +// Truncate inode (discard contents). +// Caller must hold ip->lock. +void itrunc(struct inode *ip) +{ + int i; + + for (i = 0; i < NDIRECT; i++) + { + if (ip->addrs[i]) + { + if (ip->addrs[i] < 30) + panic("itrunc: error\n"); + bfree(ip->dev, ip->addrs[i]); + ip->addrs[i] = 0; + } + } + + if (ip->addrs[NDIRECT]) + { + bwalkfree(ip, &(ip->addrs[NDIRECT]), 1, 1); + } + + if (ip->addrs[NDIRECT + 1]) + { + bwalkfree(ip, &(ip->addrs[NDIRECT + 1]), 2, 1); + } + + ip->size = 0; + iupdate(ip); +} +``` + + +## 测试结果 +![alt text](./assets/image-1.png) + + diff --git a/docs/lab-traps.md b/docs/lab-traps.md new file mode 100644 index 0000000..00a7141 --- /dev/null +++ b/docs/lab-traps.md @@ -0,0 +1,109 @@ +## 题目 +https://pdos.csail.mit.edu/6.828/2022/labs/traps.html + + +## 思路 +### backtrace +![alt text](./assets/image-7.png) +s0寄存器保存了当前栈帧的fp,只需要格局栈帧排布图来计算ra和pre_fp相对于fp的偏移量,迭代到页表边界,即可把函数调用链输出。需要注意栈顶在低地址,栈底在高地址。 + +### alarm +关键是理解陷入过程和回到用户空间的过程 +![alt text](./assets/image-9.png) + +## 代码 +### backtrace +```C +// trace back stacks while panic +void +backtrace(void) +{ + uint64 pre_fp = r_fp(); + uint64 start = PGROUNDDOWN(pre_fp); + uint64 end = PGROUNDUP(pre_fp); + uint64 ra; + printf("start = %p, end = %p\n",start, end); + printf("fp = %p\n", pre_fp); + printf("backtrace: \n"); + while(pre_fp != end) + { + ra = *((uint64*)(pre_fp-8)); + printf("%p\n",ra); + pre_fp=*((uint64*)(pre_fp-16)); + + } + +} +``` + +### alarm +修改proc数据结构 +```C + // 在proc结构体中增加 + int ainterval; // alarm interval + void (*handler)(); // handler function + int nticks; // number of ticks + struct trapframe tickframe; // store the trapframe when ticking + int tickstatus; // status = 0 imply not in handle fn, status = 1 imply handling + +``` +需要在定时中断中增加计数,直到触发internel +如果触发中断,将trapframe存到tickframe,修改trapframe中的epc, +使得回到用户空间后可以从回调函数开始执行 +```C + // give up the CPU if this is a timer interrupt. + if(which_dev == 2){ + struct proc *p = myproc(); + p->nticks++; + if(p->ainterval != 0 && p->ainterval == p->nticks && p->tickstatus == 0) + { + // set the tickstatus = 1, imply handling + p->tickstatus = 1; + + // store the trapframe in tickframe + p->tickframe = *(p->trapframe); + + // alloc a new proc for the handle fn + p->trapframe->epc = (uint64)p->handler; + p->nticks = 0; + usertrapret(); + } + yield(); + } +``` +实现定时中断系统调用 +```C +// 将中断间隔,回调函数存到proc结构体中 +uint64 +sys_sigalarm(void) +{ + struct proc *p = myproc(); + argint(0, &(p->ainterval)); + argaddr(1, (uint64*)&(p->handler)); + p->nticks=0; + // printf("interval ticks = %d\n", p->ainterval); + // printf("handler addr = %p\n", p->handler); + return 0; +} + +// 在用户程序的回调函数最后调用,用来向kernel发出恢复原始上下文的请求 +uint64 +sys_sigreturn(void) +{ + // store the value in a0 + //uint64 a0; + struct proc *p = myproc(); + // recover the context + *(p->trapframe) = p->tickframe; + + // set the tickstatus = 0, imply not in handle fn + p->tickstatus = 0; + return p->tickframe.a0; +} +``` + + + +## 测试结果 +![alt text](./assets/image-4.png) +![alt text](./assets/image-3.png) \ No newline at end of file