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内核源代码也非常可维护,代码中的模块化设计使得内核更容易扩展和维护。

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

Linux源代码分析

Linux源代码分析

Linux源代码分析Linux内核(2.6.13.2)源代码分析苗彦超摘要:1系统启动1.1汇编代码head.S及以前设置CPU状态初值,创建进程0,建立进程堆栈:movq init_rsp(%rip), %rsp,init_rsp定义.globl init_rspinit_rsp:.quad init_thread_union+THREAD_SIZE-8即将虚地址init_thread_union+THREAD_SIZE-8作为当前进程(进程0)核心空间堆栈栈底,init_thread_union定义于文件arch/x86_64/kernel/init_task.c中:union thread_union init_thread_union __attribute__((__section__(".data.init_task"))) ={INIT_THREAD_INFO(init_task)};INIT_THREAD_INFO定义于文件include/asm-x86_64/thread_info.h中,初始化init_thread_union.task = &init_task,init_task同样定义于文件init_task.c中,初始化为:struct task_struct init_task = INIT_TASK(init_task);INIT_TASK宏在include/linux/init_task.h中定义。

全部利用编译时静态设置的初值,将进程0的控制结构设置完成,使进程0可以按普通核心进程访问。

init_task.mm = NULL; init_task.active_mm = INIT_MM(init_mm),init_/doc/967617097.html,m = “swapper”INIT_MM将init_mm.pgd初始化为swapper_pg_dir,即init_level4_pgt,定义与head.S中。

Linux0.01内核源代码及注释

Linux0.01内核源代码及注释

Bootsect.s(1-9)!! SYS_SIZE is the number of clicks (16 bytes) to be loaded.! 0x3000 is 0x30000 bytes = 196kB, more than enough for current! versions of linux ! SYS_SIZE 是要加载的节数(16 字节为1 节)。

0x3000 共为1 2 3 4 5 60x7c000x00000x900000x100000xA0000system 模块代码执行位置线路0x90200! 0x30000 字节=192 kB(上面Linus 估算错了),对于当前的版本空间已足够了。

!SYSSIZE = 0x3000 ! 指编译连接后system 模块的大小。

参见列表1.2 中第92 的说明。

! 这里给出了一个最大默认值。

!! bootsect.s (C) 1991 Linus Torvalds!! bootsect.s is loaded at 0x7c00 by the bios-startup routines, and moves! iself out of the way to address 0x90000, and jumps there.!! It then loads 'setup' directly after itself (0x90200), and the system! at 0x10000, using BIOS interrupts.!! NOTE! currently system is at most 8*65536 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, especially as this doesn't contain the! buffer cache as in minix!! The loader has been made as simple as possible, 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.!! 以下是前面这些文字的翻译:! bootsect.s (C) 1991 Linus Torvalds 版权所有!! bootsect.s 被bios-启动子程序加载至0x7c00 (31k)处,并将自己! 移到了地址0x90000 (576k)处,并跳转至那里。

Linux内核源代码(free)

Linux内核源代码(free)
内核版本 发行版本
Linux系统的好处 Linux的运行及相关基本概念
什么是Linux?
Linux是一个类Unix(Unix-like)的操作系统, 在1991年发行了它的第一个版本 在Linux内核维护网站上,“What is Linux?”
Portable Operating System Interface Standard 可移植操作系统接口标准 由IEEE制订,并由ISO接受为国际标准。 Institute for Electrical and Electronic Engineers 电气电子工程师学会[美] International Organization for Standardization 国际标准化组织 制定各行各业各种产品和服务的技术规范(国际标准)
Linux简介
什么是Linux? “Linux”在不同的语境下的含义 Linux发展简史 Linux操作系统的主要内容 Linux版本
内核版本 发行版本
Linux系统的好处 Linux的运行及相关基本概念
“Linux”
在不同的语境下,“Linux”具有不同的内涵,例 如:
Linux发展简史
1991年11月,芬兰赫尔辛基大学的学生 Linus Torvalds写了个小程序,后来取名为Linux,放在 互联网上。他表达了一个愿望,希望借此搞出一 个操作系统的“内核”来,这完全是一个偶然事 件 1993,在一批高水平黑客的参与下,诞生了Linux 1.0 版 1994年,Linux 的第一个商业发行版 Slackware 问 世
基于I386的Linux使用int 0x80进行系统调用
I386系统的基本概念
代码的运行 堆栈的概念 内核态与用户态 中断/异常/系统调用 虚拟内存

