免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 984 | 回复: 0
打印 上一主题 下一主题

linux-0.00完全注释(二) [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2007-04-23 21:40 |只看该作者 |倒序浏览

                # 这段代码已经被boot.s调入到了内存0x10000,并且在保护模式下
KRN_BASE     = 0x10000
# 在boot.s中我们已经了解了段寄存器的结构,它是指向gdt表的索引
# gdt表的一个表项占8个字节。每个用户进程占两个gdt表项,tss和ldt
# 第一项不用
# 第二项是系统的代码段
# 第三项是系统的数据段
# 第三项是系统的显存段
# 那么第四项就是从第32(0x20)个字节开始,依次类推
TSS0_SEL    = 0x20
LDT0_SEL    = 0x28
TSS1_SEL    = 0X30
LDT1_SEL    = 0x38
.text
startup_32:
# 将所有段寄存器都指向系统数据段,gdt表第三项
    movl $0x10,%eax
    mov %ax,%ds
    mov %ax,%es
    mov %ax,%fs
    mov %ax,%gs
# 装入堆栈段地址
    lss stack_ptr,%esp
#
# linux-0.00有两个进程,其中一个一直打印A,另外一个一直打印B
# 直到8253产生中断切换进程
# 操作系统采用分页机制前采用分段机制。现代操作系统中的段
# 有人说Linux巧妙的绕了过去,有人说是不用了...
#
# 对于采用分段机制的系统每个用户进程占gdt表的两项,一个是tss段,另外一个是ldt段
# 下面的十几行代码设置两个进程的tss段和ldt段
# 将这两个段的绝对地址写到gdt表项相应的地方
# gdt表项的结构请参考C程序
#
http://blog.chinaunix.net/u/23177/showart_209429.html
#
    movl $KRN_BASE, %ebx
    movl $gdt, %ecx
    lea tss0, %eax
    movl $TSS0_SEL, %edi   
    call set_base
    lea ldt0, %eax
    movl $LDT0_SEL, %edi
    call set_base
    lea tss1, %eax
    movl $TSS1_SEL, %edi
    call set_base
    lea ldt1, %eax
    movl $LDT1_SEL, %edi
    call set_base
# setup_idt将256个中断向量的处理函数都设置为了ignore_int
    call setup_idt
# 将全局描述符表的长度以及起始地址载入到寄存器gdtr中
    call setup_gdt
    movl $0x10,%eax        # reload all the segment registers
    mov %ax,%ds        # after changing gdt.
    mov %ax,%es
    mov %ax,%fs
    mov %ax,%gs
    lss stack_ptr,%esp
# setup up timer 8253 chip.
    movb $0x36, %al
    movl $0x43, %edx
    outb %al, %dx
    movl $11930, %eax        # timer frequency 100 HZ
    movl $0x40, %edx
    outb %al, %dx
    movb %ah, %al
    outb %al, %dx
#
# 操作系统有三种中断,软件中断,硬件中断和处理器异常
# timer_interrupt是硬件中断处理程序,告诉系统时间片到了,切换进程
# system_interrupt是软件中断处理程序,用户进程可以利用该系统调用打印字母A或者B
#
# 实模式下的IDT(中断描述符表)在内存中从地址0x0开始的地方
# 保护模式下有一个寄存器idtr专门指向IDT开始的地方
# 因此IDT不必放在地址0x0开始的地方了
# 实模式下一个中断向量占4个字节,256项共1k字节
# 保护模式下一个中断描述符占8个字节,256项共2k字节
#
# 保护模式下的中断描述符也就是中断门、陷阱门或者任务门。有的书上说还有一种调用门
# 中断门和陷阱门的唯一区别就是中断门不允许进一步中断,也就是说执行中断门所指的
# 中断处理程序时是关中断的。中断门用来处理硬件中断
# 陷阱门相反,允许中断,处理软件中断。在下面的代码中就体现了这一点
# 任务门提供了一种切换进程的硬件级支持。现在的Linux内核并没有使用这种机制
# Linux只使用了TSS结构中的I/O端口使用位图和桟指针
# 不过linux-0.00确实使用了这种硬件切换机制
#
# 设置中断处理程序所在的段是系统代码段,也就是gdt表的第二项
# 并将中断处理程序在段中偏移的高16位置为0
    movl $0x00080000, %eax   
# 设置中断处理程序在段中偏移的低16位
    movw $timer_interrupt, %ax
# 内核态中断门,并且该门有效
    movw $0x8E00, %dx
# timer_interrupt是32号中断向量
    movl $0x20, %ecx
    lea idt(,%ecx,8), %esi
# 将设置好的中断门的内容写到idt相应的表项(32)中
    movl %eax,(%esi)
    movl %edx,4(%esi)
# 设置中断处理程序在段中偏移的低16位
    movw $system_interrupt, %ax
# 用户态陷阱门,并且该门有效
    movw $0xef00, %dx
# system_interrupt是128号中断向量
    movl $0x80, %ecx
    lea idt(,%ecx,8), %esi
# 将设置好的陷阱门的内容写到idt相应的表项(128)中
    movl %eax,(%esi)
    movl %edx,4(%esi)
# unmask the timer interrupt.
    movl $0x21, %edx
    inb %dx, %al
    andb $0xfe, %al
    outb %al, %dx
#
# 下面三行代码设置EFLAGS的NT为0
# NT=0为下面的任务切换做准备
# 《Linux内核完全注释》第102页403行代码如下(kernel/sched.c):
#    __asm__("pushfl ; andl $0xffffbfff,(%esp) ; popfl");
# 下面三行就是上面代码的展开
#
# NT是nested task的缩写,EFLAGS第15个标志位。当NT=0时不发生任务切换
# 在后面的tss0和tss1结构体中eflags=0x0200,也就是NT=1,发生任务切换
#
# x86一共有四种方式切换任务
# 1. ljmp $task_selector, $0
# 2. lcall $task_selector, $0
# 3. int 0x80
# 4. iret
# ljmp和lcall的区别是ljmp再也跳转不回来了,而lcall还可以跳转回来
# int 0x80必须跳转到任务门
# iret必须将NT设置为1
# 后面的计时器中断处理函数timer_interrupt采用的是ljmp
# ljmp $TSS0_SEL, $0
# ljmp $TSS1_SEL, $0
#
    pushfl
    andl $0xffffbfff, (%esp)
    popfl
# 载入进程0的任务寄存器
    movl $TSS0_SEL, %eax
    ltr %ax
# 载入进程0的局部描述符表寄存器
    movl $LDT0_SEL, %eax
    lldt %ax
# 设置当前进程为0
    movl $0, current
# 开中断。iret后两个任务就可以交替运行
    sti
    pushl $0x17
    pushl $stack0_ptr
    pushfl
    pushl $0x0f
    pushl $task0
# 不发生任务切换(NT=0),仅仅跳转到task0处以用户态执行
# 我觉得也可以理解为跳转到了一个特殊的任务0进程
    iret
/****************************************/
setup_gdt:
    lgdt lgdt_opcode
    ret
setup_idt:
    lea ignore_int,%edx
    movl $0x00080000,%eax
    movw %dx,%ax        /* selector = 0x0008 = cs */
    movw $0x8E00,%dx    /* interrupt gate - dpl=0, present */
    lea idt,%edi
    mov $256,%ecx
rp_sidt:
    movl %eax,(%edi)
    movl %edx,4(%edi)
    addl $8,%edi
    dec %ecx
    jne rp_sidt
    lidt lidt_opcode
    ret
# in: %eax - logic addr; %ebx = base addr ;
# %ecx - table addr; %edi - descriptors offset.
set_base:
    addl %ebx, %eax
    addl %ecx, %edi
    movw %ax, 2(%edi)
    rorl $16, %eax
    movb %al, 4(%edi)
    movb %ah, 7(%edi)
    rorl $16, %eax
    ret
write_char:
    push %gs
    pushl %ebx
    pushl %eax
# 载入显存段
    mov $0x18, %ebx
    mov %bx, %gs
# 得到上一次显示字符的位置
    movl scr_loc, %bx
# 每个字符位占两个字节,第一个字节就是我们要显示的字节
    shl $1, %ebx
# 将字符'A'或'B'直接写到显存中
    movb %al, %gs:(%ebx)
# 一个字符位占两个字节,第二个字节是属性字节
# 例如0x07就是正常显示,黑底白字;0x70就是反相显示,白底黑字
# 当程序设置了一个属性时,它就保持这种属性,之后的所有字符都具有相同的属性
# 直到另外一个操作改变了属性
# 如果在这里添加如下三行,就会得到白底黑字的效果
#    inc %ebx
#    movb $0x70, %gs:(%ebx)
#    dec %ebx
    shr $1, %ebx
    incl %ebx
# CGA提供了两种文本模式40x25和80x25,如果是80x25也就是说一屏可以有2000个字符
# 每个字符占8x8个像素,则CGA有两种分辨率320x200和640x200
# 如果将下面的2000改为1000,我们会发现只出现前半屏的输出
    cmpl $2000, %ebx
# 如果写满了一屏,则重新开始
    jb 1f
# 将计数器赋为0
    movl $0, %ebx
# 保存当前光标位置
1:    movl %ebx, scr_loc   
    popl %eax
    popl %ebx
    pop %gs
    ret
/***********************************************/
/* This is the default interrupt "handler" :-) */
.align 2
ignore_int:
    push %ds
    pushl %eax
    movl $0x10, %eax
    mov %ax, %ds
    movl $67, %eax            /* print 'C' */
    call write_char
    popl %eax
    pop %ds
    iret
/* Timer interrupt handler */
.align 2
timer_interrupt:
    push %ds
    pushl %edx
    pushl %ecx
    pushl %ebx
    pushl %eax
    movl $0x10, %eax
    mov %ax, %ds
    movb $0x20, %al
    outb %al, $0x20
    movl $1, %eax
    cmpl %eax, current
    je 1f
    movl %eax, current
    ljmp $TSS1_SEL, $0
    jmp 2f
1:    movl $0, current
    ljmp $TSS0_SEL, $0
2:    popl %eax
    popl %ebx
    popl %ecx
    popl %edx
    pop %ds
    iret
/* system call handler */
.align 2
system_interrupt:
    push %ds
    pushl %edx
    pushl %ecx
    pushl %ebx
    pushl %eax
    movl $0x10, %edx
    mov %dx, %ds
    call write_char
    popl %eax
    popl %ebx
    popl %ecx
    popl %edx
    pop %ds
    iret
/*********************************************/
current:.long 0
scr_loc:.long 0
.align 2
.word 0
lidt_opcode:
    .word 256*8-1        # idt contains 256 entries
    .long idt + KRN_BASE    # This will be rewrite by code.
.align 2
.word 0
lgdt_opcode:
    .word (end_gdt-gdt)-1    # so does gdt
    .long gdt + KRN_BASE    # This will be rewrite by code.
    .align 3
idt:    .fill 256,8,0        # idt is uninitialized
gdt:    .quad 0x0000000000000000    /* NULL descriptor */
    .quad 0x00c09a01000007ff    /* 8Mb 0x08, base = 0x10000 */
    .quad 0x00c09201000007ff    /* 8Mb 0x10 */
# 对照段描述符表项定义可以知道该段的基址是0xb8000(736K)
# 《Linux内核完全注释》一书206页649行代码如下
# vidio_mem_start = 0xb8000
# 说明了彩色模式下显示内存的起始地址是0xb8000
    .quad 0x00c0920b80000002    /* screen 0x18 - for display */
    .quad 0x0000e90100000068    # TSS0 descr 0x20
    .quad 0x0000e20100000040    # LDT0 descr 0x28
    .quad 0x0000e90100000068    # TSS1 descr 0x30
    .quad 0x0000e20100000040    # LDT1 descr 0x38
end_gdt:
    .fill 128,4,0
stack_ptr:
    .long stack_ptr
    .word 0x10
/*************************************/
.align 3
ldt0:    .quad 0x0000000000000000
    .quad 0x00c0fa01000003ff    # 0x0f, base = 0x10000
    .quad 0x00c0f201000003ff    # 0x17
tss0:
    .long 0             /* back link */
    .long stack0_krn_ptr, 0x10    /* esp0, ss0 */
    .long 0, 0            /* esp1, ss1 */
    .long 0, 0            /* esp2, ss2 */
    .long 0                /* cr3 */
    .long task0            /* eip */
    .long 0x200            /* eflags */
    .long 0, 0, 0, 0        /* eax, ecx, edx, ebx */
    .long stack0_ptr, 0, 0, 0    /* esp, ebp, esi, edi */
    .long 0x17,0x0f,0x17,0x17,0x17,0x17 /* es, cs, ss, ds, fs, gs */
    .long LDT0_SEL            /* ldt */
    .long 0x8000000            /* trace bitmap */
    .fill 128,4,0
stack0_krn_ptr:
    .long 0
/************************************/
.align 3
ldt1:    .quad 0x0000000000000000
    .quad 0x00c0fa01000003ff    # 0x0f, base = 0x10000
    .quad 0x00c0f201000003ff    # 0x17
tss1:
    .long 0             /* back link */
    .long stack1_krn_ptr, 0x10    /* esp0, ss0 */
    .long 0, 0            /* esp1, ss1 */
    .long 0, 0            /* esp2, ss2 */
    .long 0                /* cr3 */
    .long task1            /* eip */
    .long 0x200            /* eflags */
    .long 0, 0, 0, 0        /* eax, ecx, edx, ebx */
    .long stack1_ptr, 0, 0, 0    /* esp, ebp, esi, edi */
    .long 0x17,0x0f,0x17,0x17,0x17,0x17 /* es, cs, ss, ds, fs, gs */
    .long LDT1_SEL            /* ldt */
    .long 0x8000000            /* trace bitmap */
    .fill 128,4,0
stack1_krn_ptr:
    .long 0
/************************************/
task0:
    movl $0x17, %eax
    movw %ax, %ds
    movl $65, %al              /* print 'A' */
    int $0x80
    movl $0xfff, %ecx
1:    loop 1b
    jmp task0
    .fill 128,4,0
stack0_ptr:
    .long 0
task1:
    movl $0x17, %eax
    movw %ax, %ds
    movl $66, %al              /* print 'B' */
    int $0x80
    movl $0xfff, %ecx
1:    loop 1b
    jmp task1
    .fill 128,4,0
stack1_ptr:
    .long 0
/*** end ***/
               
               
               
               
               

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/23177/showart_284630.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP