Linux内核源代码解读

合集下载

Linux内核源代码的阅读和工具具体介绍

Linux内核源代码的阅读和工具具体介绍

Linux内核源代码的阅读和工具具体介绍 Linux内核源代码的阅读和工具具体介绍Linux的内核源代码可以从很多途径得到。

一般来讲,在安装的linux系统下,/usr/src/linux目录下的东西就是内核源代码。

另外还可以从互连上下载,解压缩后文件一般也都位于linux目录下。

内核源代码有很多版本,目前最新的版本是2.2.14。

许多人对于阅读Linux内核有一种恐惧感,其实大可不必。

当然,象Linux内核这样大而复杂的系统代码,阅读起来确实有很多困难,但是也不象想象的那么高不可攀。

只要有恒心,困难都是可以克服的。

任何事情做起来都需要有方法和工具。

正确的方法可以指导工作,良好的工具可以事半功倍。

对于Linux 内核源代码的阅读也同样如此。

下面我就把自己阅读内核源代码的一点(经验)介绍一下,最后介绍Window平台下的一种阅读工具。

对于源代码的`阅读,要想比较顺利,事先最好对源代码的知识背景有一定的了解。

对于linux内核源代码来讲,基本要求是:⑴(操作系统)的基本知识;⑵对(C语言)比较熟悉,最好要有汇编语言的知识和GNU C对标准C的扩展的知识的了解。

另外在阅读之前,还应该知道Linux内核源代码的整体分布情况。

我们知道现代的操作系统一般由进程管理、内存管理、文件系统、驱动程序、络等组成。

看一下Linux内核源代码就可看出,各个目录大致对应了这些方面。

Linux内核源代码的组成如下(假设相对于linux目录):arch 这个子目录包含了此核心源代码所支持的硬件体系结构相关的核心代码。

如对于X86平台就是i386。

include 这个目录包括了核心的大多数include文件。

另外对于每种支持的体系结构分别有一个子目录。

init 此目录包含核心启动代码。

mm 此目录包含了所有的内存管理代码。

与具体硬件体系结构相关的内存管理代码位于arch/*/mm目录下,如对应于X86的就是arch/i386/mm/fault.c 。

Linux内核源代码导读

Linux内核源代码导读

4.1 实模式setup阶段setup用于体系结构相关的硬件初始化工作,在arch目录中的各个系统结构的平台相关都有类似功能的代码。

在32位的x86平台中,setup的入口点是arch/x86/boot/header.S 中的_start。

代码片段 4.1.节自arch/x86/boot/header.S1 .code162 section ".bstext", "ax"34 .global bootsect_start5 bootsect_start:67 # Normalize the start address8 ljmp $BOOTSEG, $start2910 start2:11 movw %cs, %ax12 movw %ax, %ds13 movw %ax, %es14 movw %ax, %ss15 xorw %sp, %sp16 sti17 cld1819 movw $bugger_off_msg, %si2021 msg_loop:22 lodsb23 andb %al, %al24 jz bs_die25 movb $0xe, %ah26 movw $7, %bx27 int $0x1028 jmp msg_loop29 .......30 .section ".bsdata", "a"31 bugger_off_msg:32 .ascii "Direct booting from floppy is no longer supported\r\n"33 .ascii "Please use a boot loader program instead.\r\n"34 .ascii "\n"35 .ascii "Remove disk and press any key to reboot . . .\r\n"3738 .section ".header", "a"3940 .globl hdr41 hdr:42 setup_sects: .byte SETUPSECTS43 root_flags: .word ROOT_RDONLY44 syssize: .long SYSSIZE45 ram_size: .word RAMDISK46 vid_mode: .word SVGA_MODE47 root_dev: .word ROOT_DEV48 boot_flag: .word 0xAA554950 # offset 512, entry point51 .globl _start52 _start:53 # Explicitly enter this as bytes, or the assembler54 # tries to generate a 3-byte jump here, which causes55 # everything else to push off to the wrong offset.56 .byte 0xeb # short (2-byte) jump57 .byte start_of_setup-1f58 1:59 ......60 code32_start: # here loaders can put a different61 # start address for 32-bit code.62 #ifndef __BIG_KERNEL__63 .long 0x1000 # 0x1000 = default for zImage64 #else65 .long 0x100000 # 0x100000 = default for big kernel66 #endif从第48行可以看出,第一个引导扇区以0x55AA 结尾,现在的内核都需要由Boot Loader 来加载。

Linux操作系统源代码详细分析

Linux操作系统源代码详细分析

Linux操作系统源代码详细分析内容简介:Linux 拥有现代操作系统所有的功能,如真正的抢先式多任务处理、支持多用户,内存保护,虚拟内存,支持SMP、UP,符合POSIX标准,联网、图形用户接口和桌面环境。

具有快速性、稳定性等特点。

本书通过分析Linux的内核源代码,充分揭示了Linux作为操作系统的内核是如何完成保证系统正常运行、协调多个并发进程、管理内存等工作的。

现实中,能让人自由获取的系统源代码并不多,通过本书的学习,将大大有助于读者编写自己的新程序。

第一部分Linux 内核源代码arch/i386/kernel/entry.S 2arch/i386/kernel/init_task.c 8arch/i386/kernel/irq.c 8arch/i386/kernel/irq.h 19arch/i386/kernel/process.c 22arch/i386/kernel/signal.c 30arch/i386/kernel/smp.c 38arch/i386/kernel/time.c 58arch/i386/kernel/traps.c 65arch/i386/lib/delay.c 73arch/i386/mm/fault.c 74arch/i386/mm/init.c 76fs/binfmt-elf.c 82fs/binfmt_java.c 96fs/exec.c 98include/asm-generic/smplock.h 107include/asm-i386/atomic.h 108include/asm-i386/current.h 109include/asm-i386/dma.h 109include/asm-i386/elf.h 113include/asm-i386/hardirq.h 114include/asm-i386/page.h 114include/asm-i386/pgtable.h 115include/asm-i386/ptrace.h 122include/asm-i386/semaphore.h 123include/asm-i386/shmparam.h 124include/asm-i386/sigcontext.h 125include/asm-i386/siginfo.h 125include/asm-i386/signal.h 127include/asm-i386/smp.h 130include/asm-i386/softirq.h 132include/asm-i386/spinlock.h 133include/asm-i386/system.h 137include/asm-i386/uaccess.h 139include/linux/capability.h 147 include/linux/elf.h 150 include/linux/elfcore.h 156 include/linux/interrupt.h 157 include/linux/kernel.h 158 include/linux/kernel_stat.h 159 include/linux/limits.h 160 include/linux/mm.h 160 include/linux/module.h 164 include/linux/msg.h 168 include/linux/personality.h 169 include/linux/reboot.h 169 include/linux/resource.h 170 include/linux/sched.h 171 include/linux/sem.h 179 include/linux/shm.h 180 include/linux/signal.h 181 include/linux/slab.h 184 include/linux/smp.h 184 include/linux/smp_lock.h 185 include/linux/swap.h 185 include/linux/swapctl.h 187 include/linux/sysctl.h 188 include/linux/tasks.h 194 include/linux/time.h 194 include/linux/timer.h 195 include/linux/times.h 196 include/linux/tqueue.h 196 include/linux/wait.h 198init/main.c 198init/version.c 212ipc/msg.c 213ipc/sem.c 218ipc/shm.c 227ipc/util.c 236kernel/capability.c 237 kernel/dma.c 240kernel/exec_domain.c 241 kernel/exit.c 242kernel/fork.c 248kernel/info.c 255kernel/itimer.c 255kernel/kmod.c 257kernel/module.c 259kernel/panic.c 270kernel/sched.c 275kernel/signal.c 295kernel/softirq.c 307kernel/sys.c 307kernel/sysctl.c 318kernel/time.c 330mm/memory.c 335mm/mlock.c 345mm/mmap.c 348mm/mprotect.c 358mm/mremap.c 361mm/page_alloc.c 363mm/page_io.c 368mm/slab.c 372mm/swap.c 394mm/swap_state.c 395mm/swapfile.c 398mm/vmalloc.c 406mm/vmscan.c 409第二部分Linux 内核源代码分析第1章Linux 简介让用户很详细地了解大多数现有操作系统的实际工作方式是不可能的,因为大多数操作系统的源代码都是严格保密的。

Linux 内核2.4版源代码分析大全

Linux 内核2.4版源代码分析大全
4.4.3 启用设备文件系统
4.4.4 如何使传统管理方式依然有效
4.4.5 内核实现综述
4.4.6 核心结构与变量
4.4.7 devfs节点注册函数
4.4.8 编写采用devfs的设备驱动程序
4,5 块设备的请求队列
4.5.1 相关结构及请求队列的初始化
4.6.1 构造ioctl命令字
4.6.2 ioctl的实现过程
4.6.3 ioctl的上层处理函数
4.6.4 ioctl的底层处理函数
4.7 I/O端口的资源分配与操作
4.7.1 I/O端口概述
4.7.2 Linux系统中的I/O空间分配
4.7.3 端口操作函数
4.9.4 设备的使用
4.9.5 驱动程序编写实例
4.10 块设备驱动程序的实现
4.10.1 设备功能
4.10.2 编写块设备的函数接口fops
4.10.3 设备接口注册与初始化
第5章 Linux系统初始化
5.1 系统引导
1,13 系统调用
1.13.1 与系统调用有关的数据结构和
函数
1.13.2 进程的系统调用命令是如何转换为
INT0x80中断请求的
1.13.3 系统调用功能模块的初始化
1.13.4 Linux内部是如何分别为各种系统
调用服务的
4.1.2 与外设的数据交流方
4.1.3 字符设备与块设备
4.1.4 主设备号和次设备号
4.1.5 本章内容分配
4.2 设备文件
4.2.1 基本设备文件的设备访问流程
4.2.2 设备驱动程序接口
4.2.3 块设备文件接口

linux源代码分析

linux源代码分析

linux源代码分析Linux源代码是Linux操作系统的基础,它是开源的,其源代码可以被任何人查看、分析和修改。

Linux源代码的分析对于了解Linux操作系统的原理和机制非常有帮助。

在本文中,我将对Linux源代码进行分析,介绍其结构、特点以及一些常见的模块。

首先,我们来了解一下Linux源代码的目录结构。

Linux源代码的根目录是一个包含各种子目录的层次结构。

其中,arch目录包含了与硬件体系结构相关的代码;block目录包含了与块设备相关的代码;fs目录包含了文件系统相关的代码等等。

每个子目录下又有更详细的子目录,以及各种源代码文件。

Linux源代码的特点之一是它的模块化。

Linux操作系统是由许多独立的模块组成的,每个模块负责完成特定的功能。

这种模块化的设计使得Linux操作系统更容易理解和维护。

例如,网络模块负责处理与网络相关的功能,文件系统模块负责处理文件系统相关的功能,设备驱动程序模块负责处理硬件设备的驱动等等。

通过分析这些模块的源代码,我们能够深入了解Linux操作系统的各个功能组成。

在Linux源代码中,有一些常见的模块是非常重要的,例如进程调度模块、内存管理模块和文件系统模块。

进程调度模块负责为不同的进程分配CPU时间,实现多任务处理能力。

内存管理模块负责管理系统的内存资源,包括内存的分配和释放。

文件系统模块负责处理文件的读写操作,提供文件系统的功能。

通过对这些重要模块的源代码进行分析,我们可以更加全面地了解Linux操作系统的内部工作原理。

除了这些模块以外,Linux源代码还包含了许多其他的功能和模块,例如设备驱动程序、网络协议栈、系统调用等等。

这些模块共同组成了一个完整的操作系统,为用户提供了丰富的功能和服务。

对于分析Linux源代码,我们可以使用一些工具和方法来辅助。

例如,我们可以使用文本编辑器来查看和修改源代码文件,使用编译器来编译和运行代码,使用调试器来调试代码等等。

内核源码分析

内核源码分析

1.基础知识:Linux 核源代码位于/usr/src/linux 目录下,其结构分布如图1所示,每一个目录或子目录可以看作一个模块,其目录之间的连线表示“子目录或子模块”的关系。

图1 Linux 源代码的分布结构2.本文档分析Linux的中断机制部分的实现:1)Linux对8259A中断控制器的初始化,8259A 初始化的目的是写入有关命令字,8259A 内部有相应的寄存器来锁存这些命令字,以控制8259A 工作。

代码在/arch/i386/kernel/i8259.c 的函数init_8259A()中:/* 送数据到工作寄存器OCW1(又称中断屏蔽字), 屏蔽所有外部中断, 因为此时系统尚未初始化完毕, 不能接收任何外部中断请求*/outb(0xff, 0x21);outb(0xff, 0xA1);/*送0x11 到ICW1(通过端口0x20),启动初始化编程。

0x11 表示外部中断请求信号为上升沿有效,系统中有多片8295A 级连,还表示要向ICW4 送数*/outb_p(0x11, 0x20);/* 送0x20 到ICW2,写入高五位作为中断向量的高五位,低3 位根据中断源(管脚)填入中断号0~7,因此把IRQ0-7 映射到向量0x20-0x27 */outb_p(0x20+ 0, 0x21);/* 送0x04 到ICW3,ICW3 是8259 的级连命令字,0x04 表示8259A-1 是主片*/outb_p(0x04, 0x21);/* 用ICW1 初始化8259A-2 */outb_p(0x11, 0xA0);/* 用ICW2 把8259A-2 的IRQ0-7 映射到0x28-0x2f */outb_p(0x20 + 8, 0xA1);/* 送0x04 到ICW3。

表示8259A-2 是从片,并连接在8259A_1 的2 号管脚上*/ outb_p(0x02, 0xA1);/* 把0x01 送到ICW4*/outb_p(0x01, 0xA1);2)中断描述符表IDT 的预初始化第一步:中断描述表寄存器IDTR 的初始化用汇编指令LIDT 对中断向量表寄存器IDTR 进行初始化,其代码在arch/i386/boot/setup.S 中:lidt idt_48 # load idt with0,0…idt_48:.word 0 # idt limit = 0.word 0, 0 # idtbase = 0L第二步:把IDT 表的起始地址装入IDTR用汇编指令LIDT 装入IDT 的大小和它的地址,其代码在arch/i386/kernel/head.S 中:#define IDT_ENTRIES 256#其中idt 为一个全局变量,核对这个变量的引用就可以获得IDT 表的地址。

深入分析Linux内核源码

深入分析Linux内核源码

2.4.1 分页机构如前所述,分页是将程序分成若干相同大小的页,每页4K个字节。

如果不允许分页(CR0的最高位置0),那么经过段机制转化而来的32位线性地址就是物理地址。

但如果允许分页(CR0的最高位置1),就要将32位线性地址通过一个两级表格结构转化成物理地址。

1. 两级页表结构为什么采用两级页表结构呢?在80386中页表共含1M个表项,每个表项占4个字节。

如果把所有的页表项存储在一个表中,则该表最大将占4M字节连续的物理存储空间。

为避免使页表占有如此巨额的物理存储器资源,故对页表采用了两级表的结构,而且对线性地址的高20位的线性—物理地址转化也分为两部完成,每一步各使用其中的10位。

两级表结构的第一级称为页目录,存储在一个4K字节的页面中。

页目录表共有1K个表项,每个表项为4个字节,并指向第二级表。

线性地址的最高10位(即位31~位32)用来产生第一级的索引,由索引得到的表项中,指定并选择了1K个二级表中的一个表。

两级表结构的第二级称为页表,也刚好存储在一个4K字节的页面中,包含1K个字节的表项,每个表项包含一个页的物理基地址。

第二级页表由线性地址的中间10位(即位21~位12)进行索引,以获得包含页的物理地址的页表项,这个物理地址的高20位与线性地址的低12位形成了最后的物理地址,也就是页转化过程输出的物理地址,具体转化过程稍后会讲到,如图 2-21 为两级页表结构。

图2.21 两级页表结构2. 页目录项图 2-22的页目录表,最多可包含1024个页目录项,每个页目录项为4个字节,结构如图4.23所示。

图2.22 页目录中的页目录项·第31~12位是20位页表地址,由于页表地址的低12位总为0,所以用高20位指出32位页表地址就可以了。

因此,一个页目录最多包含1024个页表地址。

·第0位是存在位,如果P=1,表示页表地址指向的该页在内存中,如果P=0,表示不在内存中。

·第1位是读/写位,第2位是用户/管理员位,这两位为页目录项提供硬件保护。

linux操作系统内核版表达方式

linux操作系统内核版表达方式

Linux操作系统内核源代码的表达方式Linux操作系统内核源代码的表达方式是指内核代码中所使用的语法和结构。

它决定了内核代码的可读性、可维护性和可移植性。

内核源代码的表达方式主要包括以下几个方面:语法:Linux内核源代码使用C语言编写。

C语言是一种广泛使用的通用编程语言,具有丰富的库和工具支持,并且很容易移植到不同的平台上。

结构:Linux内核源代码由许多相互关联的文件组成,这些文件包含了内核的各种功能和模块。

内核源代码的结构通常是分层的,每一层都包含了不同的功能。

命名约定:Linux内核源代码使用了严格的命名约定,这使得内核代码更容易阅读和维护。

例如,内核中的函数通常以“sys_”或“do_”开头,而结构体通常以“struct_”开头。

注释:Linux内核源代码中包含了大量的注释,这些注释解释了代码的功能和用法。

注释对于理解内核代码非常有用,尤其是对于新内核开发者来说。

Linux内核源代码的表达方式的特点Linux内核源代码的表达方式具有以下几个特点:简洁:Linux内核源代码非常简洁,代码中没有多余的注释和代码段。

这种简洁性使得内核代码更容易阅读和维护。

模块化:Linux内核源代码是模块化的,内核中的各个模块可以独立编译和加载。

这种模块化设计使得内核更容易扩展和维护。

可移植性:Linux内核源代码具有很强的可移植性,它可以移植到不同的硬件平台上。

这种可移植性使得Linux内核成为世界上最受欢迎的操作系统之一。

Linux内核源代码的表达方式的优点Linux内核源代码的表达方式具有以下几个优点:可读性:Linux内核源代码非常可读,代码中的注释和命名约定使得内核代码很容易理解。

这种可读性对于内核开发者来说非常重要,它可以帮助开发者快速理解和修改内核代码。

可维护性:Linux内核源代码也非常可维护,代码中的模块化设计使得内核更容易扩展和维护。

这种可维护性对于内核的长期发展非常重要,它可以确保内核能够不断更新和改进。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

Linux内核源代码解读!!悬赏分:5 - 提问时间2007-1-24 16:28 问题为何被关闭赵炯书中,Bootsect代码中有mov ax , #BOOTSEG 等我曾自学过80x86汇编,没有见过#的用法,在这为什么要用#?另外,JMPI 的用法是什么?与JMP的区别是什么?提问者: Linux探索者 - 一级答复共 1 条检举系统初始化程序 boot.s 的分析 [转]系统初始化程序 boot.s 的分析:阚志刚,2000/03/20下午,在前人的基础之上进行整理完善******************************************************************************** **************boot.s is loaded at 0x7c00 by the bios-startup routines, and moves itself out of the way to address 0x90000, and jumps there.当PC 机启动时,Intel系列的CPU首先进入的是实模式,并开始执行位于地址0xFFF0处的代码,也就是ROM-BIOS起始位置的代码。

BIOS先进行一系列的系统自检,然后初始化位于地址0的中断向量表。

最后BIOS将启动盘的第一个扇区装入0x7C00(31K;0111,1100,0000,0000),并开始执行此处的代码。

这就是对内核初始化过程的一个最简单的描述。

最初,Linux核心的最开始部分是用8086汇编语言编写的。

当开始运行时,核心将自己装入到绝对地址0x90000(576K; 1001,0000,0000,0000,0000),再将其后的2k字节装入到地址0x90200(576.5k;1001,0000,0010,0000,0000)处,最后将核心的其余部分装入到0x10000(64k; 1,0000,0000,0000,0000).It then loads the system at 0x10000, using BIOS interrupts. Thereafter it disables all interrupts, moves the system down to 0x0000, changes to protected mode, and calls the start of system. System then must RE-initialize the protected mode in it's own tables, and enable interrupts as needed.然后,关掉所有中断,把系统下移到0x0000(0k;0000,0000,0000,0000,0000)处,改变到保护模式,然后开始系统的运行.系统必须重新在保护模式下初始化自己的系统表格,并且打开所需的中断.NOTE 1! currently system is at most 8*65536(8*64k=512k; 1000,0000,0000,0000,0000) bytes long. This should be no problem, even in the future. I want to keep it simple. This 512 kB kernel size should be enough - in fact more would mean we'd have to move not just these start-up routines, but also do something about the cache-memory(block IO devices). The area left over in the lower 640 kB (0xA0000;1010,0000,0000,0000,0000) is meant for these. No other memory is assumed to be "physical", ie all memory over 1Mb is demand-paging. All addresses under 1Mb are guaranteed to match their physical addresses.NOTE1 abouve is no longer valid in it's entirety. cache-memory is allocated above the 1Mb mark as well as below. Otherwise it is mainly correct.NOTE 2! The boot disk type must be set at compile-time, by setting the following equ. Having the boot-up procedure hunt for the right disk type is severe brain-damage. The loader has been made as simple as possible (had to, to get it in 512 bytes with the code to move to protected mode), and continuos read errors will result in a unbreakable loop. Reboot by hand. It loads pretty fast by getting whole sectors at a time whenever possible.| 1.44Mb disks: sectors = 18| 1.2Mb disks:| sectors = 15| 720kB disks:| sectors = 9******************************************************************************** **************.globl begtext, begdata, begbss, endtext, enddata, endbss.textbegtext:.databegdata:.bssbegbss:.textBOOTSEG = 0x07c0|把第一个扇区装入到此处INITSEG = 0x9000|核心装入地址的段地址SYSSEG = 0x1000|system loaded at 0x10000 (65536).ENDSEG = SYSSEG + SYSSIZE|SYSSIZE在Makefile中定义的 ^_^entry startstart:mov ax,#BOOTSEG| BOOTSEG = 0x07C0;现在应仍处在REAL MODE下.mov ds,ax| 移动自身从BOOTSEG:0000到INITSEG:0000mov ax,#INITSEG| 共512字节.mov es,ax| 那么BOOT.S处在0x90000-0x90200.mov cx,#256sub si,si| 寄存器清零sub di,di| 寄存器清零repmovw| 将由SI作为指针的源串中的一个字节或字或双字传送到由DI作为指针的目的串中,并根据标志DF值自动修| 改这两个指针以指向串中下一项。

若带有前缀REP,则重复执行这一传送,直到CX寄存器等于零为止。

| 源地址:DS=0x07C0(31K;0111,1100,0000,0000);| 目的地址:ES=0x9000(576K;1001,0000,0000,0000,0000)| 小结:BIOS将启动盘的第一个扇区调入到0x07C00处,然后系统把自己从0x07C00处移动到0x90000-| 0x90200处.jmpi go,INITSEGgo: mov ax,csmov ds,axmov es,ax| 将DS,ES,SS均设为0x9000,所有数据都以| 0x9000为段偏移.mov ss,ax| 堆栈偏移0x9000mov sp,#0x400| 栈顶指针0x9000:0x0400,堆栈空间512bytes??mov ah,#0x03| read cursor posxor bh,bhint 0x10mov cx,#24mov bx,#0x0007| page 0, attribute 7 (normal)mov bp,#msg1| 显示Loading System ...mov ax,#0x1301| write string, move cursorint 0x10| ok, we've written the message, now| we want to load the system (at 0x10000)| 我们已经完成了“Loading...”的在屏幕上显示,| 以下我们将完成把核心从0x10000(64k)移到0x01000(4k)处.mov ax,#SYSSEG| SYSSEG = 0x1000mov es,ax| segment of 0x010000call read_it| 读内核到0x10000call kill_motor| 杀了软驱!? ^_^| if the read went well we get current cursor position ans save it for | posterity.mov ah,#0x03| read cursor posxor bh,bhint 0x10| save it in known place, con_init fetchesmov [510],dx| it from 0x90510(1001,0000,0101,0001,0000).| 功能03H,读取光标位置和类型。

AH=03H,BH=页号。

| 返回:DH=当前字符行号; DL=当前字符列号| CH=光标的起始光栅线;CL=光标的终止光栅线| now we want to move to protected mode ...cli| 关掉所有中断| first we move the system to it's rightful placemov ax,#0x0000cld| 'direction'=0, movs moves forwarddo_move:mov es,ax| ES=0x0000;destination segmentadd ax,#0x1000cmp ax,#0x9000jz end_movemov ds,ax| DS=0x1000;source segmentsub di,di| 置零,地址为0x1000:0000sub si,si| 置零,地址为0x9000:0000mov cx,#0x8000| cx的作用是计数器,共0x8000=32krepmovswj do_move| 将位于低端0x1000:0000的内核移到内存| 高端0x9000:0000,覆盖了boot.S !?| then we load the segment descriptorsend_move:mov ax,cs| right, forgot this at first. didn't work :-) mov ds,axlidt idt_48| idt_48和gdt_48都是一个3个word长的数据结构 lgdt gdt_48| 第一个字说明(Global || Interrupt) Descriptor | Table有多长,因为每个Table是四个字长,所以| 可以得出整个DescriptorTable的entries.(见下) | 后两个字指出DT的具体位置.| idt_48是0,0,0;应表示没有中断描述符entries. | gdt_48有256个入口,第一个是个空入口,然后| 定义了一个code段和一个data段.基址都是| 0x00000000, !?那里是什么东西???| *** 0x00000000 != 0x0000:0000 ***| that was painless, now we enable A20call empty_8042mov al,#0xD1| command writeout #0x64,alcall empty_8042mov al,#0xDF| A20 onout #0x60,alcall empty_8042| well, that went ok, I hope. Now we have to reprogram the interrupts :-(| we put them right after the intel-reserved hardware interrupts, at| int 0x20-0x2F. There they won't mess up anything. Sadly IBM really| messed this up with the original PC, and they haven't been able to| rectify it afterwards. Thus the bios puts interrupts at 0x08-0x0f,| which is used for the internal hardware interrupts as well. We just| have to reprogram the 8259's, and it isn't fun.| 初始化中断处理器8259i| 初始化顺序为: 1. 向主8259A写ICW1, 0x20| 2. 向第二块8259A写ICW1, 0xA0| 3. 向主8259A写ICW2, 0x21| 4. 向第二块8259A写ICW2, 0xA1| 5. 如果ICW1指示有级联中断处理器,则初始化Master&Slave | (在下例中只有IR2有级联8259A), 0x21, 0xA1| 6. 向两块8259写ICW4,指定工作模式.| 输入了适当的初始化命令之后, 8259已经准备好接收中断请求.| 现在向他输入工作| 命令字以规定其工作方式. 8259A共有三个工作命令字,但下例中只用过OCW1.| OCW1将所有的中断都屏蔽掉, OCW2&OCW3也就没什么意义了.| ** ICW stands for Initialization Command Word;| OCW for Operation Command Word.1. mov al,#0x11out #0x20,al.word 0x00eb,0x00eb | jmp +2, jmp +22. out #0xA0,al | and to 8259A-2.word 0x00eb,0x00eb3. mov al,#0x20 | 向主8259A写入ICW2.out #0x21,al | 硬件中断入口地址0x20, 并由ICW1| 得知中断向量长度 = 8 bytes..word 0x00eb,0x00eb4. mov al,#0x28 | start of hardware int's 2 (0x28)out #0xA1,al | 第二块8259A的中断入口是0x28..word 0x00eb,0x00eb5. mov al,#0x04 | 8259-1 is masterout #0x21,al | Interrupt Request 2有级联处理..word 0x00eb,0x00ebmov al,#0x02 | 8259-2 is slaveout #0xA1,al | 于上面对应,告诉大家我就是IR2对应| 级联处理器..word 0x00eb,0x00eb6. mov al,#0x01 | 8086 mode for bothout #0x21,al.word 0x00eb,0x00ebout #0xA1,al.word 0x00eb,0x00ebmov al,#0xFF | mask off all interrupts for nowout #0x21,al.word 0x00eb,0x00ebout #0xA1,al| well, that certainly wasn't fun :-(. Hopefully it works, and we don't| need no steenking BIOS anyway (except for the initial loading :-).| The BIOS-routine wants lots of unnecessary data, and it's less| "interesting" anyway. This is how REAL programmers do it.|| Well, now's the time to actually move into protected mode. To make| things as simple as possible, we do no register set-up or anything,| we let the gnu-compiled 32-bit programs do that. We just jump to| absolute address 0x00000, in 32-bit protected mode.mov ax,#0x0001 | protected mode (PE) bitlmsw ax | This is it!jmpi 0,8 | jmp offset 0 of segment 8 (cs)******************************************************************************** *| This routine checks that the keyboard command queue is empty| No timeout is used - if this hangs there is something wrong with| the machine, and we probably couldn't proceed anyway.empty_8042:.word 0x00eb,0x00ebin al,#0x64| 8042 status porttest al,#2| is input buffer full?jnz empty_8042| yes - loopret******************************************************************************** *| This routine loads the system at address 0x10000, making sure| no 64kB boundaries are crossed. We try to load it as fast as| possible, loading whole tracks whenever we can.|| in: es - starting address segment (normally 0x1000)|| This routine has to be recompiled to fit another drive type,| just change the "sectors" variable at the start of the file| (originally 18, for a 1.44Mb drive)|sread: .word 1 | sectors read of current trackhead: .word 0 | current headtrack: .word 0 | current track**read-it子函数********************************************************************read_it:mov ax,es| ES当前应0x1000,对,是这样的!test ax,#0x0fff| 目的操作数与源操作数进行逻辑与操作,结果只反映在标志位上,对两个操作数无影响| 必需确保ES处在64KB段边界上,即0x?000:XXXX.| 要不你就会收到一个"DMA..."什么什么的ERR.die: jne die| jne:不相等/不等于零时转移,ZF=0| es must be at 64kB boundaryxor bx,bx| bx is starting address within segmentrp_read:| **** 循环入口处 ****mov ax,escmp ax,#ENDSEG| have we loaded all yet?jb ok1_read| ax<#ENDSEG时转移retok1_read:mov ax,#sectors| 1.44M, sectors=18,linux的后续版本| 中已改成由操作系统来探测sectors的值.sub ax,sread| AX内记载需要读的扇区数,初始sread为1,| 即跳过第一道的第一扇区(BOOT区)mov cx,axshl cx,#9| CX左移9位,相当于:CX*512=17*512=8704字节| CX算出需要读出的扇区的字节数, ax*512.add cx,bx| BX是当前段内偏移.| 下面连续的两个转移指令开始还真让人莫名其妙.jnc ok2_read| jnc:无进位(或借位)时转移;CF=0| 不要超过64k| 这里先检查当前段内的空间够不够装ax个扇区| cx算出字节数,加上当前偏移试试,够了的话,就| 跳到ok2_read去读吧!je ok2_read| 这么巧的事也有,刚刚够! 读!| 如果到了这里就确认溢出了,看下面的:xor ax,axsub ax,bxshr ax,#9| 这段代码我觉得很精巧.| shr指令执行逻辑右移操作。

相关文档
最新文档