Blog

Learning by doing.


Linux0.11中的汇编语言学习笔记

Feb 28, 2013 • Linux • Tags: Assembling Linux

这学期我不自量力地选了《操作系统高级教程》这门课,授课内容围绕着Linux0.11版的内核展开,主要讲解内核的工作机理和设计思想,涉及两种汇编语言和具有汇编语言风格的C语言。

授课老师是杨力祥老师,物理学出生,业余爱好操作系统,写过Unix系统的内核,正带领一个团队开发中国人自己的操作系统。 这个团队出版过一本书——《Linux内核设计的艺术》,也就是本课程的教材,下文中的一些引用如果不加说明则出自此书。

杨老师这人特逗,不时来一个段子。 而他的讲解也十分精彩到位,据他所说,学习这门课的好处之一在于能够锻炼阅读和理解代码的能力,特别是锻炼对万行量级的代码的整体驾驭能力。

课程虽好,我毕竟不是计算机系出身,各种专业术语听得我仿佛回到了初生时的状态——周围的这个世界精彩又完全陌生,只有凭着本能去探索了。

第1章介绍了“从开机加电到执行main函数之前的过程”,主要的困难在于汇编语言。 为了不挂科,我只好逼迫自己把书中出现的汇编指令记录下来,然后软磨硬泡,逐个击破。

1. bootsect对内存的规划

// 代码路径:boot/bootsect.s
SETUPLEN = 4
BOOTSEG = 0x07c0
INITSEG = 0x9000
SETUPSEG = 0x9020
SYSSEG = 0x1000
ENDSEG = SYSSEG + SYSSIZE

这些源代码的作用就是对后续操作所涉及的内存位置进行设置。

我对这些汇编代码的理解是: 这段代码的语法类似于定义变量名并赋值。 等号左端是变量名,右端是值(普通值或内存地址)。

2. 复制bootsect

// 代码路径:boot/bootsect.s
mov ax, #BOOTSEG
mov ds, ax
mov ax, #INITSEG
mov es, ax
mov cx, #256
sub si, si
sub di, di
rep
movw
jmpi go, INITSEG
go: mov ax, cs
mov ds, ax

OMG,这都啥呀。。。

这段代码本身实现的目的特牛叉:把bootsect.s的代码(itself)从内存中的一个地方挪到另一个地方。 为什么牛叉?因为你要一边执行bootsect.s的代码一边挪动itself,而“挪动”这个动作又要靠bootsect.s中的代码来实现!

好吧,一条条来看。。。

  • mov ax, #BOOTSEG
    • #BOOTSEG类似于shell中变量的取值($Var),这里也就是之前设定的0x07c0
    • ax是一种16位寄存器,并且是累加寄存器,常用于运算[1]。
    • mov是一种数据传送指令[[2]],它把BOOTSEG的地址传送到累加寄存器ax中。
  • mov ds, ax
    • ds是数据段寄存器(Data Segment)[1]。
  • mov ax, #INITSEG
    • 这句把INITSEG的地址0x9000传给ax
  • mov es, ax
    • es是附加段寄存器(Extra Segment)[1]。
  • mov cx, #256
    • #256表示256字,也就是512字节。
    • cx是计数寄存器,常用于计数[1]。
  • sub si, si
    • si是源变址寄存器(Source Index),用来存放相对于ds段之源变址指针[1]。
    • sub为减法指令,将逗号前面的数减去逗号后面的数,结果保存到逗号前面的数[2]。
    • 这句把si清零。
  • sub di, di
    • di是源变址寄存器(Destination Index),用来存放相对于es段之目的变址指针[1]。
    • 这句把di清零。
  • rep
    • Repeat, repeat, repeat…till cx = 0. [2, 3]
  • movw
    • ds:si的内容传送到es:di,这里是移动一个字(move word)[3]。
    • 这句受上一句rep的作用,也就是重复执行movw256次。循环结束后,从BOOTSEG开始的256字就被复制到了从INITSEG开始的256字的位置。
  • jmpi go, INITSEG
    • jmpi表示jump intersegment,也就是段间跳转[3]。
  • go: mov ax, cs
  • mov ds, ax

参考资料

Tags: Assembling Linux