Linux0.01内核源代码及注释
Linux 0.1.1文件系统的源码阅读

Linux 0.11文件系统的源码阅读总结1.minix文件系统对于linux 0.11内核的文件系统的开发,Linus主要参考了Andrew S.Tanenbaum 所写的《MINIX操作系统设计与实现》,使用的是其中的1.0版本的MINIX文件系统。
而高速缓冲区的工作原理参见M.J.Bach的《UNIX操作系统设计》第三章内容。
通过对源代码的分析,我们可以将minix文件系统分为四个部分,如下如1-1。
●高速缓冲区的管理程序。
主要实现了对硬盘等块设备进行数据高速存取的函数。
●文件系统的底层通用函数。
包括文件索引节点的管理、磁盘数据块的分配和释放以及文件名与i节点的转换算法。
●有关对文件中的数据进行读写操作的函数。
包括字符设备、块设备、管道、常规文件的读写操作,由read_write.c函数进行总调度。
●涉及到文件的系统调用接口的实现,这里主要涉及文件的打开、关闭、创建以及文件目录等系统调用,分布在namei和inode等文件中。
图1-1 文件系统四部分之间关系图1.1超级块首先我们了解一下MINIX文件系统的组成,主要包括六部分。
对于一个360K软盘,其各部分的分布如下图1-2所示:图 1-2 建有MINIX文件系统的一个360K软盘中文件系统各部分的布局示意图注释1:硬盘的一个扇区是512B,而文件系统的数据块正好是两个扇区。
注释2:引导块是计算机自动加电启动时可由ROM BIOS自动读入得执行代码和数据。
注释3:逻辑块一般是数据块的2幂次方倍数。
MINIX文件系统的逻辑块和数据块同等大小对于硬盘块设备,通常会划分几个分区,每个分区所存放的不同的文件系统。
硬盘的第一个扇区是主引导扇区,其中存放着硬盘引导程序和分区表信息。
分区表中得信息指明了硬盘上每个分区的类型、在硬盘中其实位置参数和结束位置参数以及占用的扇区总数。
其结构如下图1-3所示。
图1-3 硬盘设备上的分区和文件系统对于可以建立不同的多个文件系统的硬盘设备来说,minix文件系统引入超级块进行管理硬盘的文件系统结构信息。
编译 linux 源代码

编译linux 源代码
编译 Linux 源代码需要以下步骤:
1.下载 Linux 源代码
可以从官方网站或者其它可靠的源下载 Linux 源代码。
2.解压源代码
使用解压工具将下载的源代码解压到一个目录下。
3.配置编译环境
在终端中输入以下命令来配置编译环境:
bash复制代码
export ARCH=arm64 # 根据自己的硬件架构选择合适的架构
export CROSS_COMPILE=arm64-linux-gnueabi- # 根据自己的硬件架构选择合适的编译器前缀
4.执行编译命令
在终端中输入以下命令来执行编译:
bash复制代码
make menuconfig # 配置内核选项,按上下键选择需要的选项,按空格键进行确认/取消选择,按Y 键保存更改,最后按 Esc 键退出配置菜单。
make # 开始编译内核,等待编译完成。
5.等待编译完成
编译完成后,会在arch/$ARCH/boot/目录下生成一个名为Image的文件,这就是编译好的 Linux 内核映像文件。
system_call.s 文档简要注释