Linux内核源码及其分析

Linux内核源码及其分析

深入分析Linux内核源码前言第一章走进linux1.1 GNU与Linux的成长1.2 Linux的开发模式和运作机制1.3走进Linux内核1.3.1 Linux内核的特征1.3.2 Linux内核版本的变化1.4 分析Linux内核的意义1.4.1 开发适合自己的操作系统1.4.2 开发高水平软件1.4.3 有助于计算机科学的教学和科研1.5 Linux内核结构1.5.1 Linux内核在整个操系统中的位置1.5.2 Linux内核的作用1.5.3 Linux内核的抽象结构1.6 Linux内核源代码1.6.1 多版本的内核源代码1.6.2 Linux内核源代码的结构1.6.3 从何处开始阅读源代码1.7 Linux内核源代码分析工具1.7.1 Linux超文本交叉代码检索工具1.7.2 Windows平台下的源代码阅读工具Source Insight第二章Linux运行的硬件基础2.1 i386的寄存器2.1.1通用寄存器2.1.2段寄存器2.1.3状态和控制寄存器2.1.4 系统地址寄存器2.1.5 调试寄存器和测试寄存器2.2 内存地址2.3 段机制和描述符2.3.1 段机制2.3.2 描述符的概念2.3.3系统段描述符2.3.4 描述符表2.3.5 选择符与描述符表寄存器2.3.6 描述符投影寄存器2.3.7 Linux中的段2.4 分页机制2.4.1 分页机构2.4.2页面高速缓存2.5 Linux中的分页机制2.5.1 与页相关的数据结构及宏的定义2.5.2 对页目录及页表的处理2.6 Linux中的汇编语言2.6.1 A T&T与Intel汇编语言的比较2.6.2 A T&T汇编语言的相关知识2.6.3 Gcc嵌入式汇编2.6.4 Intel386汇编指令摘要第三章中断机制3.1 中断基本知识3.1.1 中断向量3.1.2 外设可屏蔽中断3.1.3异常及非屏蔽中断3.1.4中断描述符表3.1.5 相关汇编指令3.2中断描述符表的初始化3.2. 1 外部中断向量的设置3.2.2中断描述符表IDT的预初始化3.2.3 中断向量表的最终初始化3.3异常处理3.3.1 在内核栈中保存寄存器的值3.3.2 中断请求队列的初始化3.3.3中断请求队列的数据结构3.4 中断处理3.4.1中断和异常处理的硬件处理3.4.2 Linux对异常和中断的处理3.4.3 与堆栈有关的常量、数据结构及宏3.4.4 中断处理程序的执行3.4.5 从中断返回3.5中断的后半部分处理机制3.5.1 为什么把中断分为两部分来处理3.5.2 实现机制3.5.3数据结构的定义3.5.4 软中断、bh及tasklet的初始化3.5.5后半部分的执行3.5.6 把bh移植到tasklet第四章进程描述4.1 进程和程序(Process and Program)4.2 Linux中的进程概述4.3 task_struct结构描述4.4 task_struct结构在内存中的存放4.4.1 进程内核栈4.4.2 当前进程(current宏)4.5 进程组织的方式4.5.1哈希表4.5.2双向循环链表4.5.3 运行队列4.5.4 等待队列4.6 内核线程4.7 进程的权能4.8 内核同步4.8.1信号量4.8.2原子操作4.8.3 自旋锁、读写自旋锁和大读者自旋锁4.9 本章小节第五章进程调度5.1 Linux时间系统5.1.1 时钟硬件5.1.2 时钟运作机制5.1.3 Linux时间基准5.1.4 Linux的时间系统5.2 时钟中断5.2.1 时钟中断的产生5.2.2.Linux实现时钟中断的全过程5.3 Linux的调度程序-Schedule( )5.3.1 基本原理5.3.2 Linux进程调度时机5.3.3 进程调度的依据5.3.4 进程可运行程度的衡量5.3.5 进程调度的实现5.4 进程切换5.4.1 硬件支持5.4.2 进程切换第六章Linux内存管理6.1 Linux的内存管理概述6.1.1 Linux虚拟内存的实现结构6.1.2 内核空间和用户空间6.1.3 虚拟内存实现机制间的关系6.2 Linux内存管理的初始化6.2.1 启用分页机制6.2.2 物理内存的探测6.2.3 物理内存的描述6.2.4 页面管理机制的初步建立6.2.5页表的建立6.2.6内存管理区6.3 内存的分配和回收6.3.1 伙伴算法6.3.2 物理页面的分配和释放6.3.3 Slab分配机制6.4 地址映射机制6.4.1 描述虚拟空间的数据结构6.4.2 进程的虚拟空间6.4.3 内存映射6.5 请页机制6.5.1 页故障的产生6.5.2 页错误的定位6.5.3 进程地址空间中的缺页异常处理6.5.4 请求调页6.5.5 写时复制6.6 交换机制6.6.1 交换的基本原理6.6.2 页面交换守护进程kswapd6.6.3 交换空间的数据结构6.6.4 交换空间的应用6.7 缓存和刷新机制6.7.1 Linux使用的缓存6.7.2 缓冲区高速缓存6.7.3 翻译后援存储器(TLB)6.7.4 刷新机制6.8 进程的创建和执行6.8.1 进程的创建6.8.2 程序执行6.8.3 执行函数第七章进程间通信7.1 管道7.1.1 Linux管道的实现机制7.1.2 管道的应用7.1.3 命名管道(FIFO)7.2 信号(signal)7.2.1 信号种类7.2.2 信号掩码7.2.3 系统调用7.2.4 典型系统调用的实现7.2.5 进程与信号的关系7.2.6 信号举例7.3 System V 的IPC机制7.3.1 信号量7.3.2 消息队列7.3.3 共享内存第八章虚拟文件系统8.1 概述8.2 VFS中的数据结构8.2.1 超级块8.2.2 VFS的索引节点8.2.3 目录项对象8.2.4 与进程相关的文件结构8.2.5 主要数据结构间的关系8.2.6 有关操作的数据结构8.3 高速缓存8.3.1 块高速缓存8.3.2 索引节点高速缓存8.3.3 目录高速缓存8.4 文件系统的注册、安装与拆卸8.4.1 文件系统的注册8.4.2 文件系统的安装8.4.3 文件系统的卸载8.5 限额机制8.6 具体文件系统举例8.6.1 管道文件系统pipefs8.6.2 磁盘文件系统BFS8.7 文件系统的系统调用8.7.1 open 系统调用8.7.2 read 系统调用8.7.3 fcntl 系统调用8 .8 Linux2.4文件系统的移植问题第九章Ext2文件系统9.1 基本概念9.2 Ext2的磁盘布局和数据结构9.2.1 Ext2的磁盘布局9.2.2 Ext2的超级块9.2.3 Ext2的索引节点9.2.4 组描述符9.2.5 位图9.2.6 索引节点表及实例分析9.2.7 Ext2的目录项及文件的定位9.3 文件的访问权限和安全9.4 链接文件9.5 分配策略9.5.1 数据块寻址9.5.2 文件的洞9.5.3 分配一个数据块第十章模块机制10.1 概述10.1.1 什么是模块10.1.2 为什么要使用模块?10.2 实现机制10.2.1 数据结构10.2.2 实现机制的分析10.3 模块的装入和卸载10.3.1 实现机制10.3.2 如何插入和卸载模块10.4 内核版本10.4.1 内核版本与模块版本的兼容性10.4.2 从版本2.0到2.2内核API的变化10.4.3 把内核2.2移植到内核2.410.5 编写内核模块10.5.1 简单内核模块的编写10.5.2 内核模块的Makefiles文件10.5.3 内核模块的多个文件第十一章设备驱动程序11.1 概述11.1.1 I/O软件11.1.2 设备驱动程序11.2 设备驱动基础11.2.1 I/O端口11.2.2 I/O接口及设备控制器11.2.3 设备文件11.2.4 VFS对设备文件的处理11.2.5 中断处理11.2.6 驱动DMA工作11.2.7 I/O 空间的映射11.2.8 设备驱动程序框架11.3 块设备驱动程序11.3.1 块设备驱动程序的注册11.3.2 块设备基于缓冲区的数据交换11.3.3 块设备驱动程序的几个函数11.3.4 RAM 盘驱动程序的实现11.3.5 硬盘驱动程序的实现11.4 字符设备驱动程序11.4.1 简单字符设备驱动程序11.4.2 字符设备驱动程序的注册11.4.3 一个字符设备驱动程序的实例11.4.4 驱动程序的编译与装载第十二章网络12.1 概述12.2 网络协议12.2.1 网络参考模型12.2.2 TCP/IP协议工作原理及数据流12.2.3 Internet 协议12.2.4 TCP协议12.3 套接字(socket)12.3.1 套接字在网络中的地位和作用12.3.2 套接字接口的种类12.3.3 套接字的工作原理12.3.4 socket 的通信过程12.3.5 socket为用户提供的系统调用12.4 套接字缓冲区(sk_buff)12.4.1 套接字缓冲区的特点12.4.2 套接字缓冲区操作基本原理12.4.3 sk_buff数据结构的核心内容12.4.4 套接字缓冲区提供的函数12.4.5 套接字缓冲区的上层支持例程12.5 网络设备接口12.5.1 基本结构12.5.2 命名规则12.5.3 设备注册12.5.4 网络设备数据结构12.5.5 支持函数第十三章启动系统13.1 初始化流程13.1.1 系统加电或复位13.1.2 BIOS启动13.1.3 Boot Loader13.1.4 操作系统的初始化13.2 初始化的任务13.2.1 处理器对初始化的影响13.2.2 其他硬件设备对处理器的影响13.3 Linux 的Boot Loarder13.3.1 软盘的结构13.3.2 硬盘的结构13.3.3 Boot Loader13.3.4 LILO13.3.5 LILO的运行分析13.4 进入操作系统13.4.1 Setup.S13.4.2 Head.S13.5 main.c中的初始化13.6 建立init进程13.6.1 init进程的建立13.6.2 启动所需的Shell脚本文件附录:1 Linux 2.4内核API2.1驱动程序的基本函数2.2 双向循环链表的操作2.3 基本C库函数2.4 Linux内存管理中Slab缓冲区2.5 Linux中的VFS2.6 Linux的连网2.7 网络设备支持2.8 模块支持2.9 硬件接口2.10 块设备2.11 USB 设备2 参考文献前言Linux内核全部源代码是一个庞大的世界,大约有200多万行,占60MB左右的空间。

