2.html

来自「linux 0.11中文版 有注释」· HTML 代码 · 共 358 行 · 第 1/2 页

HTML
358
字号
<a name='L169'>.org 0x1000 # 从偏移0x1000 处开始是第1 个页表(偏移0 开始处将存放页表目录)。
<a name='L170'>pg0:
<a name='L171'>
<a name='L172'>.org 0x2000
<a name='L173'>pg1:
<a name='L174'>
<a name='L175'>.org 0x3000
<a name='L176'>pg2:
<a name='L177'>
<a name='L178'>.org 0x4000
<a name='L179'>pg3:
<a name='L180'>
<a name='L181'>.org 0x5000 # 定义下面的内存数据块从偏移0x5000 处开始。
<a name='L182'><i><font color='green'>/*</font></i>
<a name='L183'><i><font color='green'>* tmp_floppy_area is used by the floppy-driver when DMA cannot</font></i>
<a name='L184'><i><font color='green'>* reach to a buffer-block. It needs to be aligned, so that it isn't</font></i>
<a name='L185'><i><font color='green'>* on a 64kB border.</font></i>
<a name='L186'><i><font color='green'>*/</font></i>
<a name='L187'><i><font color='green'>/* 当DMA(直接存储器访问)不能访问缓冲块时,下面的tmp_floppy_area 内存块</font></i>
<a name='L188'><i><font color='green'>* 就可供软盘驱动程序使用。其地址需要对齐调整,这样就不会跨越64kB 边界。</font></i>
<a name='L189'><i><font color='green'>*/</font></i>
<a name='L190'>_tmp_floppy_area:
<a name='L191'>.fill 1024,1,0 # 共保留1024 项,每项1 字节,填充数值0。
<a name='L192'>
<a name='L193'><i><font color='green'># 下面这几个入栈操作(pushl)用于为调用/init/main.c 程序和返回作准备。</font></i>
<a name='L194'><i><font color='green'># 前面3 个入栈指令不知道作什么用的,也许是Linus 用于在调试时能看清机器码用的?。</font></i>
<a name='L195'><font color='darkred'>#</font> 139 行的入栈操作是模拟调用main.c 程序时首先将返回地址入栈的操作,所以如果
<a name='L196'><i><font color='green'># main.c 程序真的退出时,就会返回到这里的标号L6 处继续执行下去,也即死循环。</font></i>
<a name='L197'><font color='darkred'>#</font> 140 行将main.c 的地址压入堆栈,这样,在设置分页处理(setup_paging)结束后
<a name='L198'><i><font color='green'># 执行'ret'返回指令时就会将main.c 程序的地址弹出堆栈,并去执行main.c 程序去了。</font></i>
<a name='L199'>after_page_tables:
<a name='L200'>pushl $0 # These are the parameters to main :-)
<a name='L201'>pushl $0 # 这些是调用main 程序的参数(指init/main.c)。
<a name='L202'>pushl $0
<a name='L203'>pushl $L6 # return address for main, if it decides to.
<a name='L204'>pushl $_main # '_main'是编译程序对main 的内部表示方法。
<a name='L205'>jmp setup_paging # 跳转至第198 行。
<a name='L206'>L6:
<a name='L207'>jmp L6 # main should never return here, but
<a name='L208'><i><font color='green'># just in case, we know what happens.</font></i>
<a name='L209'>
<a name='L210'><i><font color='green'>/* This is the default interrupt "handler" :-) */</font></i>
<a name='L211'><i><font color='green'>/* 下面是默认的中断“向量句柄”? */</font></i>
<a name='L212'>int_msg:
<a name='L213'>.asciz "Unknown interrupt\n\r" # 定义字符串“未知中断(回车换行)”。
<a name='L214'>.align 2 # 按4 字节方式对齐内存地址。
<a name='L215'>ignore_int:
<a name='L216'>pushl %eax
<a name='L217'>pushl %ecx
<a name='L218'>pushl %edx
<a name='L219'>push %ds # 这里请注意!!ds,es,fs,gs 等虽然是16 位的寄存器,但入栈后
<a name='L220'><i><font color='green'># 仍然会以32 位的形式入栈,也即需要占用4 个字节的堆栈空间。</font></i>
<a name='L221'>push %es
<a name='L222'>push %fs
<a name='L223'>movl $0x10,%eax # 置段选择符(使ds,es,fs 指向gdt 表中的数据段)。
<a name='L224'>mov %ax,%ds
<a name='L225'>mov %ax,%es
<a name='L226'>mov %ax,%fs
<a name='L227'>pushl $int_msg # 把调用printk 函数的参数指针(地址)入栈。
<a name='L228'>call _printk # 该函数在/kernel/<a href='../S/73.html#L30' title='Defined at 30 in kernel/printk.c.'>printk</a>.c 中。
<a name='L229'><font color='darkred'>#</font> '_printk'是printk 编译后模块中的内部表示法。
<a name='L230'>popl %eax
<a name='L231'>pop %fs
<a name='L232'>pop %es
<a name='L233'>pop %ds
<a name='L234'>popl %edx
<a name='L235'>popl %ecx
<a name='L236'>popl %eax
<a name='L237'>iret # 中断返回(把中断调用时压入栈的CPU 标志寄存器(32 位)值也弹出)。
<a name='L238'>
<a name='L239'>
<a name='L240'><i><font color='green'>/*</font></i>
<a name='L241'><i><font color='green'>* Setup_paging</font></i>
<a name='L242'><i><font color='green'>*</font></i>
<a name='L243'><i><font color='green'>* This routine sets up paging by setting the page bit</font></i>
<a name='L244'><i><font color='green'>* in cr0. The page tables are set up, identity-mapping</font></i>
<a name='L245'><i><font color='green'>* the first 16MB. The pager assumes that no illegal</font></i>
<a name='L246'><i><font color='green'>* addresses are produced (ie &gt;4Mb on a 4Mb machine).</font></i>
<a name='L247'><i><font color='green'>*</font></i>
<a name='L248'><i><font color='green'>* NOTE! Although all physical memory should be identity</font></i>
<a name='L249'><i><font color='green'>* mapped by this routine, only the kernel page functions</font></i>
<a name='L250'><i><font color='green'>* use the &gt;1Mb addresses directly. All "normal" functions</font></i>
<a name='L251'><i><font color='green'>* use just the lower 1Mb, or the local data space, which</font></i>
<a name='L252'><i><font color='green'>* will be mapped to some other place - mm keeps track of</font></i>
<a name='L253'><i><font color='green'>* that.</font></i>
<a name='L254'><i><font color='green'>*</font></i>
<a name='L255'><i><font color='green'>* For those with more memory than 16 Mb - tough luck. I've</font></i>
<a name='L256'><i><font color='green'>* not got it, why should you :-) The source is here. Change</font></i>
<a name='L257'><i><font color='green'>* it. (Seriously - it shouldn't be too difficult. Mostly</font></i>
<a name='L258'><i><font color='green'>* change some constants etc. I left it at 16Mb, as my machine</font></i>
<a name='L259'><i><font color='green'>* even cannot be extended past that (ok, but it was cheap :-)</font></i>
<a name='L260'><i><font color='green'>* I've tried to show which constants to change by having</font></i>
<a name='L261'><i><font color='green'>* some kind of marker at them (search for "16Mb"), but I</font></i>
<a name='L262'><i><font color='green'>* won't guarantee that's all :-( )</font></i>
<a name='L263'><i><font color='green'>*/</font></i>
<a name='L264'><i><font color='green'>/*</font></i>
<a name='L265'><i><font color='green'>* 这个子程序通过设置控制寄存器cr0 的标志(PG 位31)来启动对内存的分页处理功能,</font></i>
<a name='L266'><i><font color='green'>* 并设置各个页表项的内容,以恒等映射前16 MB 的物理内存。分页器假定不会产生非法的</font></i>
<a name='L267'><i><font color='green'>* 地址映射(也即在只有4Mb 的机器上设置出大于4Mb 的内存地址)。</font></i>
<a name='L268'><i><font color='green'>* 注意!尽管所有的物理地址都应该由这个子程序进行恒等映射,但只有内核页面管理函数能</font></i>
<a name='L269'><i><font color='green'>* 直接使用&gt;1Mb 的地址。所有“一般”函数仅使用低于1Mb 的地址空间,或者是使用局部数据</font></i>
<a name='L270'><i><font color='green'>* 空间,地址空间将被映射到其它一些地方去 -- mm(内存管理程序)会管理这些事的。</font></i>
<a name='L271'><i><font color='green'>* 对于那些有多于16Mb 内存的家伙 - 太幸运了,我还没有,为什么你会有?。代码就在这里,</font></i>
<a name='L272'><i><font color='green'>* 对它进行修改吧。(实际上,这并不太困难的。通常只需修改一些常数等。我把它设置为</font></i>
<a name='L273'><i><font color='green'>* 16Mb,因为我的机器再怎么扩充甚至不能超过这个界限(当然,我的机器很便宜的?)。</font></i>
<a name='L274'><i><font color='green'>* 我已经通过设置某类标志来给出需要改动的地方(搜索“16Mb”),但我不能保证作这些</font></i>
<a name='L275'><i><font color='green'>* 改动就行了??)。</font></i>
<a name='L276'><i><font color='green'>*/</font></i>
<a name='L277'>.align 2 # 按4 字节方式对齐内存地址边界。
<a name='L278'>setup_paging: # 首先对5 页内存(1 页目录 + 4 页页表)清零
<a name='L279'>movl $1024*5,%ecx <i><font color='green'>/* 5 pages - pg_dir+4 page tables */</font></i>
<a name='L280'>xorl %eax,%eax
<a name='L281'>xorl %edi,%edi <i><font color='green'>/* pg_dir is at 0x000 */</font></i>
<a name='L282'><i><font color='green'># 页目录从0x000 地址开始。</font></i>
<a name='L283'>cld;rep;stosl
<a name='L284'><i><font color='green'># 下面4 句设置页目录中的项,我们共有4 个页表所以只需设置4 项。</font></i>
<a name='L285'><i><font color='green'># 页目录项的结构与页表中项的结构一样,4 个字节为1 项。参见上面113 行下的说明。</font></i>
<a name='L286'><font color='darkred'>#</font> "$pg0+7"表示:0x00001007,是页目录表中的第1 项。
<a name='L287'><i><font color='green'># 则第1 个页表所在的地址 = 0x00001007 &amp; 0xfffff000 = 0x1000;</font></i>
<a name='L288'><i><font color='green'># 第1 个页表的属性标志 = 0x00001007 &amp; 0x00000fff = 0x07,表示该页存在、用户可读写。</font></i>
<a name='L289'>movl $pg0+7,_pg_dir <i><font color='green'>/* set present bit/user r/w */</font></i>
<a name='L290'>movl $pg1+7,_pg_dir+4 <i><font color='green'>/* --------- " " --------- */</font></i>
<a name='L291'>movl $pg2+7,_pg_dir+8 <i><font color='green'>/* --------- " " --------- */</font></i>
<a name='L292'>movl $pg3+7,_pg_dir+12 <i><font color='green'>/* --------- " " --------- */</font></i>
<a name='L293'><i><font color='green'># 下面6 行填写4 个页表中所有项的内容,共有:4(页表)*1024(项/页表)=4096 项(0 - 0xfff),</font></i>
<a name='L294'><i><font color='green'># 也即能映射物理内存 4096*4Kb = 16Mb。</font></i>
<a name='L295'><i><font color='green'># 每项的内容是:当前项所映射的物理内存地址 + 该页的标志(这里均为7)。</font></i>
<a name='L296'><i><font color='green'># 使用的方法是从最后一个页表的最后一项开始按倒退顺序填写。一个页表的最后一项在页表中的</font></i>
<a name='L297'><i><font color='green'># 位置是1023*4 = 4092。因此最后一页的最后一项的位置就是$pg3+4092。</font></i>
<a name='L298'>movl $pg3+4092,%edi # edi??最后一页的最后一项。
<a name='L299'>movl $0xfff007,%eax <i><font color='green'>/* 16Mb - 4096 + 7 (r/w user,p) */</font></i>
<a name='L300'><i><font color='green'># 最后1 项对应物理内存页面的地址是0xfff000,</font></i>
<a name='L301'><i><font color='green'># 加上属性标志7,即为0xfff007.</font></i>
<a name='L302'>std # 方向位置位,edi 值递减(4 字节)。
<a name='L303'>1: stosl <i><font color='green'>/* fill pages backwards - more efficient :-) */</font></i>
<a name='L304'>subl $0x1000,%eax # 每填写好一项,物理地址值减0x1000。
<a name='L305'>jge 1b # 如果小于0 则说明全添写好了。
<a name='L306'><i><font color='green'># 设置页目录基址寄存器cr3 的值,指向页目录表。</font></i>
<a name='L307'>xorl %eax,%eax <i><font color='green'>/* pg_dir is at 0x0000 */</font></i> # 页目录表在0x0000 处。
<a name='L308'>movl %eax,%cr3 <i><font color='green'>/* cr3 - page directory start */</font></i>
<a name='L309'><i><font color='green'># 设置启动使用分页处理(cr0 的PG 标志,位31)</font></i>
<a name='L310'>movl %cr0,%eax
<a name='L311'>orl $0x80000000,%eax # 添上PG 标志。
<a name='L312'>movl %eax,%cr0 <i><font color='green'>/* set paging (PG) bit */</font></i>
<a name='L313'>ret <i><font color='green'>/* this also flushes prefetch-queue */</font></i>
<a name='L314'><i><font color='green'># 在改变分页处理标志后要求使用转移指令刷新预取指令队列,这里用的是返回指令ret。</font></i>
<a name='L315'><i><font color='green'># 该返回指令的另一个作用是将堆栈中的main 程序的地址弹出,并开始运行/init/main.c 程序。</font></i>
<a name='L316'><i><font color='green'># 本程序到此真正结束了。</font></i>
<a name='L317'>
<a name='L318'>.align 2 # 按4 字节方式对齐内存地址边界。
<a name='L319'>.word 0
<a name='L320'>idt_descr: #下面两行是lidt 指令的6 字节操作数:长度,基址。
<a name='L321'>.word 256*8-1 # idt contains 256 entries
<a name='L322'>.long _idt
<a name='L323'>.align 2
<a name='L324'>.word 0
<a name='L325'>gdt_descr: # 下面两行是lgdt 指令的6 字节操作数:长度,基址。
<a name='L326'>.word 256*8-1 # so does gdt (not that that's any
<a name='L327'>.long _gdt # magic number, but it works for me :^)
<a name='L328'>
<a name='L329'>.align 3 # 按8 字节方式对齐内存地址边界。
<a name='L330'>_idt: .fill 256,8,0 # idt is uninitialized # 256 项,每项8 字节,填0。
<a name='L331'>
<a name='L332'><i><font color='green'># 全局表。前4 项分别是空项(不用)、代码段描述符、数据段描述符、系统段描述符,其中</font></i>
<a name='L333'><i><font color='green'># 系统段描述符linux 没有派用处。后面还预留了252 项的空间,用于放置所创建任务的</font></i>
<a name='L334'><i><font color='green'># 局部描述符(LDT)和对应的任务状态段TSS 的描述符。</font></i>
<a name='L335'><font color='darkred'>#</font> (0-nul, 1-cs, 2-ds, 3-sys, 4-TSS0, 5-LDT0, 6-TSS1, 7-LDT1, 8-TSS2 etc...)
<a name='L336'>_gdt: .quad 0x0000000000000000 <i><font color='green'>/* NULL descriptor */</font></i>
<a name='L337'>.quad 0x00c09a0000000fff <i><font color='green'>/* 16Mb */</font></i> # 代码段最大长度16M。
<a name='L338'>.quad 0x00c0920000000fff <i><font color='green'>/* 16Mb */</font></i> # 数据段最大长度16M。
<a name='L339'>.quad 0x0000000000000000 <i><font color='green'>/* TEMPORARY - don't use */</font></i>
<a name='L340'>.fill 252,8,0 <i><font color='green'>/* space for LDT's and TSS's etc */</font></i>
</pre>
<hr>
<a name='BOTTOM'>
<i><font color='green'>/* [&lt;][&gt;][^][v]<a href='#TOP'>[top]</a>[bottom]<a href='../mains.html'>[index]</a><a href='../help.html'>[help]</a> */</font></i>
</body>
</html>

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?