首页
关于
Search
1
2023 年保研经验贴 [计算机-末九Rank50%-入北计清深-北计直博]
324 阅读
2
Linux Kernel-THP阅读分析[Hang up]
223 阅读
3
FreeBSD Kernel-编译环境搭建
198 阅读
4
Linux Kernel-编译调试环境搭建
151 阅读
5
CSAPP 1. 计算机系统漫游
139 阅读
内核
源码分析
阅读笔记
Rust
语法
习题
读书笔记
深入理解计算机系统
技术之外
开发
rcore-arm
登录
Search
yyi
累计撰写
46
篇文章
累计收到
0
条评论
首页
栏目
内核
源码分析
阅读笔记
Rust
语法
习题
读书笔记
深入理解计算机系统
技术之外
开发
rcore-arm
页面
关于
搜索到
4
篇与
的结果
2024-03-11
Zswap4BSD接入 BSD Swap
这一步是比较艰巨的,上一篇文章里虽然看上去都是fanyi的锅,实际上问题是我都是处理一些迁移好的依赖(by linuxkpi),而fanyi需要弄一个没有的依赖(没有那么好接,需要读一下源码)到了我们这,Linux和FreeBSD的swap区别还是蛮大,我将会首先梳理Linux中是如何在swap之前先调用frontswap的,再梳理FreeBSD的swap到底是怎么运转的,最后再选择一种合理的方式(尽量照抄Linux)把frontswap接入到 BSD 中。Frontswap的一探究竟Frontswap作为一个中间层,为优化方式提供了一种接口,使得页面在真正换出到swap设备之前可以先进入frontswap,如果frontswap成功,则不必再换出。从下层接口看起struct frontswap_ops { void (*init)(unsigned); /* this swap type was just swapon'ed */ int (*store)(unsigned, pgoff_t, struct page *); /* store a page */ int (*load)(unsigned, pgoff_t, struct page *); /* load a page */ void (*invalidate_page)(unsigned, pgoff_t); /* page no longer needed */ void (*invalidate_area)(unsigned); /* swap type just swapoff'ed */ };这里没加参数名字,但是那个unsigned都是type, pgoff_t 都是offset就好了。现在frontswap只有一个driver,即zswap,所以我们都会结合zswap来说。在我理解,一个type对应的是一个swap设备,或者说,我们每次做swapon系统调用的时候,那个对应的文件就是一个设备,当成一个逻辑设备就好了(毕竟万物皆文件嘛)。在这种基础上,我们就很好理解了,frontswap要求driver可以区分不同的设备,根据不同的设备初始化、插入、加载和删除页面。以及可以失效一整个设备的所有页面。上层接口extern void frontswap_init(unsigned type, unsigned long *map); extern int __frontswap_store(struct page *page); extern int __frontswap_load(struct page *page); extern void __frontswap_invalidate_page(unsigned, pgoff_t); extern void __frontswap_invalidate_area(unsigned);相对应的,frontswap也为上层提供了一系列接口,其中参数大差不差,store和load会提取page中的private字段,最后拆成type和offset,再塞给下层,这可能是我们要在过程中关注的一点,一个页面在什么时候会获得这个private,它是怎么跟pid和虚拟内存的offset对应上的?我们在FreeBSD里如何把swap的vm_object和offset 跟这个private对应起来?我们好像没有那么多地方能存放数据。总不能把zswap改成二重索引的树套树。除此之外,frontswap还在对应swap设备的控制结构swap_info_struct中留下了一个unsigned long *类型的参数map,用于中间层校验当前的page是否在frontswap中存在,及相应的是否有效上层接口到下层接口1. Driver的注册在做zswap的时候我们已经很明显的看到了,frontswap用register函数注册相关的控制结构int frontswap_register_ops(const struct frontswap_ops *ops) { if (frontswap_ops) return -EINVAL; frontswap_ops = ops; static_branch_inc(&frontswap_enabled_key); return 0; }注册的过程伴随着swap设备的swapon,但是存在一种情况即backend driver还没准备好,我们就要往里写了,所以frontswap的每个操作都会在操作前首先检测注册情况2. initvoid frontswap_init(unsigned type, unsigned long *map) { struct swap_info_struct *sis = swap_info[type]; VM_BUG_ON(sis == NULL); if (WARN_ON(!map)) return; frontswap_map_set(sis, map); if (!frontswap_enabled()) return; frontswap_ops->init(type); }这个map是个bitmap,用来判断一个页面是不是在frontswap里,初始化的时候,要新建一个map赋给这个设备的swap_info3. test & set & clear听起来就像bit操作,实际上也确实是,这两个函数根据给定的swap_info和offset,把map的第offset位返回/置1/清除4. store & load终于到了比较核心的地方int __frontswap_store(struct page *page) { int ret = -1; swp_entry_t entry = { .val = page_private(page), }; int type = swp_type(entry); struct swap_info_struct *sis = swap_info[type]; pgoff_t offset = swp_offset(entry); if (__frontswap_test(sis, offset)) { __frontswap_clear(sis, offset); frontswap_ops->invalidate_page(type, offset); } ret = frontswap_ops->store(type, offset, page); if (ret == 0) { __frontswap_set(sis, offset); inc_frontswap_succ_stores(); } else { inc_frontswap_failed_stores(); } return ret; } 就和我们之前说的一样,首先从private中拆出来private字段,拆成type和offset,验证当前页面是否在frontswap中已经出现了,如果出现,首先干掉,然后再把页面塞进去int __frontswap_load(struct page *page) { int ret = -1; swp_entry_t entry = { .val = page_private(page), }; int type = swp_type(entry); struct swap_info_struct *sis = swap_info[type]; pgoff_t offset = swp_offset(entry); if (!__frontswap_test(sis, offset)) return -1; ret = frontswap_ops->load(type, offset, page); if (ret == 0) inc_frontswap_loads(); return ret; }load也差不多,这边如果检测到不在,那就直接返回失败,否则load一下。至于后面的两个invalid,我们等到他们被调用的时候再看好了。那它在哪被调用呢?别急,我们先去看看Linux的Swap吧Linux Swap的浅述swap应该是大家都听过的概念,废话不多说,不管是linux还是unix,只有匿名页面在需要内存回收的时候,需要放在swap分区中,以便重新访问的时候调入内存,不然只能OOM Kill了,而以文件做backend的页面可能是不需要换出的(可读写页面可能会分情况,比如出现了CoW之后再设置为可以换出的页面)什么时候发生swap?周期性检查,保证内存有充足空间:mm/vmscan.c - kswapd()直接内存回收,当内存不够用的时候: mm/page_alloc.c - __alloc_pages_slowpath我们关心的第一个问题:swp_entry_t 从哪里来,又到哪里去来看一张图swp_entry就是一个long,和一个PTE等长,正好可以放进一个页表作为一个页表项,当swap out的时候,linux在回收物理页面之前先修改这个PTE,这样下次访问到这个PTE的时候就会触发一个Page Fault,再通过这个swp_entry_t找到原来这个页面在swap中的位置,进而换出,再把这个表项修改成真的PTE。PTE的如下三种情况全为0:该页不属于进程的地址空间,或相应的页框尚未分配给进程。最低位为0,其余位不全为0:该页现在在swap分区中。最低位为1:present位为1,表示该页在内存中。直接转换。至于FreeBSD怎么办,我们一会再想,反正现在是知道这个entry是咋来的了swap是怎么调用frontswap的?失败了怎么办?什么时候initSYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags) { // ... if (IS_ENABLED(CONFIG_FRONTSWAP)) frontswap_map = kvcalloc(BITS_TO_LONGS(maxpages), sizeof(long), GFP_KERNEL); // ... enable_swap_info(p, prio, swap_map, cluster_info, frontswap_map); // ... } static void enable_swap_info(struct swap_info_struct *p, int prio, unsigned char *swap_map, struct swap_cluster_info *cluster_info, unsigned long *frontswap_map) { if (IS_ENABLED(CONFIG_FRONTSWAP)) frontswap_init(p->type, frontswap_map); }swapon的时候,这个调用链路实在太明确啦,就不说了,因此我们大概也是要找到FreeBSD对应的Swapon,幸运的是,这个应该不是很难。什么时候Store肯定是先Store再load啦,我们先看Store好了。在mm/page_io.c下int swap_writepage(struct page *page, struct writeback_control *wbc) { // some other code if (frontswap_store(&folio->page) == 0) { folio_start_writeback(folio); folio_unlock(folio); folio_end_writeback(folio); return 0; } __swap_writepage(&folio->page, wbc); return 0; }如果走frontswap成功,就不走后面的write_page了,调用start_writeback,然后结束流程。然而这块很显然出现了一个问题,folio_xx_writeback是用来干啥的呢?按理说,我们是在swap之前就把该unmap的已经unmap掉了,最核心的写入部分也搞完了,应该只能有点收尾工作,不能有writeback啊?这个问题稍后再看,我们把frontswap看完什么时候load读取的部分是+号位有点长void swap_readpage(struct page *page, bool synchronous, struct swap_iocb **plug) { struct swap_info_struct *sis = page_swap_info(page); bool workingset = PageWorkingset(page); unsigned long pflags; bool in_thrashing; VM_BUG_ON_PAGE(!PageSwapCache(page) && !synchronous, page); VM_BUG_ON_PAGE(!PageLocked(page), page); VM_BUG_ON_PAGE(PageUptodate(page), page); /* * Count submission time as memory stall and delay. When the device * is congested, or the submitting cgroup IO-throttled, submission * can be a significant part of overall IO time. */ if (workingset) { delayacct_thrashing_start(&in_thrashing); psi_memstall_enter(&pflags); } delayacct_swapin_start(); if (frontswap_load(page) == 0) { SetPageUptodate(page); unlock_page(page); } else if (data_race(sis->flags & SWP_FS_OPS)) { swap_readpage_fs(page, plug); } else if (synchronous || (sis->flags & SWP_SYNCHRONOUS_IO)) { swap_readpage_bdev_sync(page, sis); } else { swap_readpage_bdev_async(page, sis); } if (workingset) { delayacct_thrashing_end(&in_thrashing); psi_memstall_leave(&pflags); } delayacct_swapin_end(); }别看这里多了几个if else,其实store只是把这几个if-else包了一层,如果失败自动回归原流程,好像也没啥需要特别注意的。至于range_free(invalidate)什么的,我们倒是不着急,先把核心做出来吧.
2024年03月11日
25 阅读
0 评论
0 点赞
2024-03-11
zswap4BSD 功能测试
zswap4BSD 功能测试概述在已经迁移 zswap 至 FreeBSD之后,设计一类测试方法,梯度的完成如下两个目标测试zswap的基础功能,包括zswap_initzpool_initcrypto_initzbud_registerzswap_frontswap_storezswap_frontswap_loadzswap_exit测试zswap的性能测试单位时间内swap可以换出、换入的页面数量,对比zswap换入、换出的页面数量,计算其加速比比较Linux启用zswap时的加速比,和FreeBSD启用zswap时的加速比测试基础功能的同时也兼有测试驱动开发的目的,完成整个zswap的开发前置内容由于是交叉编译,时刻注意相关环境的问题。编译命令:export BASEDIR=$(pwd) export MAKEOBJDIRPREFIX=$BASEDIR/obj cd $BASEDIR/src ./tools/build/make.py --clean --cross-bindir=/usr/lib/llvm-14/bin/ TARGET=amd64 TARGET_ARCH=amd64 -n ./tools/build/make.py --clean --cross-bindir=/usr/lib/llvm-14/bin/ TARGET=amd64 TARGET_ARCH=amd64 kernel-toolchain -s -j32 -DWITH_DISK_IMAGE_TOOLS_BOOTSTRAP ./tools/build/make.py --clean --cross-bindir=/usr/lib/llvm-14/bin/ TARGET=amd64 TARGET_ARCH=amd64 KERNCONF=GENERIC NO_MODULES=yes buildkernel -s -j32基础功能测试基础功能测试采用syscall为入口,分别为init、store、load、exit提供根据调用参数而来的分入口,后续可能支持触发更多事件用于benchmark注册系统调用accord:AddingSyscalls - FreeBSD WikiSyscall在sys/kern/syscalls.master处注册,在这个位置我们定义了它在用户空间所能展现的样子,先以一个int参数为例583 AUE_ZSWAP_INTERFACE STD { int zswap_interface( int cmd ); }之后通过make,生成sys/kern和sys/sys中的代码make sysent在这遇到了问题,我目测后续如果坚持在Linux下交叉编译会遇到更多的问题,考虑部署一个基于VMWare的FreeBSD工作环境在lib/libc/sys/Symbol.map中添加符号在sys/compact/freebsd32/syscalls.master添加32位兼容在我们的zswap.c中进行实现编译内核Chapter 9. Building and Installing a FreeBSD Kernel 好吧这个有点简略,不是我想要的看眼现在的调下性能配置,之前2C1G太磕碜了,先来16C 8Gmake buildkernel KERNCONF=GENERIC -s -j32我们定义的这个Identifier没有看看别的标志符在哪定义的看上去少了个audit的步骤,我还以为不关心安全可以不用管。。AuditingSecurity event auditing allows fine-grained logging of security-relevant events in the operating system, and is described by the CAPP common criteria protection profile. Detailed information on configuring audit can be found in the FreeBSD Handbook http://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/audit.html, and an implementation paper can be found on the TrustedBSD website http://www.TrustedBSD.org/.Most system calls are considered security-relevant, as they frequently involve access control checks, communication between processes, or system configuration, and therefore almost all system calls will be audited. You can find more detailed information on the AddingAuditEvents page. Please do not forget to add appropriate auditing to your new system call, be it a native call or a compat one!Please e-mail the trustedbsd-audit mailing list, or RobertWatson, if you require help. The audit event identifier space is managed by the OpenBSM project, and you will need to request assignment of a new identifier if an existing one isn't a good match.AddingAuditEvents - FreeBSD Wiki由于我们是测试,直接define为AUE_NULL好了测试编译好之后替换make install reboot#include <stdio.h> #include <unistd.h> #include <sys/syscall.h> // 定义操作码 #define OP_INIT 0 #define OP_SWAP_STORE 1 #define OP_SWAP_LOAD 2 #define OP_EXIT 3 // 定义zswap_interface系统调用的编号,根据你的系统实际情况替换583 #define SYS_zswap_interface 583 int main() { int result; // 调用OP_INIT printf("Calling OP_INIT...\n"); result = syscall(SYS_zswap_interface, OP_INIT); if (result == 0) { printf("OP_INIT executed successfully.\n"); } else { perror("syscall OP_INIT failed"); } // 调用OP_SWAP_STORE (需要更多逻辑来处理存储操作) // 注意: 由于OP_SWAP_STORE和OP_SWAP_LOAD没有实现具体逻辑,这里的调用仅作为示例 printf("Calling OP_SWAP_STORE...\n"); result = syscall(SYS_zswap_interface, OP_SWAP_STORE); if (result == 0) { printf("OP_SWAP_STORE executed successfully.\n"); } else { perror("syscall OP_SWAP_STORE failed"); } // 调用OP_SWAP_LOAD (需要更多逻辑来处理加载操作) printf("Calling OP_SWAP_LOAD...\n"); result = syscall(SYS_zswap_interface, OP_SWAP_LOAD); if (result == 0) { printf("OP_SWAP_LOAD executed successfully.\n"); } else { perror("syscall OP_SWAP_LOAD failed"); } // 调用OP_EXIT printf("Calling OP_EXIT...\n"); result = syscall(SYS_zswap_interface, OP_EXIT); if (result == 0) { printf("OP_EXIT executed successfully.\n"); } else { perror("syscall OP_EXIT failed"); } return 0; }最开始的panic,竟然只是我起了个字符串叫panic,我真的晕,因为这个调了好一会。搞了个小脚本,commit完了就可以直接直接替换了,大概几十秒的量级#!/bin/sh cd /work/zswap4BSD sudo git pull sudo make buildkernel -j16 NO_CLEAN=YES && \ sudo make installkernel && \ sudo reboot在这个位置卡住,目测是访问acomp_ctx出问题了访问了一个0x440,导致kernel里 page fault了,这鬼东西显然不可能是一个内核的的地址稍作改造,之前cpuhp相关的callback都是注册为0的,我们知道我们不会涉及到cpuhp,那么直接把唯一的node传进去初始化就好了,再来一次。现在每次调用必FATAL。。。都得等Reboot这块是store的第一个核心点,是压缩,fanyi做的。感觉要变成我自己的了。这个地方其实应该不难的,我们初始化输入输出的scatterlist(其实接的是uio),我感觉是这块有点小问题整理一下这块实际上发生了啥吧// 入参:u8* dst (acomp_ctx->dstmem); // 入参:vm_page* page; // 入参:uio input, output // 1 : sg_init_table() // did nothing sg_set_page(input, page, PAGE_SIZE, 0) { struct iovec iov[1]; iov[0].iov_base = (void *)PHYS_TO_DMAP(VM_PAGE_TO_PHYS(page)); iov[0].iov_len = len; uio->uio_iov = iov; uio->uio_iovcnt = 1; uio->uio_offset = 0; uio->uio_resid = len; uio->uio_segflg = UIO_SYSSPACE; } sg_init_one(output, dst, PAGE_SIZE * 2) { struct iovec iov[1]; iov[0].iov_base = (void *)buf; iov[0].iov_len = buflen; uio->uio_iov = iov; uio->uio_iovcnt = 1; uio->uio_offset = 0; uio->uio_resid = buflen; uio->uio_segflg = UIO_SYSSPACE; // uio_rw暂时无法设置 return; } acomp_request_set_params(struct acomp_req *req, struct uio *input, struct uio *output, unsigned int slen, unsigned int dlen) { // 设置uio_rw input->uio_rw = UIO_READ; output->uio_rw = UIO_WRITE; // 设置cryptop参数 struct cryptop *crp = kzalloc(sizeof(struct cryptop), GFP_KERNEL); // linuxkpi crypto_initreq(crp, req->sid); crp->crp_flags = CRYPTO_F_CBIFSYNC | CRYPTO_F_CBIMM; // 存疑 crp->crp_callback = crypto_callback; crypto_use_uio(crp, input); // 使用input输入 crypto_use_output_uio(crp, output); // 使用output输出 crp->crp_payload_start = 0; crp->crp_payload_length = max(slen, dlen); req->crp = crp; return; } 在这个req传完之后,就调用crypto_acomp_compress(struct acomp_req *req) { req->crp->crp_op = CRYPTO_OP_COMPRESS; int err = crypto_dispatch(req->crp); crypto_destroyreq(req->crp); return err; }这块应该是最核心的调用部分了,我们应该是没做异步,所以外面的wait_req是个假的函数,如果这个压缩的过程没啥问题,应该就是没啥问题。这个dispatch没啥问题,那似乎最关键的压缩环节应该是压缩完了的,可能是destroy出问题了?真奇怪,竟然是访问dlen的时候出问题了找到问题了,感觉是destroy的东西有点多啊before的address还是一组kernel的地址,出来之后不是kernel地址了,肯定要报错的,问题是destroy的是req的crp啊,为什么会影响到req本身呢请教师兄之后,猜测是有地方溢出了,导致栈上有问题,把req错误覆盖了。那就是捋一遍dispatch能用到的地方,好家伙,我就觉得这个地方很怪此处应有甩锅,fanyi写错了哈哈稍作修改,顺便防止内存泄漏。终于把每次都FATAL的问题解决了然后就是新的问题,压缩无效,4096大小的数据压缩之后竟然要4101?Exm?综合我的信息论经验,是我的数据太随机了。。。降低一下数据随机度,果然能压缩了,大概压缩大小在1200上下,压缩率30%左右,至少做验证是可以了。用同样的页面测试一下Load又开始Panic了,排查下dispatch失败了,又是general protection已经有点经验了,猜测是哪个指针申请的不对,log出在dispatch中导致的,还是crp的问题原来是异步的opaque忘记传了,小问题然后出现了一次死循环,但是这次没log,但是很快就排查到了,因为就一个地方会有死循环,就是callback失败了最开始用olen当做是否完成的标志,但是如果etype != 0 (出错的时候),这个东西还是会返回一个0,特判一下,如果Error传个负数回去。最开始丈二和尚摸不到头脑,定义Etype的地方并没有说过22是啥,但是根据我的Kernel经验,22应该是EINVAL,又是crp的参数有问题,但是这次真排查不到了,进别的函数去排查吧最后定位在opencrypto/cryptosoft.c里发现这个地方怎么是4096,我Decomp的时候长度应该是一千多的那个数才对好了,fanyi成功获得第二个锅这个payload_length就应该是输入的src_len,不能取max,这个对了之后,谢天谢地,功能测试到此结束
2024年03月11日
28 阅读
0 评论
0 点赞
2024-02-24
rCore-Tutorial #1 应用程序与基本执行环境[WIP]
概述第一章就一个基本目标,说起来很快,实际上难度不小:隔离应用与硬件。更像一堆库函数,为硬件提供一层简单接口,让应用可以通过这层接口访问硬件。阶段一的OS称为LibOS这个系统的bootloader由RustSBI完成,OS负责为APP初始化加载环境,然后就跳转到app执行,app通过函数调用获得OS提供的输出字符串等服务。移除标准库依赖我跳过了第一节,老知识了,需要的同学自行查阅吧。本节目标是构建一个最小环境,使得程序可以打印Hello, world。在之前的编程里,我们都是通过Rust std实现的,我们要扔掉std(std本身也依赖于系统调用),直达硬件。移除println!我们首先准备一个干净的环境,在原来基础上checkout到我们自己的分支git checkout -b my_ch1 rm -rf os # 新建我们的项目 cargo new os # 修改编译target arch cd os && mkdir .cargo echo '# os/.cargo/config [build] target = "riscv64gc-unknown-none-elf"' \ >> .cargo/config此时编译,会出现找不到std的问题当然,预料之中,我们在main的开头加上\#![no_std],告诉Rust不要用Rust标准库。Rust会自动使用不依赖os的core库我们需要提供panic_handler,来处理致命系统错误,按教程提供后,现在应该长这样:移除main中的println,再次编译的时候出现了新的错误(这个错误和教程不太一样)虽然不太一样但是意思是一致的,main本身是依赖标准库的,如果你熟悉实际上的C编程,会有印象大部分的linux ELF会有个.init,然后调用libc_start_main,才进入真正的main函数。main只是对编译器有意义罢了,并不是说我们一定必须进去。所以,我们把main删掉,按编译提示加入no_main注解。此时的程序终于可以通过编译了,不过也有点可怜接下来会二进制分析已经编译出的程序,看看我们应该怎么办入口点是0,0显然不会是一个可执行的代码地址内核的第一条指令这节分为基础篇和实践篇,我们从基础篇的第二节开始,看看Qemu是怎么执行的。(其他知识已经很熟了,我这边先行跳过,同学有需要可以自行查阅)Qemu启动qemu会把bootloader和内核镜像分别加载到0x80000000 (虚拟设备内存起始地址)和0x80200000(我们指定的地址),具体如何boot是由rustsbi帮我们完成的。正常的Boot会有Bios引导,先把bootloader加载到内存,然后控制权转交给bootloader,bootloader对CPU初始化,把OS加载到内存,最后把控制权流转给内核。但是我们这不是个正常的boot,由qemu负责把bootloader和os都加载到内存,再把控制权流转给bootloader,不管怎么样,我们知道RustSBI帮我们初始化了一些事,只要我们能控制0x80200000,我们就能获得CPU控制权了。编写第一条指令 # os/src/entry.asm .section .text.entry .globl _start _start: li x1, 100它只执行了一条简单的指令,即l(oad)i(mm) x1, 100把立即数100赋值给寄存器x1这里还定义了全局符号_start,并声明这段代码应该在.text.entry上,我们把这段汇编嵌入到rust中。并通过它检测qemu是否正确的把控制权流转给了我们的代码当然,做到这还不够,我们希望指令出现在0x80200000,熟悉编译的同学都知道,这个最后在哪是链接器决定的,所以通过修改cargo配置文件,我们使用自定义链接器。 // os/.cargo/config [build] target = "riscv64gc-unknown-none-elf" [target.riscv64gc-unknown-none-elf] rustflags = [ "-Clink-arg=-Tsrc/linker.ld", "-Cforce-frame-pointers=yes" ]// os/src/linker.ld OUTPUT_ARCH(riscv) ENTRY(_start) BASE_ADDRESS = 0x80200000; SECTIONS { . = BASE_ADDRESS; skernel = .; stext = .; .text : { *(.text.entry) *(.text .text.*) } . = ALIGN(4K); etext = .; srodata = .; .rodata : { *(.rodata .rodata.*) *(.srodata .srodata.*) } . = ALIGN(4K); erodata = .; sdata = .; .data : { *(.data .data.*) *(.sdata .sdata.*) } . = ALIGN(4K); edata = .; .bss : { *(.bss.stack) sbss = .; *(.bss .bss.*) *(.sbss .sbss.*) } . = ALIGN(4K); ebss = .; ekernel = .; /DISCARD/ : { *(.eh_frame) } }配置过程文档里都有,我不赘述了这块的意思就是把所有目标文件的.text.entry段、.text段,.text.*段都放到最后链接完毕的文件的.text段中,这里的*统统是通配符,括号外代表目标文件,括号内代表目标文件的段。按从上到下的顺序组织,以BASE_ADDRESS为基准偏移。小功告成!此时我们是不是可以直接扔给qemu了呢?别急还有点早。因为qemu会把整个ELF load到它的物理内存里。我们的代码段之前是存在一些其他的元信息的,通过objcopy直接获得程序真正的部分rust-objcopy --strip-all target/riscv64gc-unknown-none-elf/release/os \ -O binary target/riscv64gc-unknown-none-elf/release/os.bin
2024年02月24日
33 阅读
0 评论
0 点赞
2024-02-23
rCore-Tutorial #0 实验环境配置
当你看到这个标题,是的,我要开个大坑了,而且难度很高,貌似没有什么现成的资料。ipads著名的chcore是基于arm架构的,但是是C开发的,清华著名的rcore是RISCV架构的,但是是Rust开发的。我想搞一个Rust+AArch64的Kernel,显然,我既不怎么会Rust,又不怎么会ARM,我也就只剩一颗想做的心了。当然,如果最后真的搞不下去,我也想至少把rCore-RISCV这个Lab做完做好。我不知道是迁移chcore更难,还是迁移rcore更难,先选一个吧,迁移chcore面对的是Rust的大量特质要重构,迁移rcore面对的是架构差异,从引导、中断到分页都要重构。但是毕竟是给人写的嘛。这个环境配置Chapter我可能还会慢慢的更新,也可能更的不全,有的小依赖可能随手就安了忘记更新,只能说尽量做吧。希望真的能把这个项目做到最后。0 环境配置 参考链接Rust开发环境配置export RUSTUP_DIST_SERVER=https://mirrors.ustc.edu.cn/rust-static export RUSTUP_UPDATE_ROOT=https://mirrors.ustc.edu.cn/rust-static/rustup curl https://sh.rustup.rs -sSf | sh source "$HOME/.cargo/env" > rustc 1.76.0 (07dca489a 2024-02-04)文档中说必须要nightly版本,关于Nightly版本是什么:Rust版本说明我们也切换到nightlyrustup install nightly rustup default nightly这里出现了个小插曲,VM空间不够了,稍稍扩容一下,没想到扩容也没那么简单插曲这里df -h可以看到sda3已经满了fdisk -l在VMWare的图形化界面操作后运行fdisk /dev/sda 并跟着输入n,然后一路回车,最后p一下n是新建分区,p是打印如果正确新建了一个Linux filesystem分区,按w写入分区表,然后reboot下我发现这么搞太麻烦了,反正我有快照,如果直接干掉sda3的分区表,新建分区,能行么,大胆操作一下fdisk里生效了,df里没生效,根据我搞FreeBSD的经验,要growfs一下resize2fs /dev/sda3插曲结束,大功告成。安装完nightly之后,应该有如下输出(版本号不对无所谓)❯ rustc --version rustc 1.78.0-nightly (3406ada96 2024-02-21)crates换源,不赘述安装rust软件包的时候,原文档只给了 riscv64gc-unknown-none-elf 作为交叉编译的target,我们还要加一个aarch64-unknown-nonerustup target add riscv64gc-unknown-none-elf rustup target add aarch64-unknown-none cargo install cargo-binutils # 后面这俩我这时候不能用清华源 rustup component add llvm-tools-preview rustup component add rust-src这俩都是Tier2的Support,应该不会有太大问题 Platform SupportQEMU环境这东西对我来说比较熟悉,可能会简略一点sudo apt-get install autoconf automake autotools-dev curl libmpc-dev libmpfr-dev libgmp-dev \ gawk build-essential bison flex texinfo gperf libtool patchutils bc \ zlib1g-dev libexpat-dev pkg-config libglib2.0-dev libpixman-1-dev libsdl2-dev \ git tmux python3 python3-pip ninja-build别管有没有了安装一把梭其实纠结了一下,Tutoial给定的是qemu 7.0.0 实验,但是本文撰写时qemu已经是这个状态了:搜了一下,看到了这个:我超,可能是要硬着头皮上Qemu8了,一步到位!(虽然我知道这大概率不是一个明智的选择,比起很低概率会用上的新支持,更大概率是会遇到和教程版本不一致导致的问题,但是谁管呢?真的有人不喜欢用新的东西么?)wget https://download.qemu.org/qemu-8.2.1.tar.xz # 可能会有点慢,有条件上proxy吧 tar xJvf qemu-8.2.1.tar.xz 此时要参考一下qemu的官方文档,看看我们选择啥targets,当然不嫌费劲可以直接全部编译可能不用参考了,就选这四个吧./configure --target-list=riscv64-softmmu,riscv64-linux-user,aarch64-linux-user,aarch64-softmmu make -j16在编译目录的./build目录下即可看到编译完的qemu,添加到PATH中跑起来!git clone git@github.com:rcore-os/rCore-Tutorial-v3.git cd os make run
2024年02月23日
52 阅读
0 评论
0 点赞