linux 内核源码需要掌握的数据结构和算法

linux 内核源码需要掌握的数据结构和算法

linux 内核源码需要掌握的数据结构和算法在深入理解Linux内核源码的过程中,掌握数据结构和算法是非常重要的。

数据结构和算法是编程和系统编程的基础,也是理解Linux内核源码的关键。

本文将介绍Linux内核源码需要掌握的一些常见数据结构和算法,帮助读者更好地理解内核源码。

一、数据结构1.数组:Linux内核源码中经常使用数组来存储固定大小的元素。

数组在内核源码中主要用于存储数据结构(如链表、树、图等)的元素。

2.链表:链表是一种常见的数据结构,它由一系列节点组成,每个节点包含数据和指向下一个节点的指针。

在Linux内核源码中,链表常用于实现内存管理、文件系统、网络协议等。

3.树:树是一种由节点和边组成的图形结构,其中每个节点最多只有两个子节点。

在Linux内核源码中,树常用于进程调度、内存管理、文件系统等。

4.二叉树:二叉树是一种特殊的树结构,每个节点最多只有两个子节点,通常称为根、左子节点和右子节点。

在Linux内核源码中,二叉树常用于维护设备树、路由表等。

5.图:图是由节点和边组成的图形结构,其中每个节点可以有多个相邻节点。

