首页 Home.

  • 自制操作系统(21):rtl8139网卡驱动(下)- 中断处理与ARP支持 我们先接着写驱动来实现一个获取MAC地址的函数。 获取MAC地址 获取MAC地址也是读取网卡驱动的某个寄存器。RTL8139 的 MAC 地址存储在 I/O 端口偏移 0x00 ~ 0x05 处,直接逐字节读取就行: static void get_mac(uint8_t mac[6]) { for (int i = 0; i < 6; ++i) mac[i] = inb(io_addr + i); } 我这里还多做了一步,把这个网卡的mac也包了起来: static int nic_mac_read(char* […]

  • 小时候玩的盗版"中文游戏 300 合 1"里,超级马里奥的城堡关卡总会响起一段诡异得像恐怖片的"胜利音乐"。这段让无数 80、90 后难忘的童年阴影到底是怎么来的?本期通过 ROM 比对和 6502 反汇编,一步步定位到了真正的元凶——原来那段音乐,其实是盗版厂商塞进去的选关补丁代码,被 APU 当成乐谱"演奏"了出来。
    涉及内容:NES ROM 结构 / PRGROM 与 CHRROM / 6502 汇编 / NMI 中断 / APU 声道 / 盗版卡带逆向

  • 自制操作系统(20):rtl8139网卡驱动(上)- 驱动介绍,初始化与收发数据逻辑实现 我平常不会开发驱动这种东西,所以这种越靠近底层的东西,我觉得自顶向下去实现去实现就更合适。 讲解一个东西的思路应该是,你找到一个最小的你有足够清醒认识的概念,然后顺着这个概念往下讲,比如对我来说,可能就是以太网帧。 目标态 既然是自顶向下,我们就从目标态讲起:我们的目标是以/dev/nic作为我们网卡驱动的虚拟设备文件,我们对它进行读写操作,其实就是读写以太网帧。(不久之后我们就会明白,这是一个很糟糕的点子——但是对于目前的我们来说借它来调试,倒也不算是什么坏主意)那么本质上,我们是要实现nic_read […]

  • 自制操作系统(19):IPC(进程间通信) 这一节我们来实现IPC的两种:管道和信号。 管道 管道的原理:管道可以看作是一个文件,从外部看,其实就是多个进程打开同一个文件,于是它们都会持有这个文件的读端或写端两个文件描述符,或持有读端读取,或写入这个文件,或同时持有。 内部实现的话,存储形态是一个环形缓冲区,还有一把管道访问相关的锁,以及读写端的引用计数。 所以我们想实现管道的话应该是这样: 实现一个pipe函数,这个函数能为我们返回两个文件描述符,它们指向同一个代表管道的文件; 文件描述符会记录当前是写端还是读端,以及当前指向的文件类型是一个管道; 当文件是空的时候,如果有读端来读取,就需要 […]

  • 自制操作系统(18):用户态 malloc/free,解析ELF 我们的进程居然不支持malloc堆空间!这导致我们现在还在栈空间上读取程序映像… 这样做的话,后面稍微大一点的程序跑起来,栈空间就危险了。我们还是需要给进程做一个堆空间。 在PCB中记录堆的指针 首先,我们在PCB中加入两个字段来追踪堆的状态: typedef struct PCB { pid_t pid; uintptr_t esp; uintptr_t cr3; uint32_t saved_eflags; // 该任务的内核栈底(用于释放内存) void* kernel_stack_bottom; uintp […]

  • 自制操作系统(17):围绕增强Shell进行的一系列改进 我们实现了文件系统…但是我们的shell居然还不能自由地访问这个文件系统!是可忍熟不可忍! 我们来制定目标: 1、可以用cd 切换当前工作目录; 2、输入命令时,能匹配/usr/bin下面的文件(最好是可配的),还有当前目录的文件,还能把紧跟着的参数作为入参传给这个文件; 3、运行进程时能阻塞shell,还能把返回值返回给shell。 接下来我们来逐个实现。 匹配/usr/bin下面的文件并执行 我们先来看看怎么让shell默认匹配/usr/bin下面的文件并执行。 我们构建一个数据,冒充path环境变量: constex […]

  • 自制操作系统(16):tarfs 上一节我们说完了抽象的vfs,这一节我们来讲讲具体的tarfs。 tar文件 tar文件其实早期是用于在磁带上组织文件的文件系统。所以解析这个文件很简单,既然文件的记录方式是顺序记录,我们可以用一次循环把里面的所有东西读取出来。我们以512字节为单位解析,解析一个头出来,获取大小,偏移512+大小字节,就是下一个头,直到读到空文件名。 typedef struct { char name[100]; char filemode[8]; char owner_id[8]; char group_id[8]; char size[12]; char last_mo […]

  • 自制操作系统(15):虚拟文件系统 在上一节我们实现了用户态程序的封装编译和加载,完善了系统调用和libc库、crt0,还把shell迁移到了用户空间。今天,我们要干点更激动人心的事情:实现一个基于ustar的文件系统! 不过,我们先来解决一些遗留问题… 一些遗留问题的解决 我们先来做一些收尾操作,之前有些todo的事项放了太久了,后面就积重难返了,所以我们现在集中解决下。 低地址资源重映射和清理 我们的mbi和pmm初始化时遗留的一些数据一直占据着低地址区域,我们是时候考虑它们的去向了。 mbi的数据只会在内核启动初期用到,所以我们可以直接把它清掉,但是记录pmm就需要重新做映 […]

  • 自制操作系统(14):独立用户态程序封装、编译与加载 上一节,我们终于把我们的一小段函数搬到了用户态,但是这个函数居然连调用libc的函数都做不到!看来,是时候把我们从用户态给分离出去了。 独立的用户态程序——简单示例 我们的征程当然不能止步于Hello world,接下来我们要独立编译出用户态的二进制程序,结合Grub Module加载程序到内存,我们再从Kernel_main读取内存内容创建进程。我们先写一个极简的程序来完成分离编译,后面再逐步完善。 独立编译准备 是时候在我们的根目录新建一个user目录来存放我们的用户态程序源文件了。我们先在底下创建一个Makefile: # 编译器设置 […]

  • 自制操作系统(13):用户态进程 上一篇,我们把内核态进程给收了下尾,完善了很多进程相关的特性,但是我们现在还是处于内核态。 而从这一节开始,我们终于要走出内核态,步入用户态的世界了。 用户态vs内核态 …但是我们为什么需要用户态进程呢? 你可能会想,用户态更安全,因为它只能够执行一些非特权指令,以及调用我们给它准备好的系统调用,而且即使用户态的进程崩溃了,也不会导致整个操作系统的崩溃,用户态的进程也有自己的虚拟地址空间,不会污染到内核空间。是的!这就是我们之前做内核进程时回避掉的一点:隔离!而今天我们就要来实现它。 用户态进程创建的基础设施 要创建用户态的进程,得思考下面两个问 […]