KERNEL/system_call.s文档简要注释Willweb于2003-4-2简述:系统调用是linux内核的用户程序的接口,用户程序可以通过系统调用陷入到linux内核执行相关的功能和操作,同时,用户空间的进程也可以通过系统调用深入到内核空间执行,平时我们用到最多的fork,exit,execve等函数和操作实际上都是通过系统调用来执行的.在linux0.01内核中,很多以sys_开头的函数实际上就是系统调用执行的函数体,它在内核空间执行,虽然它支持的系统调用不如2.4内核的多,在内核代码中可以看到很多sys_函数都是空的(这是因为它需要按照posix标准来做),但是它的整体结构和流程和2.4等更高版本的内核差不多.下面可以通过一个简单的用户调用来看看0.01的系统调用究竟是如何工作的,这里假设用户在程序中执行一条创建一个新进程的函数----fork(),这里与平时编程的函数调用不同,这里找不到fork的函数体,实际上是通过unistd.h中的_syscall宏来将当前的fork操作转化,具体转化的方式是通过syscall0(int,fork)宏将fork的系统调用号3装入eax寄存器中,然后就通过中断0x80来进入kernel/systemcall.s中的_system_call执行.这里注意,在sched_init中将_system_call注入到idt中,且对应的是0x80这个中断向量号.当通过0x80中断之后,cpu自动保存一些寄存器的值到堆栈中,系统调用可以在用户态和核心态调用,但是它的执行是在核心态执行,所以,如果是在用户态调用的话,int 0x80的时候会自动的将用户态的ss和esp保存到堆栈中,同时将eflags,cs和eip也保存返回地址,如果是在内核态调用系统调用,那么就不需要保存ss和esp了,因为执行完系统调用之后堆栈段和堆栈指针并不切换.当进入到_system_call之后,就执行下面的操作了./** system_call.s contains the system-call low-level handling routines.* This also contains the timer-interrupt handler, as some of the code is* the same. The hd-interrupt is also here.** NOTE: This code handles signal-recognition, which happens every time* after a timer-interrupt and after each system call. Ordinary interrupts* don't handle signal-recognition, as that would clutter them up totally* unnecessarily.** Stack layout in 'ret_from_system_call':*//下面是在系统调用过程中内核堆栈的参数顺序,系统调用和平时的应用程序中的函数调用不同,平时我们的函数调用是通过堆栈传值,但是系统调用是通过寄存器传值,因为这里可能需要更改堆栈段* 0(%esp) - %eax* 4(%esp) - %ebx* 8(%esp) - %ecx* C(%esp) - %edx* 10(%esp) - %fs* 14(%esp) - %es* 18(%esp) - %ds* 1C(%esp) - %eip* 20(%esp) - %cs* 24(%esp) - %eflags* 28(%esp) - %oldesp* 2C(%esp) - %oldss*///下面的指明了一些参数,其中的EAX,EBX,ECX,EDX,FS,ES,DS,CS,EFLAGS,OLDESP和OLDSS实际上是他们在堆栈中相对于esp的偏移SIG_CHLD = 170x00EAX =0x04EBX =0x08ECX =EDX =0x0CFS = 0x10ES = 0x14DS = 0x18EIP = 0x1CCS = 0x20EFLAGS = 0x24OLDESP = 0x28OLDSS = 0x2C//下面的这些字段是定义的在进程结构中他们的偏移地址state = 0 # these are offsets into the task-struct.counter = 4priority = 812signal =restorer = 16 # address of info-restorersig_fn = 20 # table of 32 signal addresses//总的系统调用个数nr_system_calls = 67.globl _system_call,_sys_fork,_timer_interrupt,_hd_interrupt,_sys_execve.align 2//如果系统调用号EAX超过了总的系统调用个数,那么参数不对,将返回值为-1返回bad_sys_call:$-1,%eaxmovliret.align 2//系统调用完成之后,如果当前进程的状态不为运行状态或者时间片刚好用光,那么重新选择一个进程运行reschedule:$ret_from_sys_callpushl_schedulejmp.align 2//系统调用的入口_system_call://比较调用号是否超出了总的系统调用界限cmpl$nr_system_calls-1,%eaxbad_sys_callja//将所有的段和寄存器都保存到堆栈中,因为他们需要在系统调用过程中传递参数,前面提到了,ss,esp,eflags,cs和eip是系统进入中断的时候硬件自动完成的%dspush%espush%fspush%edxpushlpushl %ecx # push %ebx,%ecx,%edx as parameterspushl %ebx # to the system call//下面将ds和es置为内核代码段movl $0x10,%edx # set up ds,es to kernel space%dx,%dsmov%dx,%esmov//同时把fs设置为用户数据段,因为后面可以看到它用来保存一些值movl $0x17,%edx # fs points to local data space%dx,%fsmov//调用系统调用表中的对应函数,因为一个系统调用函数的地址是4个字节的,所以这里是eax*4了_sys_call_table(,%eax,4)call//当系统调用函数执行完之后,自动将返回值保存到eax中%eaxpushl//比较当前进程是否在运行和时间片是否到了,如果不能继续运行,那么就重新调度进程_current,%eaxmovlstatecmpl$0,state(%eax) #reschedulejne#counter$0,counter(%eax)cmplrescheduleje//返回系统调用,在返回系统调用的时候,检查进程是否有信号,如果有,那么就处理对应的函数ret_from_sys_call:movl _current,%eax # task[0] cannot have signals//init进程不能够接收任何信号,所以如果是init进程就不需要检查信号位图_task,%eaxcmpl3fje//检查系统调用结束后是否需要返回用户态,如果不返回用户态,那么就不会执行信号处理函数,因为信号处理函数是在用户态执行的,而且是进程返回用户态的是否就执行movl CS(%esp),%ebx # was old code segment supervisortestl $3,%ebx # mode? If so - don't check signals3fje//同样检查堆栈段cmpw $0x17,OLDSS(%esp) # was stack segment = 0x17 ?3fjne//如果要返回用户态,那么就逐个检查signal对应的bit2: movl signal(%eax),%ebx # signals (bitmap, 32 signals)//检查ebx中是否有对应位为1的,也就是是否有信号,如果有,那么索引放到ecx中bsfl %ebx,%ecx # %ecx is signal nr, return if none//如果没有,那么就不执行3fje//清对应的位clearit%ecx,%ebx #btrl%ebx,signal(%eax)movl//将进程对应的信号处理函数地址放入ebx,检查做何种处理movl sig_fn(%eax,%ecx,4),%ebx # %ebx is signal handler address$1,%ebxcmpl//如果为0那么就执行系统默认的信号处理函数jb default_signal # 0 is default signal handler – exit//如果为1就忽略这个信号je 2b # 1 is ignore - find next signal//否则就执行次信号处理函数,将其地址放到eip中,这样信号切换回来之后就开始运行它movl $0,sig_fn(%eax,%ecx,4) # reset signal handler addressincl%ecxxchgl %ebx,EIP(%esp) # put new return address on stack//因为要用到一些参数,所以需要对参数地址进行检查,以免用户态的地址调用了内核的subl $28,OLDESP(%esp)movl OLDESP(%esp),%edx # push old return address on stackpushl %eax # but first check that it's ok.%ecxpushlpushl$28%edxpushl_verify_areacall%edxpopl$4,%espaddl%ecxpopl%eaxpopl//将所有用到的一些重要字段保存在fs段中movlrestorer(%eax),%eaxmovl %eax,%fs:(%edx) # flag/reg restorermovl %ecx,%fs:4(%edx) # signal nrEAX(%esp),%eaxmovlmovl %eax,%fs:8(%edx) # old eaxmovlECX(%esp),%eaxmovl %eax,%fs:12(%edx) # old ecxEDX(%esp),%eaxmovlmovl %eax,%fs:16(%edx) # old edxEFLAGS(%esp),%eaxmovlmovl %eax,%fs:20(%edx) # old eflagsmovl %ebx,%fs:24(%edx) # old return addr//返回之前,恢复压入的参数3: popl %eax%ebxpoplpopl%ecx%edxpopl%fspop%espop%dspopiret//信号的默认处理函数,如果是SIG_CHLD信号,就处理它,否则就exitdefault_signal:%ecxinclcmpl$SIG_CHLD,%ecx2bje%ecxpushl_do_exitcall$4,%espaddl3bjmp.align 2_timer_interrupt:%dspush%espush%fspush%edxpushl%ecxpushl%ebxpushl%eaxpushl$0x10,%eaxmovl%ax,%dsmov%ax,%esmov$0x17,%eaxmovl%ax,%fsmov//在时间中断中将jiffies全局变量加1incl_jiffies//发EOI指令给EOI寄存器$0x20,%almovb%al,$0x20outbCS(%esp),%eaxmovlandl $3,%eax # %eax is CPL (0 or 3, 0=supervisor)%eaxpushl//在这里eax就是do_timer中的cpl了call _do_timer # 'do_timer(long CPL)' does everything from addl $4,%esp # task switching to accounting ...ret_from_sys_calljmp.align 2_sys_execve:EIP(%esp),%eaxlea%eaxpushl_do_execvecall$4,%espaddlret.align 2_sys_fork:call_find_empty_process%eax,%eaxtestl1fjspush%gs%esipushl%edipushl%ebppushl%eaxpushl_copy_processcall$20,%espaddl1: ret_hd_interrupt:%eaxpushl%ecxpushl%edxpushl%dspush%espush%fspush$0x10,%eaxmovl%ax,%dsmov%ax,%esmov$0x17,%eaxmovl%ax,%fsmov$0x20,%almovboutb %al,$0x20 # EOI to interrupt controller #1 jmp 1f # give port chance to breathe1: jmp 1f1: outb %al,$0xA0 # same to controller #2_do_hd,%eaxmovl%eax,%eaxtestl1fjne$_unexpected_hd_interrupt,%eaxmovl1: call *%eax # "interesting" way of handling intr.%fspop%espop%dspop%edxpopl%ecxpopl%eaxpopliret。
linux内核(kernel)版本号的意义