在Linux内核源码中,图常用于网络协议、进程间通信等。

6.哈希表:哈希表是一种基于哈希函数的数据结构,它可以快速查找、插入和删除元素。

在Linux内核源码中,哈希表常用于进程调度、内存管理等。

二、算法1.遍历算法:遍历算法是用于遍历数据结构的算法,如深度优先搜索(DFS)、广度优先搜索(BFS)等。

这些算法在Linux内核源码中常用于遍历链表、树、图等数据结构。

2.排序算法:排序算法是用于将数据元素按照一定顺序排列的算法,如冒泡排序、快速排序等。

在Linux内核源码中,排序算法常用于维护内存分配表、设备驱动等。

3.查找算法:查找算法是用于在数据结构中查找特定元素的算法,如线性查找、二分查找等。

在Linux内核源码中,查找算法常用于设备驱动、内存管理等。

4.递归算法:递归算法是一种通过函数自我调用来解决问题的方法。

linux内核源码介绍

linux内核源码介绍

Linux的内核源代码Linux的内核源代码可以从很多途径得到,我一般常常去看看。

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

源码的版本号有一个非常简单的编号约定:任何偶数的核心(例如2.0.30)都是一个稳定地发行的核心,而任何奇数的核心(例如 2.1.42)都是一个开发中的核心。

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

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

