【OS课设日志】《Orange‘S:一个操作系统的实现》Ch5

【OS课设日志】《Orange‘S:一个操作系统的实现》第五章——内核雏形

实验内容:在Linux下用汇编写Hello, World!;进一步,汇编和C同时使用;从loader到kernel内核,把kernel内核加载到内存;将控制权交给kernel内核;跳入保护模式,并显示内存的使用情况。

5.1 在Linux下用汇编写Hello World

实验步骤

①创建hello.asm文件

②编译链接(每执行一步都用ls命令查看文件的增加情况)

> ls
hello.asm
> nasm -f elf hello.asm -o hello.o	# 指定输出文件的格式为ELF格式,hello.o为01文件
> ls
hello.asm  hello.o
> ld -s hello.o -o hello	# 将 hello.o与系统库文件连接,并生成可执行文件 hello

! ①执行最后一条指令时报错 \textcolor{red}{!①执行最后一条指令时报错} !执行最后一条指令时报错
【OS课设日志】《Orange‘S:一个操作系统的实现》Ch5_第1张图片
上面为Linux系统下执行汇编文件的操作过程(编译 链接 执行)

5.2 汇编和C同时使用

源文件包含foo.asmbar.c,其调用关系如下:
【OS课设日志】《Orange‘S:一个操作系统的实现》Ch5_第2张图片

本例重点是汇编代码和C代码之间相互调用

实验步骤

①将foo.asmbar.c文件编译为.o目标文件
在这里插入图片描述

②将bar.ofoo.o文件链接为可执行foobar文件

! ②执行指令再次出现不兼容问题 \textcolor{red}{!②执行指令再次出现不兼容问题} !执行指令再次出现不兼容问题
在这里插入图片描述

解决后界面如下
在这里插入图片描述
在这里插入图片描述

③执行foobar文件

./foobar

在这里插入图片描述

重点就是关键字globalextern!!!!!!!!!!

  • 汇编文件中,global是一个汇编符号,用于声明一个全局变量,当一个变量被声明为global时,它可以被程序中的任何模块访问
  • C语言中, extern 是关键字,用于声明一个变量,表明这个变量并不是在这里声明的,而是在别的文件中定义的。使用 extern 关键字可以让多个文件共享同一个变量

5.3 ELF(Executable and Linkable Format)

【OS课设日志】《Orange‘S:一个操作系统的实现》Ch5_第3张图片

  • ELF header:是一个描述文件本身的结构体,它包含了文件的基本信息,如文件类型、机器类型(目标文件所运行的机器平台)、入口地址、程序头表和节头表等信息。
  • Program header table:段表,描述一个段在文件中的位置、大小以及它被放进内存后所在的位置和大小

在Linux系统中,当一个ELF文件被加载到内存中时,操作系统会根据Program header table中的信息将文件中的各个段映射到内存中的相应位置,并设置相应的权限。这样,程序就可以正确地执行了

  • 暂时告一段落

5.4 从Loader到内核

Loader的工作:

  • 加载内核到内存
  • 跳入保护模式

5.4.1 用Loader加载ELF

在这里插入图片描述

文件介绍:

  • fat12hdr.inc:包含了FAT12文件系统的头文件,其中定义了FAT12文件系统的各种数据结构和常量
  • boot.asm:装入引导扇区的启动程序
  • loader.asm:将内核放进内存

第一次运行:

①创建新软盘a.img

②将boot.bin装载到引导扇区:

nasm boot.asm -o boot.bin
dd if=boot.bin of=a.img bs=512 count=1 conv=notrunc

③将kernel.bin装载到软盘

nasm -f elf -o kernel.o kernel.asm
ld -m elf_i386 -s -o kernel.bin kernel.o
sudo mount -o loop a.img /mnt/floppy/
sudo cp kernel.bin /mnt/floppy/ -v
sudo umount /mnt/floppy/

④运行报错
【OS课设日志】《Orange‘S:一个操作系统的实现》Ch5_第4张图片

loader没装进去

⑤装载loader
在这里插入图片描述

⑥再次运行,完成
【OS课设日志】《Orange‘S:一个操作系统的实现》Ch5_第5张图片

5.4.2 跳入保护模式(文件夹d)

代码改变:

第三章中程序由BIOS或DOS加载,描述符的段基址都是运行时计算后填入相应的位置,我们不知道段地址,于是也就不知道程序运行时在内存中的位置

现在Loader由我们自己加载,文件中定义段地址为BaseOfLoader

即,在loader中增加跳入保护模式的代码

实验步骤

同5.4.1节一模一样
【OS课设日志】《Orange‘S:一个操作系统的实现》Ch5_第6张图片

运行结果:
【OS课设日志】《Orange‘S:一个操作系统的实现》Ch5_第7张图片

5.4.3 重新放置内核(文件夹e)

我们要做的工作是根据内核(ELF文件格式)的 Program header table 的信息进行类似下面这个C语言语句的内存复制

memcpy(p_vaddr, BaseOfLoaderPhyAddr + p_offset, p_filesz);

函数解释:从源内存地址的起始位置开始拷贝若干个字节目标内存地址

参数解释:

  • p_vaddr:目标内存地址
  • BaseOfLoaderPhyAddr + p_offset:源内存地址
  • p_filesz:需要拷贝的字节数

实验步骤

①编译链接程序时,将程序入口地址改为0x30400

nasm -f elf -o kernel.o kernel.asm
ld -m elf_i386 -s -Ttext 0x30400 -o kernel.bin kernel.o

内核放置函数为

memcpy(3000h, 9000h + 0, 40Dh);

②查看kernel.bin的内容

xxd -u -a -g 1 -c 16 kernel.bin

【OS课设日志】《Orange‘S:一个操作系统的实现》Ch5_第8张图片

5.4.4 向内核交出控制权

使用jmp指令跳转到事先设定的物理内存地址即可
【OS课设日志】《Orange‘S:一个操作系统的实现》Ch5_第9张图片

5.5 扩充内核

5.5.1 切换堆栈和GDT(文件夹f)

呃呃呃

附录:Bug

①ld: i386 架构于输入文件 hello.o 与 i386:x86-64 输出不兼容

【OS课设日志】《Orange‘S:一个操作系统的实现》Ch5_第10张图片

原因分析

输入文件hello.o编译为 i386 架构,与输出架构 i386:x86-64 不兼容。这可能是因为编译器和链接器正在尝试将不同架构的目标文件进行链接,导致链接失败。

解决办法

更改输入指令为

ld -m elf_i386 -s -o hello hello.o

其中-m 选项指定目标文件的架构为 elf_i386,与输入的hello.o文件架构匹配

参考

这是一个连接

②问题同上

【OS课设日志】《Orange‘S:一个操作系统的实现》Ch5_第11张图片

原因分析

错误提示是由于foo.obar.o的架构不同,foo.o 是 i386 架构,而 bar.o 是 x86-64 架构

解决办法

将文件全部编译为i386框架下的.o文件,再进行链接,即执行下述指令

nasm -f elf -o foo.o foo.asm
gcc -m32 -c bar.c -o bar.o
ld -m elf_i386 -s -o foobar foo.o bar.o

你可能感兴趣的:(c语言,linux,开发语言,ubuntu)