linux内核(kernel)版本号的意义在linux下有⼀个⽬录,即/usr/src/kernels/⽬录,下⾯记载着⼀个linux系统的内核⽂件,例如:2.6.18-164.el5-x86_64、2.6.18-8.el5-x86_64和2.6.18-194.el5-x86_64等,这些⽂件编号意味着什么呢?例如2.6.18代表着什么?el5代表着什么?x86_64⼜代表着什么?linux内核版本的分类Linux内核版本有两种:稳定版和开发版,Linux内核版本号由3组数字组成:第⼀个组数字.第⼆组数字.第三组数字第⼀个组数字:⽬前发布的内核主版本。
第⼆个组数字:偶数表⽰稳定版本;奇数表⽰开发中版本。
第三个组数字:错误修补的次数。
例1: 2.6.18-128.ELsmp ,第⼀个组数字: 2 , 主版本号第⼆个组数字: 6 , 次版本号,表⽰稳定版本(因为有偶数)第三个组数字 18 , 修订版本号,表⽰修改的次数,头两个数字合在⼀齐可以描述内核系列。
如稳定版的2.6.0,它是2.6版内核系列。
128:表⽰这个当前版本的第5次微调patch ,⽽ELsmp指出了当前内核是为ELsmp特别调校的 EL : Enterprise Linux ; smp : 表⽰⽀持多处理器,表⽰该内核版本⽀持多处理器linux内核下⾥的ELsmp与EL与smp在linux下ELsmp指出了当前内核是为ELsmp特别调校的 EL : Enterprise Linux ; smp : 表⽰⽀持多处理器,表⽰该内核版本⽀持多处理器例2:Red Hat Linux开机的时候,GRUB的启动菜单会有两个选项,分别是 Red Hat Enterprise Linux ES (版本号.ELsmp) Red Hat Enterprise Linux ES-up (版本号.EL) 其实这个就是系统开机时由GRUB引导启动-单处理器与对称多处理器启动核⼼⽂件的区别。
Linux操作系统应用编程课件(完整版)