核心源程序的文件按树形结构进行组织,在源程序树的最上层你会看到这样一些目录:· Arch :arch子目录包括了所有和体系结构相关的核心代码。

它的每一个子目录都代表一种支持的体系结构,例如i386就是关于intel cpu及与之相兼容体系结构的子目录。

PC机一般都基于此目录;· Include: include子目录包括编译核心所需要的大部分头文件。

与平台无关的头文件在 include/linux 子目录下,与 intel cpu相关的头文件在include/asm-i386子目录下,而include/scsi目录则是有关 scsi设备的头文件目录;· Init:这个目录包含核心的初始化代码(注:不是系统的引导代码),包含两个文件main.c和Version.c,这是研究核心如何工作的一个非常好的起点。

· Mm :这个目录包括所有独立于 cpu 体系结构的内存管理代码,如页式存储管理内存的分配和释放等;而和体系结构相关的内存管理代码则位于arch/*/mm/,例如arch/i386/mm/Fault.c· Kernel:主要的核心代码,此目录下的文件实现了大多数linux系统的内核函数,其中最重要的文件当属sched.c;同样,和体系结构相关的代码在arch/*/kernel中;· Drivers:放置系统所有的设备驱动程序;每种驱动程序又各占用一个子目录:如,/block 下为块设备驱动程序,比如ide(ide.c)。

Linux内核调试机制源代码分析

Linux内核调试机制源代码分析

kimage_entry_t *entry; kimage_entry_t *last_entry; unsigned long destination; unsigned long start; struct page *control_code_page; struct page *swap_page; unsigned long nr_segments; struct kexec_segment segment[KEXEC_SEGMENT_MAX]; /*段数组*/ struct list_head control_pages; struct list_head dest_pages; struct list_head unuseable_pages; /* 分配给崩溃内核的下一个控制页的地址*/ unsigned long control_page; /* 指定特殊处理的标识*/ unsigned int type : 1; #define KEXEC_TYPE_DEFAULT 0 #define KEXEC_TYPE_CRASH 1 unsigned int preserve_context : 1; };
内核 kexec 接口函数说明如下:
extern void machine_kexec(struct kimage *image); /*启动内核映像*/ extern int machine_kexec_prepare(struct kimage *image); /*建立内核映 像所需要的控制页*/ extern void machine_kexec_cleanup(struct kimage *image); extern asmlinkage long sys_kexec_load(unsigned long entry, unsigned long nr_segments, struct kexec_segment __user *segments, unsigned long flags); /*装 载内核的系统调用*/ extern int kernel_kexec(void); /*启动内核*/