2.Linux操作系统的发行版
Linux操作系统发行版实际就是Linux内核加上外围实用程序 组成的一个大软件包。相对于Linux操作系统的内核版本,发行版 的版本号随发布者的不同而不同,与Linux操作系统内核的版本号 是相对独立的。因此把SUSE、RedHat、Ubuntu、Slackware等直 接称为Linux是不确切的,它们是Linux操作系统的发行版。更确 切地说,应该将它们称为“以Linux为核心的操作系统软件包”。
Shell是Linux操作系统的一种用户界面,它作为操作系统 的“外壳”,为用户提供使用操作系统的接口。Shell主要有以 下两大功能特点。
(1)Shell是一个命令解释器,它拥有自己内建的Shell命令集。 (2)Shell的另一个重要特性是它自身就是一种解释型的程序设 计语言。
当用户成功登录Linux系统后,系统将执行一个Shell程序。 正是Shell进程提供了命令提示符。作为默认值,Shell对普通用 户用“$”作提示符,对超级用户(root)用“#”作提示符。
1.4.4 联机手册
联机手册命令man可向用户提供系统中各种命令、系统调用、 库函数和重要系统文件的详细说明,包括名字、使用语法、功能 描述、应用实例和相关参考文件等。其格式如下:
$ man [拥有哪个级别的帮助。 -k:查看和命令相关的所有帮助。
查看who命令的详细说明示例如下。 $ man who
Linux操作系统 应用编程
本章主要介绍Linux文件系统,包括文件系统的结构、文 件的定义与分类、目录与文件操作命令、文件的权限管理等, 让读者对Linux文件系统有一定的认识和理解,为后文的学习 打下基础。
2.1.1 组织结构
Linux操作系统中所有文件存储在文件系统中,文件被组织 到一棵“目录树”中,其文件系统层次结构(树状目录结构)如 图2.1所示。树根在该层次结构的顶部,树根的下方衍生出子目 录分支。
怎样读Linux内核源代码

Linux内核分析方法2010-9-12Linux的最大的好处之一就是它的源码公开。
同时,公开的核心源码也吸引着无数的电脑爱好者和程序员;他们把解读和分析Linux的核心源码作为自己的最大兴趣,把修改Linux 源码和改造Linux系统作为自己对计算机技术追求的最大目标。
Linux内核源码是很具吸引力的,特别是当你弄懂了一个分析了好久都没搞懂的问题;或者是被你修改过了的内核,顺利通过编译,一切运行正常的时候。
那种成就感真是油然而生!而且,对内核的分析,除了出自对技术的狂热追求之外,这种令人生畏的劳动所带来的回报也是非常令人着迷的,这也正是它拥有众多追随者的主要原因:•首先,你可以从中学到很多的计算机的底层知识,如后面将讲到的系统的引导和硬件提供的中断机制等;其它,象虚拟存储的实现机制,多任务机制,系统保护机制等等,这些都是非都源码不能体会的。
等等,这些都是非读源码不能体会的。
•同时,你还将从操作系统的整体结构中,体会整体设计在软件设计中的份量和作用,以及一些宏观设计的方法和技巧:Linux的内核为上层应用提供一个与具体硬件不相关的平台;同时在内核内部,它又把代码分为与体系结构和硬件相关的部分,和可移植的部分;再例如,Linux虽然不是微内核的,但他把大部分的设备驱动处理成相对独立的内核模块,这样减小了内核运行的开销,增强了内核代码的模块独立性。
•而且你还能从对内核源码的分析中,体会到它在解决某个具体细节问题时,方法的巧妙:如后面将分析到了的Linux通过Botoom_half机制来加快系统对中断的处理。
•最重要的是:在源码的分析过程中,你将会被一点一点地、潜移默化地专业化。
一个专业的程序员,总是把代码的清晰性,兼容性,可移植性放在很重要的位置。
他们总是通过定义大量的宏,来增强代码的清晰度和可读性,而又不增加编译后的代码长度和代码的运行效率;他们总是在编码的同时,就考虑到了以后的代码维护和升级。
甚至,只要分析百分之一的代码后,你就会深刻地体会到,什么样的代码才是一个专业的程序员写的,什么样的代码是一个业余爱好者写的。
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_m = “swapper”INIT_MM将init_mm.pgd初始化为swapper_pg_dir,即init_level4_pgt,定义与head.S中。
Linux内核0.11体系结构——《Linux内核完全注释》笔记打卡
Linux内核0.11体系结构——《Linux内核完全注释》笔记打卡0 总体介绍⼀个完整的操作系统主要由4部分组成:硬件、操作系统内核、操作系统服务和⽤户应⽤程序,如图0.1所⽰。
操作系统内核程序主要⽤于对硬件资源的抽象和访问调度。
图0.1 操作系统组成部分内核的主要作⽤是为了与计算机硬件进⾏交互,实现对硬件部件的编程控制和接⼝操作,调度对硬件资源的访问,并为计算机上的⽤户程序提供⼀个⾼级的执⾏环境和对硬件的虚拟接⼝。
1 Linux内核模式操作系统内核的结构模式主要可分为整体式的单内核模式和层次是的微内核模式。
Linux 0.11采⽤了单内核模式。
如图1.2所⽰,单内核操作系统所提供的服务流程为:应⽤主程序使⽤指定的参数值执⾏系统调⽤指令(int x80),使CPU从⽤户态切换到核⼼态,然后操作系统根据具体的参数值调⽤特定的系统调⽤服务程序,这些服务程序根据需要再调⽤底层的⼀些⽀持函数以完成特定的功能。
完成服务后,系统使CPU从核⼼态回到⽤户态,执⾏后续的指令。
图1.1 单内核模式的简单模型结构2 Linux内核系统体系结构Linux内核主要由5个模块构成,分别为:进程调度模块、内存管理模块、⽂件系统模块、进程间通信模块和⽹络接⼝模块。
模块之间的依赖关系如图2.1所⽰,虚线部分表⽰0.11版本内核中未实现部分(所有的模块都与进程调度模块存在依赖关系)。
图2.1 Linux内核系统模块结构及相互依赖关系从单内核模式结构模型出发,Linux 0.11内核源代码的结构将内核主要模块分配如图2.2所⽰。
(除了硬件控制⽅框,其他粗线分别对应内核源代码的⽬录组织结构)图2.2 内核结构框图3 Linux内核对内存的管理和使⽤对于机器中的物理内存,Linux 0.11内核中,系统初始化阶段将其划分的功能区域如图3.1所⽰。
图3.1 物理内存使⽤的功能分布图虚拟地址:(virtual address)由程序产⽣的由段选择符合段内偏移地址两个部分组成的地址。
第二章-Linux内核及内核编程分析课件
系统攻击者利用的漏洞。
Linux内核及编程
Linux内核编译
Linux内核的获取和更新
• linux内核版本发布的官方网站http:// 。 • 发布形式:一种是full/Source 版本,另外一种是patch文件,即补丁。 • 完整内核版本较大,一般是tar.gz或者是.bz2文件,二者分别是使用
Linux内核源代码目录结构
• arch:和硬件体系结构相关的代码,每种平台占一个相应目录。 • drivers:设备驱动程序,每个不同驱动占用一个子目录。 • fs:支持的各种文件系统,如EXT、FAT、NTFS、JFFS2。 • block:块设备驱动程序I/O调度。 • include:与系统相关的头文件放在include/linux下。 • init:内核初始化代码。 • kernel:内核最核心部分,和平台相关的一部分放在arch/*/kernel • mm:内存管理代码,和平台相关的一部分放在arch/*/mm • scripts:用于配置内核的脚本文件。 • usr:实现了用于打包和压缩的cpio等。
FORLINX_linux-2.6.36.2.tar.gz 。 • 文件解压到/usr/src/linux目录,然后稍作修改。 mv linux linux-2.6.5;
ln -s linux-2.6.5 linux。(可选)
Linux内核及编程
Linux内核编译步骤
• 通常要运行的第一个命令是: cd /usr/src/linux 。 • make mrproper :该命令确保源代码目录下没有不正确的.ko文件以及
Linux内核源代码(free)
I386系统的基本概念
代码的运行 堆栈的概念 内核态与用户态 中断/异常/系统调用 虚拟内存
堆栈的概念
堆栈是C语言程序运行时必须的一个记录调用路径和参 数的空间
函数调用框架 传递参数 保存返回地址 提供局部变量空间 等等
C语言编译器对堆栈的使用有一套的规则 了解堆栈存在的目的和编译器对堆栈使用的规则是理解 操作系统一些关键性代码的基础 以x86体系结构为例
内核版本 发行版本
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 国际标准化组织 制定各行各业各种产品和服务的技术规范(国际标准)
GNU,“g-noo”,“GNU's Not Unix”
Linux内核维护网站
Linux简介
什么是Linux? “Linux”在不同的语境下的含义 Linux发展简史 Linux操作系统的主要内容 Linux版本
内核版本 发行版本
Linux系统的好处 Linux的运行及相关基本概念
堆栈
程序的代码段
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
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)处,并跳转至那里。
!! 它然后使用BIOS 中断将'setup'直接加载到自己的后面(0x90200)(576.5k),! 并将system 加载到地址0x10000 处。
!! 注意! 目前的内核系统最大长度限制为(8*65536)(512k)字节,即使是在! 将来这也应该没有问题的。
我想让它保持简单明了。
这样512k 的最大内核长度应该! 足够了,尤其是这里没有象minix 中一样包含缓冲区高速缓冲。
!! 加载程序已经做的够简单了,所以持续的读出错将导致死循环。
只能手工重启。
! 只要可能,通过一次取取所有的扇区,加载过程可以做的很快的。
.globl begtext, begdata, begbss, endtext, enddata, endbss ! 定义了6 个全局标识符;.text ! 文本段;begtext:.data ! 数据段;begdata:.bss ! 堆栈段;begbss:.text ! 文本段;SETUPLEN = 4 ! nr of setup-sectors! setup 程序的扇区数(setup-sectors)值;BOOTSEG = 0x07c0 ! original address of boot-sector! bootsect 的原始地址(是段地址,以下同);INITSEG = 0x9000 ! we move boot here - out of the way! 将bootsect 移到这里-- 避开;SETUPSEG = 0x9020 ! setup starts here! setup 程序从这里开始;SYSSEG = 0x1000 ! system loaded at 0x10000 (65536).! system 模块加载到0x10000(64 kB)处;ENDSEG = SYSSEG + SYSSIZE ! where to stop loading! 停止加载的段地址;! ROOT_DEV: 0x000 - same type of floppy as boot.! 根文件系统设备使用与引导时同样的软驱设备;! 0x301 - first partition on first drive etc! 根文件系统设备在第一个硬盘的第一个分区上,等等;ROOT_DEV = 0x306 ! 指定根文件系统设备是第2 个硬盘的第1 个分区。
这是Linux 老式的硬盘命名! 方式,具体值的含义如下:! 设备号=主设备号*256 + 次设备号(也即dev_no = (major<<8) + minor )! (主设备号:1-内存,2-磁盘,3-硬盘,4-ttyx,5-tty,6-并行口,7-非命名管道)! 0x300 - /dev/hd0 - 代表整个第1 个硬盘;! 0x301 - /dev/hd1 - 第1 个盘的第1 个分区;! …! 0x304 - /dev/hd4 - 第1 个盘的第4 个分区;! 0x305 - /dev/hd5 - 代表整个第2 个硬盘盘;! 0x306 - /dev/hd6 - 第2 个盘的第1 个分区;! …! 0x309 - /dev/hd9 - 第2 个盘的第4 个分区;! 从linux 内核0.95 版后已经使用与现在相同的命名方法了。
entry start ! 告知连接程序,程序从start 标号开始执行。
start: ! 47--56 行作用是将自身(bootsect)从目前段位置0x07c0(31k)! 移动到0x9000(576k)处,共256 字(512 字节),然后跳转到! 移动后代码的go 标号处,也即本程序的下一语句处。
mov ax,#BOOTSEG ! 将ds 段寄存器置为0x7C0;mov ds,axmov ax,#INITSEG ! 将es 段寄存器置为0x9000;mov es,axmov cx,#256 ! 移动计数值=256 字;sub si,si ! 源地址ds:si = 0x07C0:0x0000sub di,di ! 目的地址es:di = 0x9000:0x0000rep ! 重复执行,直到cx = 0movw ! 移动1 个字;jmpi go,INITSEG ! 间接跳转。
这里INITSEG 指出跳转到的段地址。
go: mov ax,cs ! 将ds、es 和ss 都置成移动后代码所在的段处(0x9000)。
mov ds,ax !由于程序中有堆栈操作(push,pop,call),因此必须设置堆栈。
mov es,ax! put stack at 0x9ff00. ! 将堆栈指针sp 指向0x9ff00(即0x9000:0xff00)处mov ss,axmov sp,#0xFF00 ! arbitrary value >>512! 由于代码段移动过了,所以要重新设置堆栈段的位置。
! sp 只要指向远大于512 偏移(即地址0x90200)处! 都可以。
因为从0x90200 地址开始处还要放置setup 程序,! 而此时setup 程序大约为4 个扇区,因此sp 要指向大! 于(0x200 + 0x200 * 4 + 堆栈大小)处。
! load the setup-sectors directly after the bootblock.! Note that 'es' is already set up.! 在bootsect 程序块后紧根着加载setup 模块的代码数据。
! 注意es 已经设置好了。
(在移动代码时es 已经指向目的段地址处0x9000)。
load_setup:! 68--77 行的用途是利用BIOS 中断INT 0x13 将setup 模块从磁盘第2 个扇区! 开始读到0x90200 开始处,共读4 个扇区。
如果读出错,则复位驱动器,并! 重试,没有退路。
INT 0x13 的使用方法如下:! 读扇区:! ah = 0x02 - 读磁盘扇区到内存;al = 需要读出的扇区数量;! ch = 磁道(柱面)号的低8 位;cl = 开始扇区(0-5 位),磁道号高2 位(6-7);! dh = 磁头号;dl = 驱动器号(如果是硬盘则要置位7);! es:bx ??指向数据缓冲区;如果出错则CF 标志置位。
mov dx,#0x0000 ! drive 0, head 0mov cx,#0x0002 ! sector 2, track 0mov bx,#0x0200 ! address = 512, in INITSEGmov ax,#0x0200+SETUPLEN ! service 2, nr of sectorsint 0x13 ! read itjnc ok_load_setup ! ok - continuemov dx,#0x0000mov ax,#0x0000 ! reset the disketteint 0x13j load_setupok_load_setup:! Get disk drive parameters, specifically nr of sectors/track! 取磁盘驱动器的参数,特别是每道的扇区数量。
! 取磁盘驱动器参数INT 0x13 调用格式和返回信息如下:! ah = 0x08 dl = 驱动器号(如果是硬盘则要置位7 为1)。
! 返回信息:! 如果出错则CF 置位,并且ah = 状态码。
! ah = 0,al = 0,bl = 驱动器类型(A T/PS2)! ch = 最大磁道号的低8 位,cl = 每磁道最大扇区数(位0-5),最大磁道号高2 位(位6-7) ! dh = 最大磁头数,dl = 驱动器数量,! es:di -?? 软驱磁盘参数表。