[重点]linux源码分析

[重点]linux源码分析

[重点]linux源码分析linux源码分析Linux内核源代码中的C语言代码Linux 内核的主体是以 GNU的 C 语言编写的,GNU为此提供了编译工具gcc。

GNU对 C 语言本身(在 ANSI C 基础上)做了不少扩充,可能是读者尚未见到过的。

另一方面,由于内核代码,往往会用到一些在应用程序设计中不常见的语言成分或编程技巧,也许使读者感到陌生。

本书并非介绍 GNU C语言的专著,也非技术手册,所以不在这里一一列举和详细讨论这些扩充和技巧。

再说,离开具体的情景和上下文,罗列一大堆规则,对于读者恐怕也没有多大帮助。

所以,我们在这里只是对可能会影响读者阅读 Linux 内核源程序,或使读者感到困惑的一些扩充和技巧先作一些简单的介绍。

以后,随着具体的情景和代码的展开,在需要时还会结合实际加以补充。

首先,gcc 从 C++语言中吸收了“inline”和“const”。

其实,GNU 的 C 和C++是合为一体的,gcc既是 C 编译又是 C++编译,所以从 C++中吸收一些东西到 C 中是很自然的。

从功能上说,inline 函数的使用与#define 宏定义相似,但更有相对的独立性,也更安全。

使用 inline函数也有利于程序调试。

如果编译时不加优化,则这些inline 就是普通的、独立的函数,更便于调试。

调试好了以后,再采用优化重新编译一次,这些 inline函数就像宏操作一样融入了引用处的代码中,有利于提高运行效率。

由于 inline 函数的大量使用,相当一部分的代码从.c 文件移入了.h 文件中。

还有,为了支持 64 位的CPU结构(Alpha 就是 64 位的),gcc 增加了一种新的基本数据类型“longlong int”,该类型在内核代码中常常用到。

许多 C 语言都支持一些“属性描述符”(attribute),如“aligned”、“packed”等等;gcc 也支持不少这样的描述符。

这些描述符的使用等于是在 C 语言中增加了一些新的保留字。

Linux内核源码分析

Linux内核源码分析

Linux内核源码分析Linux内核在启动的时候需要一些参数,以获得当前硬件的信息或者启动所需资源在内存中的位置等等。

这些信息可以通过bootloader 传递给内核,比较常见的就是cmdline。

以前我在启动内核的时候习惯性的通过uboot传递一个cmdline给内核,没有具体的分析这个过程。

最近在分析内核启动过程的时候,重新看了一下内核启动参数的传递过程,彻底解决一下在这方面的疑惑。

一、bootloader与内核的通讯协议内核的启动参数其实不仅仅包含在了cmdline中,cmdline不过是bootloader传递给内核的信息中的一部分。

bootloader和内核的通信方式根据构架的不同而异。

对于ARM构架来说,启动相关的信息可以通过内核文档(Documentation/arm/Booting)获得。

其中介绍了bootloader与内核的通信协议,我简单总结如下:(1)数据格式:可以是标签列表(tagged list)或设备树(device tree)。

(2)存放地址:r2寄存器中存放的数据所指向的内存地址。

在我所做过的开发中,都是使用tagged list的,所以下面以标签列表为例来介绍信息从bootloader(U-boot)到内核(Linux-3.0)的传递过程。

内核文档对此的说明,翻译摘要如下:1.4a. 设置内核标签列表2.--------------------------------3.4.bootloader必须创建和初始化内核标签列表。

一个有效的标签列表以ATAG_CORE标签开始,且以ATAG_NONE标签结束。

ATAG_CORE标签可以是空的,也可以是非空。

一个空ATAG_CORE标签其 size 域设置为 '2' (0x00000002)。

ATAG_NONE标签的 size 域必须设置为 '0'。

5.6.在列表中可以保存任意数量的标签。

linux启动内核源码分析

linux启动内核源码分析

linux启动内核源码分析内核的启动时从main.c这个⽂件⾥⾯的start_kernel函数开始的,这个⽂件在linux源码⾥⾯的init⽂件夹下⾯下⾯我们来看看这个函数这个函数很长,可以看个⼤概过去asmlinkage __visible void __init start_kernel(void){char *command_line;char *after_dashes;set_task_stack_end_magic(&init_task);smp_setup_processor_id();debug_objects_early_init();cgroup_init_early();local_irq_disable();early_boot_irqs_disabled = true;/** Interrupts are still disabled. Do necessary setups, then* enable them.*/boot_cpu_init();page_address_init();pr_notice("%s", linux_banner);setup_arch(&command_line);/** Set up the the initial canary and entropy after arch* and after adding latent and command line entropy.*/add_latent_entropy();add_device_randomness(command_line, strlen(command_line));boot_init_stack_canary();mm_init_cpumask(&init_mm);setup_command_line(command_line);setup_nr_cpu_ids();setup_per_cpu_areas();smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */boot_cpu_hotplug_init();build_all_zonelists(NULL);page_alloc_init();pr_notice("Kernel command line: %s\n", boot_command_line);parse_early_param();after_dashes = parse_args("Booting kernel",static_command_line, __start___param,__stop___param - __start___param,-1, -1, NULL, &unknown_bootoption);if (!IS_ERR_OR_NULL(after_dashes))parse_args("Setting init args", after_dashes, NULL, 0, -1, -1,NULL, set_init_arg);jump_label_init();/** These use large bootmem allocations and must precede* kmem_cache_init()*/setup_log_buf(0);vfs_caches_init_early();sort_main_extable();trap_init();mm_init();ftrace_init();/* trace_printk can be enabled here */early_trace_init();/** Set up the scheduler prior starting any interrupts (such as the* timer interrupt). Full topology setup happens at smp_init()* time - but meanwhile we still have a functioning scheduler.*/sched_init();/** Disable preemption - early bootup scheduling is extremely* fragile until we cpu_idle() for the first time.preempt_disable();if (WARN(!irqs_disabled(),"Interrupts were enabled *very* early, fixing it\n"))local_irq_disable();radix_tree_init();/** Set up housekeeping before setting up workqueues to allow the unbound * workqueue to take non-housekeeping into account.*/housekeeping_init();/** Allow workqueue creation and work item queueing/cancelling* early. Work item execution depends on kthreads and starts after* workqueue_init().*/workqueue_init_early();rcu_init();/* Trace events are available after this */trace_init();if (initcall_debug)initcall_debug_enable();context_tracking_init();/* init some links before init_ISA_irqs() */early_irq_init();init_IRQ();tick_init();rcu_init_nohz();init_timers();hrtimers_init();softirq_init();timekeeping_init();time_init();printk_safe_init();perf_event_init();profile_init();call_function_init();WARN(!irqs_disabled(), "Interrupts were enabled early\n");early_boot_irqs_disabled = false;local_irq_enable();kmem_cache_init_late();/** HACK ALERT! This is early. We're enabling the console before* we've done PCI setups etc, and console_init() must be aware of* this. But we do want output early, in case something goes wrong.*/console_init();if (panic_later)panic("Too many boot %s vars at `%s'", panic_later,panic_param);lockdep_init();/** Need to run this when irqs are enabled, because it wants* to self-test [hard/soft]-irqs on/off lock inversion bugs* too:*/locking_selftest();/** This needs to be called before any devices perform DMA* operations that might use the SWIOTLB bounce buffers. It will* mark the bounce buffers as decrypted so that their usage will* not cause "plain-text" data to be decrypted when accessed.*/mem_encrypt_init();#ifdef CONFIG_BLK_DEV_INITRDif (initrd_start && !initrd_below_start_ok &&page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) {pr_crit("initrd overwritten (0x%08lx < 0x%08lx) - disabling it.\n",page_to_pfn(virt_to_page((void *)initrd_start)),min_low_pfn);initrd_start = 0;#endifkmemleak_init();setup_per_cpu_pageset();numa_policy_init();acpi_early_init();if (late_time_init)late_time_init();sched_clock_init();calibrate_delay();pid_idr_init();anon_vma_init();#ifdef CONFIG_X86if (efi_enabled(EFI_RUNTIME_SERVICES))efi_enter_virtual_mode();#endifthread_stack_cache_init();cred_init();fork_init();proc_caches_init();uts_ns_init();buffer_init();key_init();security_init();dbg_late_init();vfs_caches_init();pagecache_init();signals_init();seq_file_init();proc_root_init();nsfs_init();cpuset_init();cgroup_init();taskstats_init_early();delayacct_init();check_bugs();acpi_subsystem_init();arch_post_acpi_subsys_init();sfi_init_late();/* Do the rest non-__init'ed, we're now alive */arch_call_rest_init();}这个函数⾥⾯我们会看到有很多的各种init,也就是初始化,我们只说⼏个重点操作⾸先来看下这个函数set_task_stack_end_magic(&init_task);在linux⾥⾯所有的进程都是由⽗进程创建⽽来,所以说在启动内核的时候需要有个祖先进程,这个进程是系统创建的第⼀个进程,我们称为0号进程,它是唯⼀⼀个没有通过fork或者kernel_thread的进程然后就是初始化系统调⽤,对应的函数就是trap_init();这⾥⾯设置了很多中断门,⽤于处理各种中断系统调⽤也是通过发送中断的⽅式进⾏的。

Linux内核源代码分析

Linux内核源代码分析

Linux内核源代码的组成
核心模块
– 核心模块代码部分布在内核中,部分位 于modules包中。核心代码位于 kernel/modules.c且其数据结构与核心后 台进程kerneld消息位于 include/linux/module.h和 include/linux/kerneld.h目录中。必要时 需查阅inlude/linux/elf.h中的ELF文件格 式。
– 利用自由软件让个人计算机带十几个硬 盘实现阵列技术,及其亚微米超大规模 集成电路CAD系统,可直接输出生产线 控制数据等。
– Linux内核的许多面向通信的底层代码对 开发我国自己的信息安全产品极有参考 价值。
分析Linux内核的意义
开发高水平软件
– 目前Linux的源代码中包含了世界各地几 百名计算机高手的作品,分析这些源代 码对于我们掌握核心技术会起到事半功 倍的作用,尤其是各种驱动程序的编写, 对于我们把软硬件结合起来发展民族信 息产业至关重要。
Linux内核源代码的组成
核心
– 大多数通用代码位于kernel目录下,而处理器相 关代码被放在arch/kernel中,调度管理程序位 于kernel/sched.c ,fork代码位于kernel/fork.c。 底层部分处理及中断处理的代码位于 include/linux/interrupt.h里。在/linux/sched.h中 可以找到task_struct的描述。
– Windows下的一个阅读源代码的工具:Source Insight。该软件可从下载
– Linux下一个阅读源代码的工具是LXR
网络
– 网络代码位于net目录而大多数包含文件位于 include/net中,BSD套接字代码位于socket.c中。 IPv4的INET套接字代码位于net/ipv4/af_inet.c中。 通用协议支撑代码(包括sk_buff处理过程)位 于net/core中,同时TCP/IP网络代码位于 net/ipv4中。网络设备驱动位于drivers/net中。
  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指令执行逻辑右移操作。

相关文档
最新文档