2010 第2章 Linux中的启动代码分析

合集下载

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

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

linux源代码分析:Linux操作系统源代码详细分析疯狂代码 / ĵ:http://Linux/Article28378.html内容介绍: Linux 拥有现代操作系统所有功能如真正抢先式多任务处理、支持多用户内存保护虚拟内存支持SMP、UP符合POSIX标准联网、图形用户接口和桌面环境具有快速性、稳定性等特点本书通过分析Linux内核源代码充分揭示了Linux作为操作系统内核是如何完成保证系统正常运行、协调多个并发进程、管理内存等工作现实中能让人自由获取系统源代码并不多通过本书学习将大大有助于读者编写自己新 第部分 Linux 内核源代码 arch/i386/kernel/entry.S 2 arch/i386/kernel/init_task.c 8 arch/i386/kernel/irq.c 8arch/i386/kernel/irq.h 19 arch/i386/kernel/process.c 22 arch/i386/kernel/signal.c 30arch/i386/kernel/smp.c 38 arch/i386/kernel/time.c 58 arch/i386/kernel/traps.c 65arch/i386/lib/delay.c 73 arch/i386/mm/fault.c 74 arch/i386/mm/init.c 76 fs/binfmt-elf.c 82fs/binfmt_java.c 96 fs/exec.c 98 /asm-generic/smplock.h 107 /asm-i386/atomic.h 108 /asm-i386/current.h 109 /asm-i386/dma.h 109 /asm-i386/elf.h 113 /asm-i386/hardirq.h 114 /asm-i386/page.h 114 /asm-i386/pgtable.h 115 /asm-i386/ptrace.h 122 /asm-i386/semaphore.h 123 /asm-i386/shmparam.h 124 /asm-i386/sigcontext.h 125 /asm-i386/siginfo.h 125 /asm-i386/signal.h 127/asm-i386/smp.h 130 /asm-i386/softirq.h 132 /asm-i386/spinlock.h 133 /asm-i386/system.h 137/asm-i386/uaccess.h 139 //binfmts.h 146 //capability.h 147 /linux/elf.h 150 /linux/elfcore.h 156/linux/errupt.h 157 /linux/kernel.h 158 /linux/kernel_stat.h 159 /linux/limits.h 160 /linux/mm.h 160/linux/module.h 164 /linux/msg.h 168 /linux/personality.h 169 /linux/reboot.h 169 /linux/resource.h 170 /linux/sched.h 171 /linux/sem.h 179 /linux/shm.h 180 /linux/signal.h 181 /linux/slab.h 184/linux/smp.h 184 /linux/smp_lock.h 185 /linux/swap.h 185 /linux/swapctl.h 187 /linux/sysctl.h 188/linux/tasks.h 194 /linux/time.h 194 /linux/timer.h 195 /linux/times.h 196 /linux/tqueue.h 196/linux/wait.h 198 init/.c 198 init/version.c 212 ipc/msg.c 213 ipc/sem.c 218 ipc/shm.c 227 ipc/util.c 236 kernel/capability.c 237 kernel/dma.c 240 kernel/exec_do.c 241 kernel/exit.c 242 kernel/fork.c 248 kernel/info.c 255 kernel/itimer.c 255 kernel/kmod.c 257 kernel/module.c 259 kernel/panic.c 270 kernel/prk.c 271 kernel/sched.c 275 kernel/signal.c 295 kernel/softirq.c 307 kernel/sys.c 307kernel/sysctl.c 318 kernel/time.c 330 mm/memory.c 335 mm/mlock.c 345 mm/mmap.c 348mm/mprotect.c 358 mm/mremap.c 361 mm/page_alloc.c 363 mm/page_io.c 368 mm/slab.c 372mm/swap.c 394 mm/swap_state.c 395 mm/swapfile.c 398 mm/vmalloc.c 406 mm/vmscan.c 409第 2部分 Linux 内核源代码分析 第1章 Linux 介绍 让用户很详细地了解大多数现有操作系统实际工作方式是不可能大多数操作系统源代码都是严格保密除了些研究用及为操作系统教学而设计系统外尽管研究和教学目都很好但是这类系统很少能够通过对正式操作系统小部分实现来体现操作系统实际功能对于操作系统些特殊问题这种折衷系统所能够表现就更是少得可怜了 在以实际使用为目标操作系统中让任何人都可以自由获取系统源代码无论目是要了解、学习还是改进这样现实系统并不多本书主题就是这些少数操作系统中个:Linux Linux工作方式类似于Uinx它是免费源代码也是开放符合标准规范标准32位(在64位CPU上是64位)操作系统Linux拥有现代操作系统所具有内容例如: * 真正抢先式多任务处理支持多用户 * 内存保护 * 虚拟内存 * 支持对称多处理机SMP(symmetric multiprocessing)即多个CPU机器以及通常单CPU(UP)机器 * 符合POSIX标准 * 联网 * 图形用户接口和桌面环境(实际上桌面环境并不只个) * 速度和稳定性 严格说来Linux并不是个完整操作系统当我们在安装通常所说Linux时我们实际安装是很多工具集合这些工具协同工作以组成个功能强大实用系统Linux本身只是这个操作系统内核是操作系统心脏、灵魂、指挥中心(整个系统应该称为GNU/Linux其原因在本章后续内容中将会给以介绍)内核以独占方式执行最底层任务保证系统正常运行—协调多个并发进程管理进程使用内存使它们相互的间不产生冲突满足进程访问磁盘请求等等 在本书中我们给大家揭示就是Linux是如何完成这具有挑战性工作 1.1 Linux和Unix简明历史 为了让大家对本书所讨论内容有更清楚了解让我们先来简要回顾下Linux历史由于Linux是在Unix基础上发展而来我们话题就从Unix开始 Unix是由AT&T贝尔实验室Ken Thompson和Dennis Ritchie于1969年在台已经废弃了PDP-7上开发;它最初是个用汇编语言写成单用户操作系统不久Thompson和Ritchie成功地说服管理部门为他们购买更新机器以便该开发小组可以实现个文本处理系统Unix就在PDP-11上用C语言重新编写(发明C语言部分目就在于此)它果真变成了个文本处理系统—不久的后只不过问题是他们先实现了个操作系统而已…… 最终他们实现了该文本处理工具而且Unix(以及Unix上运行工具)也在AT&T得到广泛应用在1973年Thompson和Ritchie在个操作系统会议上就这个系统发表了篇论文该论文引起了学术界对Unix系统极大兴趣 由于1956年反托拉斯法案限制AT&T不能涉足计算机业务但允许它象征性地收取费用发售该系统就这样Unix被广泛发布首先是学术科研用户后来又扩展到政府和商业用户 伯克利加州大学是学术用户中个在这里Unix得到了计算机系统研究小组(CSRG)广泛应用并且在这里所进行修改引发了Unix大系列这就是广为人知伯克利软件Software开发(BSD)Unix除了AT&T所提供Unix系列的外BSD是最有影响力Unix系列BSD在Unix中增加了很多显著特性例如TCP/IP网络更好用户文件系统(UFS)工作控制并且改进了AT&T内存管理代码 多年以来BSD版本Unix直在学术环境中占据主导地位但最终发展成为 V版本AT&TUnix则成为商业领域领头羊从某种程度上来说这是有社会原因:学校倾向于使用非正式但通常更好用BSD风格Unix而商业界则倾向于从AT&T获取Unix 在用户需求和用户编程改进特性促进下BSD风格Unix般要比AT&TUnix更具有创新性而且改进也更为迅速但是在AT&T发布最后个正式版本 V Release 4(SVR4)时 V Unix已经吸收了BSD大多数重要优点并且还增加了些自己优势这部分由于从1984年开始AT&T逐渐可以将Unix商业化而伯克利Unix开发工作在1993年BSD4.4版本完成以后就逐渐收缩以至终止了然而BSD进步改进由外界开发者延续下来到今天还在继续进行正在进行Unix系列开发中至少有 4个独立版本是直接起源于BSD4.4这还不包括几个厂商Unix版本例如惠普HP-UX都是部分地或者全部基于BSD而发展起来 实际上Unix变种并不止BSD和 V由于Unix主要使用C语言来编写这就使得它移植到新机器上相对比较容易它简单性也使其重新设计和开发相对比较容易Unix这些特点大受商业界硬件供应商欢迎比如Sun、SGI、HP、IBM、DEC、Amdahl等等;IBM还不止次对Unix进行了再开发厂商们设计开发出新硬件并简单地将Unix移植到新硬件上这样新硬件经发布便具备定功能经过段时间的后这些厂商都拥有了自己专有Unix版本而且为了占有市场这些版本故意以区别侧重点发布出来以更好地占有用户版本混乱状态促进了标准化工作进行其中最主要就是POSIX系列标准它定义了套标准操作系统接口和工具从理论上说POSIX标准代码很容易移植到任何遵守POSIX标准操作系统中而且严格POSIX测试已经把这种理论上可移植性转化为现实直到今天几乎所有正式操作系统都以支持POSIX标准为目标 现在让我们回顾下在1984年杰出电脑黑客Richard Stallman独立开发出个类Unix操作系统该操作系统具有完全内核、开发工具和终端用户应用在GNU(“GNU誷 Not Unix”首字母缩写)计划配合下Stallman开发这个产品有自己技术理想:他想开发出个质量高而且自由操作系统Stallman使用了“自由”(free)这个词不仅意味着用户可以免费获取软件Software;而且更重要是它将意味着某种程度“解放”:用户可以自由使用、拷贝、查询、重用、修改甚至是分发这份软件Software完全没有软件Software使用限制这也正是Stallman创建自由软件Software基金会(FSF)资助GNU软件Software开发本意(FSF也在资助其他科研方面开发工作) 15年来GNU工程已经吸收、产生了大量这不仅包括Emacs、gcc(GNUC编译器)、bash(shell命令)还有大部分Linux用户所熟知许多应用现在正在进行开发项目是GNU Hurd内核这是GNU操作系统最后个主要部件(实际上Hurd内核早已能够使用了不过当前版本号为0.3系统在什么时候能够完成还是未知数)尽管Linux大受欢迎但是Hurd内核还在继续开发原因有几个方面其是Hurd体系结构十分清晰地体现了Stallman有关操作系统工作方式思想例如在运行期间任何用户都可以部分地改变或替换Hurd(这种替换不是对每个用户都是可见而是只对申请修改用户可见而且还必须符合规范标准)另个原因是据介绍Hurd对于多处理器支持比Linux本身内核要好还有个简单原因是兴趣驱动员们希望能够自由地进行自己所喜欢工作只要有人希望为Hurd工作Hurd开发就不会停止如果他们能够如愿以偿Hurd有朝日将成为Linux强劲对手不过在今天Linux还是自由内核王国里无可争议统治者 在GNU发展中期也就是1991年个名叫Linus Torvalds芬兰大学生想要了解Intel新CPU—80386他认为比较好学习思路方法是自己编写个操作系统内核出于这种目加上他对当时Unix变种版本对于80386类机器脆弱支持十分不满他决定要开发出个全功能、支持POSIX标准、类Unix操作系统内核该系统吸收了BSD和 V优点同时摒弃了它们缺点Linus(虽然我知道我应该称他为Torvalds但是所有人都称他为Linus)独立把这个内核开发到0.02版这个版本已经可以运行gcc、bash和很少些应用这些就是他开始全部工作了后来他又开始在因特网上寻求广泛帮助 不到 3年LinusUnix—Linux已经升级到1.0版本它源代码量也呈指数形式增长实现了基本TCP/IP功能(网络部分代码后来重写过而且还可能会再次重写)此时Linux就已经拥有大约10万用户了 现在Linux内核由150多万行代码组成Linux也已经拥有了大约1000万用户(由于Linux可以自由获取和拷贝获取具体统计数字是不可能)Linux内核GNU/Linux附同GNU工具已经占据了Unix 50%市场些公司正在把内核和些应用同安装软件Software打包在起生产出Linux发行版本这些公司包括Red Hat和Caldera 公司现在GNU/Linux已经备受瞩目得到了诸如Sun、IBM、SGI等公司广泛支持SGI最近决定在其基于IntelMerced系列机器上不再搭载自己Unix变种版本IRIX而是直接采用GNU/Linux;Linux甚至被指定为Amiga将要发布新操作系统基础1.2 GNU通用公共许可证 这样个如此流行操作系统当然值得我们学习按照通用公共许可证(GPLGeneral Public License)规定Linux源代码可以自由获取这满足了我们学习该系统强烈愿望GPL这份非同寻常软件Software许可证充分体现了上面提到Stallman思想:只要用户所做修改是同等自由用户可以自由地使用、拷贝、查询、重用、修改甚至重新发布这个软件Software通过这种方式GPL保证了Linux(以及同许可证保证下大量其他软件Software)不仅现在自由可用而且以后经过任何修改的后都仍然可以自由使用 请注意这里自由并不是说没有人靠这个软件Software盈利有些日益兴起公司比如发行最流行Linux发行版本Red Hat就是个例子(Red Hat自从上市以来市值已经突破数十亿美元每年盈利数十万美元而且这些数字还在不断增长)但是任何人都不能限制其他用户涉足本软件Software领域而且所做修改不能减少其自由程度 本书附录B中收录了GNU通用公共许可证全文1.3 Linux开发过程 如上所述由于Linux是个自由软件Software它可以免费获取以供学习研究Linux的所以值得学习研究是它是相当优秀操作系统如果Linux操作系统相当糟糕那它就根本不值得我们使用也就没有必要去研究相关书籍Linux是个十分优秀操作系统还在于几个相互关联原因 原因的在于它是基于天才思想开发而成在学生时代就开始推动整个系统开发Linus Torvalds是个天才他才能不仅展现在编程能力方面而且组织窍门技巧也相当杰出Linux内核是由世界上些最优秀员开发并不断完善他们通过Internet相互协作开发理想操作系统;他们享受着工作中乐趣而且也获得了充分自豪感 Linux优秀另外个原因在于它是基于组优秀概念Unix是个简单却非常优秀模型在Linux创建的前Unix已经有20年发展历史Linux从Unix各个流派中不断吸取成功经验模仿Unix优点抛弃Unix缺点这样做结果是Linux 成为了Unix系列中佼佼者:高速、健壮、完整而且抛弃了历史包袱 然而Linux最强大生命力还在于其公开开发过程每个人都可以自由获取内核源每个人都可以对源加以修改而后他人也可以自由获取你修改后源如果你发现了缺陷你可以对它进行修正而不用去乞求不知名公司来为你修正如果你有什么最优化或者新特点创意你也可以直接在系统中增加功能而不用向操作系统供应商解释你想法指望他们将来会增加相应功能当发现个漏洞后你可以通过编程来弥补这个漏洞而不用关闭系统直到你供应商为你提供修补由于你拥有直接访问源代码能力你也可以直接阅读代码来寻找缺陷或是效率不高代码或是安全漏洞以防患于未然 除非你是个员否则这点听起来仿佛没有多少吸引力实际上即使你不是员这种开发模型也将使你受益匪浅这主要体现在以下两个方面: * 可以间接受益于世界各地成千上万员随时进行改进工作 * 如果你需要对系统进行修改你可以雇用员为你完成工作这部分人将根据你需求定义单独为你服务可以设想这在源不公开操作系统中将是什么样子Linux这种独特自由流畅开发模型已被命名为bazaar(集市模型)它是相对于cathedral(教堂)模型而言在cathedral模型中源代码被锁定在个保密小范围内只有开发者(很多情况下是市场)认为能够发行个新版本这个新版本才会被推向市场这些术语在Eric S. Raymond教堂和集市(The Cathedral and the Bazaar)文中有所介绍大家可以在/~esr/writings/找到这篇文章bazaar开发模型通过重视实验征集并充分利用早期反馈对巨大数量脑力资源进行平衡配置可以开发出更优秀软件Software(顺便说下虽然Linux是最为明显使用bazaar开发模型例子但是它却远不是第个使用这个模型系统) 为了确保这些无序开发过程能够有序地进行Linux采用了双树系统个树是稳定树(stable tree)另个树是非稳定树(unstable tree)或者开发树(development tree)些新特性、实验性改进等都将首先在开发树中进行如果在开发树中所做改进也可以应用于稳定树那么在开发树中经过测试以后在稳定树中将进行相同改进按照Linus观点旦开发树经过了足够发展开发树就会成为新稳定树如此周而复始进行下去 源版本号形式为x.y.z对于稳定树来说y是偶数;对于开发树来说y比相应稳定树大(因此是奇数)截至到本书截稿时最新稳定内核版本号是2.2.10最新开发内核版本号是2.3.12对2.3树缺陷修正会回溯影响(back-propagated)2.2树而当2.3树足够成熟时候会发展成为2.4.0(顺便说下这种开发会比常规惯例要快每版本所包含改变比以前更少了内核开发人员只需花很短时间就能够完成个实验开发周期)及其镜像站点提供了最新可供内核版本而且同时包括稳定和开发版本如果你愿意话不需要很长时间这些站点所提供最新版本中就可能包含了你部分源代码第2章 代 码 初 识 本章首先从较高层次介绍Linux内核源概况这些都是大家关心些基本特点随后将简要介绍些实际代码最后介绍如何编译内核 2.1 Linux内核源部分特点 在过去段时期Linux内核同时使用C语言和汇编语言来实现这两种语言需要定平衡:C语言编写代码移植性较好、易于维护而汇编语言编写则速度较快般只有在速度是关键原因或者些因平台相关特性而产生特殊要求(例如直接和内存管理硬件进行通讯)时才使用汇编语言 正如实际中所做即使内核并未使用C对象特性部分内核也可以在g(GNUC编译器)下进行编译同其他面向对象编程语言相比较相对而言C开销是较低但是对于内核开发人员来说这已经是太多了 内核开发人员不断发展编程风格形成了Linux代码独有特色本节将讨论其中些问题 2.1.1 gcc特性使用 Linux内核被设计为必须使用GNUC编译器gcc来编译而不是任何种C编译器都可以使用内核代码有时要使用gcc特性本书将陆续介绍其中部分 些gcc特有代码只是简单地使用gcc语言扩展例如允许在C(不只是C)中使用inline关键字指示内联也就是说代码中被在每次时都会被扩充因而就可以节约实际开销 般情况下代码编写方式比较复杂对于某些类型输入gcc能够产生比其他输入效率更高执行代码从理论上讲编译器可以优化具有相同功能两种对等思路方法并且得到相同结果因此代码编写方式是无关紧要但在实际上用某种思路方法编写所产生代码要比用另外些思路方法编写所产生代码执行速度快许多内核开发人员知道怎样才能产生更高效执行代码这不断地在他们编写代码中反映出来 例如考虑内核中经常使用goto语句—为了提高速度内核中经常大量使用这种般要避免使用语句在本书中所包含不到40 000行代码中共有500多条goto语句大约是每80行个除汇编文件外精确统计数字是接近每72行个goto语句公平地说这是选择偏向结果:比例如此高原因的是本书中涉及是内核源核心在这里速度比其他原因都需要优先考虑整个内核比例大概是每260行个goto语句然而这仍然是我不再使用Basic进行编程以来见过使用goto频率最高地方 代码必需受特定编译器限制特性不仅和普通应用开发有很大区别而且也区别于大多数内核开发大多数开发人员使用C语言编写代码来保持较高可移植性即使在编写操作系统时也是如此这样做优点是显而易见最为重要点是旦出现更好编译器员们可以随时进行更换 内核对于gcc特性完全依赖使得内核向新编译器上移植更加困难最近Linus对这问题在有关内核邮件列表上表明了自己观点:“记住编译器只是个工具”这是对依赖于gcc特性个很好基本思想表述:编译器只是为了完成工作如果通过遵守标准还不能达到工作要求那么就不是工作要求有问题而是对于标准依赖有问题 在大多数情况下这种观点是不能被人所接受通常情况下为了保证和语言标准致开发人员可能需要牺牲某些特性、速度或者其他相关原因其他选择可能会为后期开发造成很大麻烦 但是在这种特定情况下Linus是正确Linux内核是个特例其执行速度要比向其他编译器可移植性远为重要如果设计目标是编写个可移植性好而不要求快速运行内核或者是编写个任何人都可以使用自己喜欢编译器进行编译内核那么结论就可能会有所区别了;而这些恰好不是Linux设计目标实际上gcc几乎可以为所有能够运行LinuxCPU生成代码因此对于gcc依赖并不是可移植性严重障碍 在第3章中我们将对内核设计目标进行详细介绍说明2.1.2 内核代码习惯用语 内核代码中使用了些显著习惯用语本节将介绍常用几个当通读源代码时真正重要问题并不在这些习惯用语本身而是这种类型习惯用语确存在而且是不断被使用和发展如果你需要编写内核代码你应该注意到内核中所使用习惯用语并把这些习惯用语应用到你代码中当通读本书(或者代码)时看看你还能找到多少习惯用语 为了讨论这些习惯用语我们首先需要对它们进行命名为了便于讨论笔者创造了这些名字而在实际中大家不定非要参考这些用语它们只是对内核工作方式描述而已 个普通习惯用语笔者称的为“资源获取”(resource acquisition idiom)在这个用语中个必须实现系列资源获取包括内存、锁等等(这些资源类型未必相同)只有成功地获取当前所需要资源的后才能处理后面资源请求最后该还必须释放所有已经获取资源而不必考虑没有获取资源 我采用“变量”这用语(error variable idiom)来辅助介绍说明资源获取用语它使用个临时变量来记录期望返回值当然相当多都能实现这个功能但是变量区别点在于它通常是用来处理由于速度原因而变得非常复杂流程控制中问题变量有两个典型值0(表示成功)和负数(表示有错) 如果执行到标号out2则都已经获取了r1和r2资源而且也都需要进行释放如果执行到标号out1(不管是顺序执行还是使用goto语句进行跳转到)则r2资源是无效(也可能刚被释放)但是r1资源却是有效而且必需在此将其释放同理如果标号out能被执行则r1和r2资源都无效err所返回是或成功标志 在这个简单例子中对err些赋值是没有必要在实战中实际代码必须遵守这种模式这样做原因主要在于同行中可能包含有多种测试而这些测试应该返回相同代码因此对变量统赋值要比多次赋值更为简单虽然在这个例子中对于这种属性必要性并不非常迫切但是我还是倾向于保留这种特点有关实际应用可以参考sys_shmctl(第21654行)在第9章中还将详细介绍这个例子 2.1.3 减少#和#def使用 现在Linux内核已经移植到区别平台上但是我们还必须解决移植过程中所出现问题大部分支持各种区别平台代码由于包含许多预处理代码而已经变得非常不规范标准例如: 这个例子试图实现操作系统可移植性虽然Linux关注焦点很明显是实现代码在各种CPU上可移植性但是 2者基本原理是致对于这类问题来说预处理器是种解决方式这些杂乱问题使得代码晦涩难懂更为糟糕是增加对新平台支持有可能要求重新遍历这些杂乱分布低质量代码段(实际上你很难能找到这类代码段全部) 和现有方式区别是Linux般通过简单(或者是宏)来抽象出区别平台间差异内核移植可以通过实现适合于相应平台(或宏)来实现这样不仅使代码主体简单易懂而且在移植过程中还可以比较容易地自动检测出你没有注意到内容:如引用未声明时会出现链接有时用预处理器来支持区别体系结构但这种方式并不常用而相对于代码风格变化就更是微不足道了 顺便说下我们可以注意到这种解决思路方法和使用用户对象(或者C语言中充满指针struct结构)来代替离散switch语句处理区别类型思路方法十分相似在某些层次上这些问题和解决思路方法是统 可移植性问题并不仅限于平台和CPU移植编译器也是个重要问题此处为了简化假设Linux只使用gcc来编译由于Linux只使用同个编译器所以就没有必要使用#块(或者#def块)来选择区别编译器 内核代码主要使用#def来区分需要编译或不需要编译部分从而对区别结构提供支持例如代码经常测试SMP宏是否定义过从而决定是否支持SMP机2.2 代码样例 了解Linux代码风格最好思路方法就是实际研究下它部分代码即使你不完全理解本节所讨论代码细节也无关紧要毕竟本节主要目不是理解代码些读者可以只对本节进行浏览本节主要目是让读者对Linux代码进。

linux启动代码分析--kevy...

linux启动代码分析--kevy...

linux启动代码分析--kevy...我们使用的cpu是采用xscale技术的pxa250,具体的开发板是cerfboard。

这里的代码解释都是根据pxa250进行的。

不过,正如我们下面会看到,大部分的代码都是和具体的pxa型号无关的。

另外,我们列出代码的行号仅仅为了解释方便,并不是代码在源文件中真正的行号。

1. bootloader及内核解压bootloader将内核加载到内存中,设定一些寄存器,然后就将控制权交由内核了,这时并不需要打开MMU功能。

一般,我们所说的内核都是指被压缩了的内核piggy.gz,其与head.o、misc.o和head-xscale.o一起构成最终的内核代码。

启动内核的过程首先包括解压,最先执行的代码是:arch/arm/boot/compressed/head.Sarch/arm/boot/compressed/head-xscale.Sarch/arm/boot/compressed/misc.c上面代码主要任务是解压真正的内核代码,真正的内核代码是从head-armv.S开始,以后提到的内核都是指真正的内核:arch/arm/kernel/head-armv.S2. 内核启动第一阶段内核启动第一阶段是从ENTRY(stext)到c语言函数start_kernel,这一部分涉及的代码有arch/arm/kernel/head-armv.Sarch/arm/mm/proc-xscale.S在bootloader调用内核运行之前,要在指定的寄存器中存放参数,如下:r0 = 0,r1 = machine type number,对于MACH_TYPE_PXA_CERF是139r2 = physical address of tagged list in system RAM.2.1 stextENTRY(stext)是整个内核的入口,其代码位于编译后生成的目标文件kernel.o中的.text.init区(section),最后被链接到内核的.init区。

Linux系统启动深度剖析(二)

Linux系统启动深度剖析(二)

Linux系统启动过程前面我们已经剖析了内核引导和运行init两个部分,后面还有更重要的工作要做就是系统的初始化、对应级别的守护程序启动、建立终端、和完成登陆。

这样就会完成一个完整的Linux系统启动过程。

第三部分:系统初始化在init的配置文件中有这么一行:si::sysinit:/etc/rc.d/rc.sysinit它调用执行了/etc/rc.d/rc.sysinit,而rc.sysinit是一个bashshell的脚本,它主要是完成一些系统初始化的工作,rc.sysinit是每一个运行级别都要首先运行的重要脚本。

它主要完成的工作有:激活交换分区,检查磁盘,加载硬件模块以及其它一些需要优先执行任务。

rc.sysinit约有850多行,但是每个单一的功能还是比较简单,而且带有注释,建议有兴趣的用户可以自行阅读自己机器上的该文件,以了解系统初始化所详细情况。

由于此文件较长,所以不在本文中列出来,也不做具体的介绍。

当rc.sysinit程序执行完毕后,将返回init继续下一步。

第四部分:启动对应运行级别的守护进程在rc.sysinit执行后,将返回init继续其它的动作,通常接下来会执行到/etc/rc.d/rc程序。

以运行级别3为例,init将执行配置文件inittab中的以下这行:l5:5:wait:/etc/rc.d/rc 5这一行表示以5为参数运行/etc/rc.d/rc,/etc/rc.d/rc是一个Shell脚本,它接受5作为参数,去执行/etc/rc.d/rc5.d/目录下的所有的rc启动脚本,/etc/rc.d/rc5.d/目录中的这些启动脚本实际上都是一些链接文件,而不是真正的rc启动脚本,真正的rc启动脚本实际上都是放在/etc/rc.d/init.d/目录下。

而这些rc启动脚本有着类似的用法,它们一般能接受start、stop、restart、status等参数。

/etc/rc.d/rc5.d/中的rc启动脚本通常是K或S开头的链接文件,对于以以S开头的启动脚本,将以start参数来运行。

linux内核启动解析参数

linux内核启动解析参数

在Linux内核启动时,可以通过解析参数来配置内核的行为。

这些参数通常由引导加载程序(如GRUB)或内核命令行传递。

以下是一些常见的内核启动解析参数:
1. `root=`:指定根文件系统的设备或UUID。

例如,`root=/dev/sda1`或
`root=UUID=12345678-1234-5678-1234-56789abcdef`。

2. `init=`:指定作为第一个进程启动的程序的路径。

通常是init进程(如`/sbin/init`)。

3. `ro`或`rw`:指定根文件系统以只读(read-only)或读写(read-write)模式挂载。

4. `quiet`或`silent`:禁止内核在启动期间输出冗长的消息。

5. `loglevel=`:设置内核的日志级别。

较低的值会产生更多的调试信息,较高的值则会减少输出。

例如,`loglevel=7`。

6. `nomodeset`:禁用图形模式设置,可能有助于解决某些图形问题。

7. `acpi=off`:禁用ACPI(高级配置与电源接口)功能。

8. `irqpoll`:在中断处理期间轮询(poll)IRQ线,用于解决某些硬件兼容性问题。

9. `debug`:启用内核调试模式,产生更详细的调试信息。

这只是一小部分常见的内核启动解析参数,实际上还有许多其他可用的参数。

你可以根据需要选择和配置这些参数来满足特定的需求。

2、linux系统启动过程分析(上)

2、linux系统启动过程分析(上)

本章内容

与启动有关的背景知识

PC/AT组成与引导原理 80X86保护模式初步 引导过程的描述 Boot.s的分析 AT&T汇编语言初步 head.s的分析

Linux0.01启动分析

第一节与启动有关的背景知识


PC/AT组成与引导原理 80X86保护模式初步
PC/AT组成与引导原理

指示GDT全局描述符表所在的段 指示LDT局部描述符表所在的段


LDTR局部描述符表寄存器

IDTR中断描述符表寄存器

指示中断描述符表所在的段
指示TSS任务状态段
TR任务寄存器

控制寄存器

4个32位用于控制和确定处理器操作模式 等特性的寄存器


CR0 处理器操作状态 CR1 保留不用 CR2 保存导致页错误的线性地址 CR3 保存页目录表物理内存基地址
BIOS ROM

早期的BIOS多为可重写EPROM芯片,上面的 标签起着保护BIOS内容的作用,因为紫外线照 射会使EPROM内容丢失,所以不能随便撕下。 现在的ROM BIOS多采用Flash ROM(快闪可擦 可编程只读存储器),通过刷新程序,可以对 Flash ROM进行重写,方便地实现BIOS升级。 目前市面上较流行的主板BIOS主要有 Award BIOS、AMI BIOS、Phoenix BIOS三种 类型。Award BIOS是由Award Software公司开 发的BIOS产品,在目前的主板中使用最为广泛。 Award BIOS功能较为齐全,支持许多新硬件, 目前市面上主机板都采用了这种BIOS。 AMI BIOS是AMI公司出品的BIOS系统软 件,开发于80年代中期,它对各种软、硬件的 适应性好,能保证系统性能的稳定,在90年代 后AMI BIOS应用较少;Phoenix BIOS是 Phoenix公司产品,Phoenix BIOS多用于高档的 原装品牌机和笔记本电脑上,其画面简洁,便 于*作,现在Phoenix已和Award公司合并,共 同推出具备两者标示的BIOS产品。

启动信息与RC.d

启动信息与RC.d

Linux启动信息解析组名:Cloud Walker组长:郑祥云组员:郑祥云,徐绩,王新华日期:2010-7-24目录Linux启动信息解析 (1)目录 (2)1.项目需求分析 (3)1.1项目背景: (3)1.2时间: (3)1.3项目要求分析: (3)2.Linux启动过程 (4)2.1 Linux下各个目录的作用 (4)2.2 Linux启动过程 (4)2.2.1 Linux的引导过程 (5)2.2.2 运行级别(run level) (5)2.2.3 /etc/rc.d/与/etc/rc.d/init.d的关系 (5)3. /init/main.c中init()函数 (7)4. 启动信息 (10)1.项目需求分析1.1项目背景:在学习操作系统中,操作系统的如何启动是一个难点,本文基于Linux开源操作系统,分析了从BIOS加载BOOTLOAD,操作系统接管CPU后,init()函数所做的事情以及如何初始化系统的各种服务以及SHELL。

1.2时间:开始时间:2010-7-25 结束时间:2010-7-251.3项目要求分析:1)介绍Linux启动所需的目录。

2)Linux启动过程。

3)/etc/rc.d/与/etc/rc.d/init.d的关系。

4)分析init/main.c代码中init()函数。

2.Linux启动过程2.1 Linux启动所需的目录init.d/ :各种服务器和程序的二进制文件存放目录。

rcx.d/: 各个启动级别的执行程序连接目录。

里头的东西都是指向init.d/的一些软连接。

还有三个脚本:rc.sysinit, rc, rc.local。

/etc/rc.d/init.d/目录下的脚本就类似与windows 中的注册表,在系统启动的时候某些指定脚本将被执行。

在Redhat中,/etc/rc.d/rc.sysinit主要做在各个运行模式中相同的初始化工作,包括:i.调入keymap以及系统字体ii.启动swappingiii.设置主机名iv.设置NIS域名v.检查(fsck)并mount文件系统vi.打开quotavii.装载声卡模块viii.设置系统时钟ix.等等。

Linux内核分析_课程设计

Linux内核分析_课程设计

Linux内核分析_课程设计计算机科学与⼯程学院课程设计报告题⽬全称:Linux内核初起代码分析学⽣学号:姓名:指导⽼师:职称:指导⽼师评语:签字:课程设计成绩:⽬录摘要 (2)第⼀章引⾔ (1)1.1 问题的提出 (1)1.2任务与分析 (1)第⼆章代码分析 (2)2.1系统初始化过程流程 (2)2.2数据结构 (2)2.3常量和出错信息的意义 (4)2.4调⽤关系图 (4)2.5各模块/函数的功能及详细框图 (5)2.5.1 static void time_init(void)分析 (6)2.5.2 void main(void)分析 (6)2.5.3 pause()分析 (8)2.5.4 static int printf(const char *fmt, ...)分析 (8)2.5.5 void init(void)分析 (9)第三章内核调试 (12)3.1运⾏环境 (12)3.2编译内核过程 (12)第四章总结与体会 (15)致谢 (16)参考⽂献 (17)摘要随着计算机的普及,计算机发挥着越来越重要的作⽤,计算机的使⽤也越来越普遍,所以让更多的⼈能够更好的使⽤和掌握⼀些计算机⽅法显得⼗分重要。

充分发挥计算机的作⽤也显得⼗分重要。

操作系统应运⽽⽣。

操作系统是⼀种软件,⽤来帮助其他的程序控制计算机并和⽤户进⾏交互。

因⽽,对操作系统的研究是很有必要的。

操作系统包含了多个部分或者组件,最核⼼的部分是内核。

其他的部分⽤来帮助内核完成计算机资源的管理和应⽤程序的控制。

Linux操作系统是使⽤很⼴泛的,⾼质量的⼀个操作系统。

此次起始代码分析,我分析了init/main.c⽂件中的main()、init()以及编译内核代码。

main()中主要是关于起始的调⽤和设备和系统信息初始化,以及创建进程。

此时中断仍被禁⽌着,做完必要的设置后就将其开启init()是创建进程,并检测是否出错,出错则再次创建执⾏并打印出出错信息。

第2章 Linux操作系统的启动

第2章 Linux操作系统的启动

从硬盘引导时,由于硬盘是可分区的,因此引导过程 比软盘复杂一些.BIOS 比软盘复杂一些.BIOS 首先读取并运行硬盘主引导记录 中的代码,这些代码首先检验主引导记录中的分区表,寻 找到活动分区(即标志为可引导分区的分区),然后读取 并运行活动分区之引导扇区中的代码.活动分区引导扇区 的作用和软盘引导扇区的作用一样:从分区中读取内核映 象并启动内核.和软盘引导不同的是,内核映象保存在硬 盘分区文件系统中,而不象软盘那样保存在后续的连续扇 区中,因此,硬盘引导扇区中的代码还需要定位内核映象 在文件系统中的位置,然后装载内核并启动内核.
2.2 Linux 的引导过程
不同计算机平台引导过程的区别主要在于第一阶段的 引导过程.对 PC 机上的 Linux 系统而言,计算机(即 BIOS)负责从软盘或硬盘的第一个扇区(即引导扇区) BIOS)负责从软盘或硬盘的第一个扇区(即引导扇区) 中读取引导装载器,然后,由引导装载器从磁盘或其他位 置装入操作系统.从软盘引导时,BIOS 置装入操作系统.从软盘引导时,BIOS 读取并运行引导 扇区中的代码.引导扇区中的代码读取软盘前几百个块 (依赖于实际的内核大小),然后将这些代码放置在预先 定义好的内存位置.利用软盘引导 Linux 时,没有文件 系统,内核处于连续的扇区中,这样安排可简化引导过程. 但是,如果利用LILO( 但是,如果利用LILO(LInux LOader)也可从包含文件 LOader)也可从包含文件 系统的软盘上引导 Linux. Linux.
2.1操作系统的启动 2.1操作系统的启动
一般来说,操作系统的引导过程分两个步骤. 首先,计算机硬件经过开机自检(Power 首先,计算机硬件经过开机自检(Power On Self-Test,POST)之后,从软盘或硬盘的固定 Self-Test,POST)之后,从软盘或硬盘的固定 位置装载一小段代码,这段代码一般称为" 位置装载一小段代码,这段代码一般称为"引导 装载器" 装载器".然后,由引导装载器负责装入并运行 操作系统.引导装载器非常小,一般只有几百个 字节,而操作系统庞大而复杂.上述分成两阶段 的引导过程,可将计算机中的固化软件保持得足 够小,同时也便于实现对不同操作系统的引导.

Linux系统启动流程分析-电脑资料

Linux系统启动流程分析-电脑资料

Linux系统启动流程分析-电脑资料一系统上电和启动ROMNOR Flash作为启动ROM的系统启动过程NOR Flash开头处存放启动代码,程序从NOR Flash开始处启动,。

配置EMI寄存器,设置好各存储器的地址和存取规则。

配置电源管理模块,各模块上电。

启动代码将位于NOR Flash中的正式执行代码复制到内存中,以提高执行效率。

设置PC指针,指向NOR Flash中固定地址。

设置地址映射,用0地址映射到内存RAM空间。

设置PC指针,指向RAM中初始化代码,开始执行代码。

NAND Flash作为启动ROM的系统启动流程上电初始,DMA默认设置将存储在NAND Flash中第一页的数据搬运到内部RAM中,然后设置PC到内部RAM开始处的地址,开始执行代码,电脑资料《Linux系统启动流程分析》(https://www.)。

在启动代码中设置中断向量和硬件配置等。

将执行代码搬运到外部SDRAM或DDR=RAM,留出启动代码的位置。

将启动代码搬运到SDRAM或DDR-RAM中首址。

设置Remap,将0地址重新映射到SDRAM或DDR-RAM首地址。

设置PC指针,开始执行正式的执行代码。

二 Bootloader引导三 Linux内核引导非压缩内核:Image压缩内核:zImage内核初始化设备初始化启动内核挂载文件系统启动用户空间进程四 init初始化系统服务初始化log系统解析/init.rc和/init.%hardware%.rc文件,执行early-init,并执行解析出的init动作、early-boot动作、boot动作和execute property动作。

进行设备初始化,属性服务器初始化并开启属性服务。

进入无线循环以等待属性设置或子进程退出事件。

linux源码分析(二)-启动过程

linux源码分析(二)-启动过程

linux源码分析(⼆)-启动过程前置:这⾥使⽤的linux版本是4.8,x86体系。

这篇是的学习笔记。

linux的启动过程有点像是⼩鱼吃⼤鱼,最后吃成⼀个胖⼦。

计算机中的PC寄存器是⽤来指⽰下个执⾏程序。

最开始的时候,pc寄存器都是指向0xfffffff0。

这个程序是指向BIOS的POST程序的。

POST 全称是Power On Self Test,意思是加点⾃检。

过程包括内存检查,系统总线检查等。

POST过程结束,就进⼊到了⾃举过程,⾃举过程把MBR(主引导扇区)加载到内存中,并且执⾏它。

这个主引导扇区是第⼀个扇区的前512字节。

Master Boot Record过程是为了后⾯⼀个过程准备的。

它主要做的是读⼊GRUB stage2所在的扇区。

并且执⾏它。

GRUB stage2 将系统切换到保护模式。

设置C运⾏环境。

然后进⼊到x86/boot/header.S中执⾏。

在这⾥⾯,你能找到main的函数⼊⼝。

这个对应到x86/boot/main.c的main函数。

这个main函数执⾏到最后会进⼊go_to_protected_mode(); 进⼊到pm.c的这个函数的定义,除了初始化⼀些逻辑以外,主要是protected_mode_jump下⾯就进⼊到boot/pmjump.S的protected_mode_jump29 protected_mode_jump:30 movl %edx, %esi # Pointer to boot_params table3132 xorl %ebx, %ebx33 movw %cs, %bx # 将实模式的代码段放⼊ bx34 shll $4, %ebx # 转换为线性地址35 addl %ebx, 2f # 将 in_pm32 的实模式地址转换为线性地址3637 movw $__BOOT_DS, %cx # ds 段选择⼦38 movw $__BOOT_TSS, %di # tss 段选择⼦3940 movl %cr0, %edx41 orb $X86_CR0_PE, %dl # Protected mode42 movl %edx, %cr0 # 将 cr0 的0位置0是进⼊保护模式的标志43 jmp 1f # Short jump to serialize on 386/48644 1:45 # 下⾯这段作⽤是跳转到 in_pm32,由于已经在保护模式,所以需要考虑段的问题46 # Transition to 32-bit mode47 .byte 0x66, 0xea # ljmpl opcode48 2: .long in_pm32 # offset49 .word __BOOT_CS # segment5051 .size protected_mode_jump, .-protected_mode_jump5253 .code3254 .type in_pm32, @function55 in_pm32: # 下⾯的注释挺清楚,就不翻译了56 # Set up data segments for flat 32-bit mode57 movl %ecx, %ds58 movl %ecx, %es59 movl %ecx, %fs60 movl %ecx, %gs61 movl %ecx, %ss62 # The 32-bit code sets up its own stack, but this way we do have63 # a valid stack if some debugging hack wants to use it.64 addl %ebx, %esp6566 # Set up TR to make Intel VT happy67 ltr %di # 这个⽐较有意思6869 # Clear registers to allow for future extensions to the70 # 32-bit boot protocol71 xorl %ecx, %ecx72 xorl %edx, %edx73 xorl %ebx, %ebx74 xorl %ebp, %ebp75 xorl %edi, %edi7677 # Set up LDTR to make Intel VT happy78 lldt %cx # ⼜是⼀个骗 CPU 的东西79 # eax 是 protected_mode_jump 的第⼀个参数,即 header.S 中定义的 boot_params.hdr.code32_start,即 vmlinux 的⼊⼝地址80 jmpl *%eax # Jump to the 32-bit entrypoint8182 .size in_pm32, .-in_pm32最后的jmpl就跳转到arch/x86/kernel/head_32.S的startup_32ENTRY(initial_code).long i386_start_kernel进⼊到arch/x86/kernel/head32.casmlinkage __visible void __init i386_start_kernel(void){cr4_init_shadow();sanitize_boot_params(&boot_params);x86_early_init_platform_quirks();/* Call the subarch specific early setup function */switch (boot_params.hdr.hardware_subarch) {case X86_SUBARCH_INTEL_MID:x86_intel_mid_early_setup();break;case X86_SUBARCH_CE4100:x86_ce4100_early_setup();break;default:i386_default_early_setup();break;}start_kernel();}这⾥最后是调⽤了start_kernel,这⾥的start_kernel是与操作系统⽆关的init/main.c⾥⾯了。

Linux操作系统分析-linux启动代码分析

Linux操作系统分析-linux启动代码分析
Linux Operating System Analysis 17

它们最终都找到i386/boot的Makefile
i386/boot的Makefile

看i386/boot的Makefile z代表压缩;b代表大内核 可见compressed下的vmlinux/bvmlinux为

Linux Operating System Analysis
5
Source Insight

可以用来辅助代码阅读 到/上可以下载到 它的试用版,试用期估计在30天左右
Linux Operating System Analysis
6
Linux-2.6.11
Linux Operating System Analysis 22
Linux的Boot Loader

典型的有:LILO和Grub LILO(Linux Loader)

可以被安装在OS分区的第一个扇区(启动扇区) 也可以代替MBR中的引导程序

事实上,LILO的代码尺寸大于一个扇区,因此被分成 两个部分

在进入bootsect.S的源代码讲解之前,我们先 看一下加载i386内核的内存布局图
Linux Operating System Analysis
26
硬件角度: I386实模式下的内存布局图
0xF0000
ROM-BIOS
VIDEO-BIOS VRAM
0xC0000
0xA0000
1-MB RAM
内核解压缩
Linux Operating System Analysis 11

在arch/i386/kernel目录下的.S文件

嵌入式Linux内核启动部分代码分析

嵌入式Linux内核启动部分代码分析
3. 内核启动地址的确定 内核自解压方式 Head.S/head-XXX.S 获得内核解压后首地址 ZREALADDR,然后解压内核,并把解压后的内核放在 ZREALADDR
的位置上,最后跳转到 ZREALADDR 地址上,开始真正的内核启动。
arch/armnommu/boot/Makefile,定义 ZRELADDR 和 ZTEXTADDR。ZTEXTADDR 是自解压代码的起始地址,如果 从内存启动内核,设置为 0 即可,如果从 Rom/Flash 启动,则设置 ZTEXTADDR 为相应的值。ZRELADDR 是内核解 压缩后的执行地址。
__lookup_processor_type 这个函数根据芯片的 ID 从 获取 proc_info_list 结构,proc_info_list 结构定义在 include/asm-armnommu/proginfo.h 中,该结构的数据定义在 arch/armnommu/mm/proc-arm*.S 文件中,ARM7TDMI 系列
说明: 执行完 decompress_kernel 函数后,代码跳回 head.S/head-XXX.S 中,检查解压缩之后的 kernel 起始地址是否紧挨着 kernel image。如果是,beqcall_kernel,执行解压后的 kernel。如果解压缩之后的 kernel 起始地址不是紧挨着 kernelimage, 则执行 relocate,将其拷贝到紧接着 kernel image 的地方,然后跳转,执行解压后的 kernel。
5.2 setup_arch() setup_arch()函数做体系相关的初始化工作,函数的定义在 arch/armnommu/kernel/setup.c 文件中,主要涉及下列主

[2]Linux的启动过程分析

[2]Linux的启动过程分析

Linux的启动过程1、加载BIOS当启动电源时,计算机首先会从CMOS加载BIOS,并且检查基本的硬件信息,如内存数量,处理器速度,以及硬盘容量等。

之后,BIOS会查找是否存在系统引导盘,如果没有系统系统引导盘,则会转而查找硬盘的MBR,并执行记录在MBR上的程序,这个程序通常就是操作系统的Loader。

Loader的主要功能是用来指示系统在启动之后要加载哪个系统以及到何处加载。

Grub是一种Linux上常用的Loader,记录在MBR上。

2、进入GRUB进入GRUB后,系统会出现列表,表中会出现所有现存的操作系统选项,如2.6.29.4-167.fc11.i686.PAE等。

3、加载Linux内核在GRUB中选择Linux,系统开始加载Liunx内核,此时才正式进入Linux控制。

Liunx 会查找系统上的所有硬件设备并且驱动它们,同时这些信息会显示在电脑屏幕上。

启动后,可以在/var/log/dmesg文件中检查所有的启动信息。

4、执行/sbin/init加载kernel后,紧接着调用/sbin/init程序,也就是系统初始化工作。

init程序执行后,它会成为所有启动程序的父进程,而后执行一连串的程序及Script。

5、执行/etc/re.c/rc.sysint Script/etc/re.c/rc.sysint是一个Script,完成许多功能。

6、执行/etc/inittab Scriptinittab文件中,除了注释行以外,每一行都有如下格式:id: runlevels : actioin : process7、执行init默认Runlevel目录中的所有Script有init默认Runlevel目录中的所有Script,它们都是以S和K开头的程序。

S表示Startup,也就是在启动时要执行的Script,执行顺序由S后面的数字决定,数字越小,启动越早。

而K表示Kill,在退出Runlevel时执行的Script,也以数字为执行优先次序的依据。

linux-mips启动分析(2)

linux-mips启动分析(2)
所以这个函数直接返回。
parse_early_param();
---------------------------------
打印 linux 启动命令行参数。
printk(KERN_NOTICE "Kernel command line: %s\n", boot_command_line);
---------------------------------
设置 CPU 的异常处理函数,TLB 重填,cache 出错,还有通用异常处理表的初始化。
trap_init();
---------------------------------
初始化 RCU 机制,这个步骤必须比本地 timer 的初始化早。
在考察结束时,分配将从 ZONE_HIGHMEM 回退到 ZONE_NORMAL,
在分配时从 ZONE_NORMAL 退回到 ZONE_DMA 就不会回退了。
build_all_zonelists();
---------------------------------
linux-2.6.22.8
V 0.1
欢迎转载,但请保留作者信息
********************************************
linux 内核启动的第一个阶段是从 /arch/mips/kernel/head.s文件开始的。
并且初始化进程 0 ,即 idle 进程,但是并没有设置 idle 进程的 NEED_RESCHED 标志,
以完成内核剩余的启动部分。
---------------------------------
preempt_disable();

ARM_Linux启动代码分析

ARM_Linux启动代码分析

首先,porting linux的时候要规划内存影像,如小弟的系统有64m SDRAM,地址从0x 0800 0000 -0x0bff ffff,32m flash,地址从0x0c00 0000-0x0dff ffff.规划如下:bootloader, linux kernel, rootdisk放在flash里。

具体从0x0c00 0000开始的第一个1M放bootloader,0x0c10 0000开始的2m放linux kernel,从0x0c30 0000开始都给rootdisk。

启动:首先,启动后arm920T将地址0x0c00 0000映射到0(可通过跳线设置),实际上从0x0c00 0000启动,进入我们的bootloader,但由于flash速度慢,所以bootloader前面有一小段程序把bootloader拷贝到SDRAM 中的0x0AFE0100,再从0x 0800 0000 运行bootloader,我们叫这段小程序为flashloader,flashloader必须要首先初始化SDRAM,不然往那放那些东东:.equ SOURCE, 0x0C000100 bootloader的存放地址.equ TARGET, 0x0AFE0100 目标地址.equ SDCTL0, 0x221000 SDRAM控制器寄存器// size is stored in location 0x0C0000FC.global _start_start: //入口点//;***************************************//;* Init SDRAM//;***************************************// ;***************// ;* SDRAM// ;***************LDR r1, =SDCTL0 //// ; Set Precharge CommandLDR r3, =0x92120200//ldr r3,=0x92120251STR r3, [r1]// ; Issue Precharge All CommadLDR r3, =0x8200000LDR r2, [r3]// ; Set AutoRefresh CommandLDR r3, =0xA2120200STR r3, [r1]// ; Issue AutoRefresh CommandLDR r3, =0x8000000LDR r2, [r3]LDR r2, [r3]LDR r2, [r3]LDR r2, [r3]LDR r2, [r3]LDR r2, [r3]LDR r2, [r3]LDR r2, [r3]// ; Set Mode RegisterLDR r3, =0xB2120200STR r3, [r1]// ; Issue Mode Register CommandLDR r3, =0x08111800 //; Mode Register V alue LDR r2, [r3]// ; Set Normal ModeLDR r3, =0x82124200STR r3, [r1]//;*************************************** //;* End of SDRAM and SyncFlash Init *//;***************************************// copy code from FLASH to SRAM_CopyCodes:ldr r0,=SOURCEldr r1,=TARGETsub r3,r0,#4ldr r2,[r3]_CopyLoop:ldr r3,[r0]str r3,[r1]add r0,r0,#4add r1,r1,#4sub r2,r2,#4teq r2,#0beq _EndCopyb _CopyLoop_EndCopy:ldr r0,=TARGETmov pc,r0欲知后事如何,下回分解:长篇连载--arm linux演艺---第二回--------------------------------------------------------------------------------上回书说到flashloader把bootloader load到0x0AFE0100,然回跳了过去,其实0x0AFE0100 就是烧在flash 0x0C000100中的真正的bootloader:bootloader 有几个文件组成,先是START.s,也是唯一的一个汇编程序,其余的都是C写成的,START.s 主要初始化堆栈:_start:ldr r1,=StackInitldr sp,[r1]b main//此处我们跳到了C代码的main函数,当C代码执行完后,还要调用//下面的JumpToKernel0x跳到LINXU kernel运行.equ StackInitV alue, __end_data+0x1000 // 4K __end_data在连结脚本中指定StackInit:.long StackInitV alue.global JumpToKernelJumpToKernel:// jump to the copy code (get the arguments right)mov pc, r0.global JumpToKernel0x// r0 = jump address// r1-r4 = arguments to use (these get shifted)JumpToKernel0x:// jump to the copy code (get the arguments right)mov r8, r0mov r0, r1mov r1, r2mov r2, r3mov r3, r4mov pc, r8.section ".data.boot".section ".bss.boot"欲知bootloader中的c代码如何运行,请看下集书接上回:下面让我们看看bootloader的c代码干了些什么。

嵌入式Linux系统的引导过程及启动代码分析

嵌入式Linux系统的引导过程及启动代码分析

面介绍相关技术 F
的作用 y 嵌入式系统中 M N N O b N g [ Z h
嵌 入式 系 统 中 首 先 要 考 虑 的 是 启 动 问 题 E 当一个 微处理 器最 初启动 时 E 它首先执行一个预定地址处的 指 令E 这 个 地 址 处 存 放 系 统 初 始 化 或 引 导 程 序E 正如
2 3 45 X P ^ ^ X ^ ^ 8 Z % WX * W% _ , ‘ L $ , 9 a ) M ‘ L 6 W_ 提供一组 2 主要是 9 c a b N a c 3 4 协处理器相关函数" 对2 的操作 % a ’ , 为中断和异常提供处理函数 9 c L M * , N b * & a c 串口的设备驱动 " 主要对串口 的 控 制 寄 存 9 c & , L % $ a c 器操作 9 c c 全局开始启动代码 & * % * & 8 Z % Wd * d 7 8 Z Z % * Wd Y e f L g Wb a O O h
代码分析 V 4 ( ) ) *
是 一 个 开 源 的 固 件 程 序" 它能实现对 4 ( ) ) * 加载操作系统及其它的 ( 2 3 4 及 主 板 的 初 始 化" ) ) * 等功能 并提供了命令行操作 下的 丰 富 的功 能 " ! L W% 6 , 它 提 供 对 多 种 处 理 器 的 支 持! 下 面 以 硬 件 开 发 板 启 动 部 分 的 代 码 进 行 分 析! X Y 7 8为 例 对 4 ( F 3 ) ) * 为核 ! X Y 7 8板以 /. 8 Z F 3 0X * 的目录结构 V 9 : [ \ ] ] <
的基本框架结构 1 ( ) ) * $ ) % + , 大部分的 ( 依赖于 2 ) ) * $ ) % + , 3 4 的体系结构和 嵌 入 式 板 级 设 备 的 配 置" 因 此" 可分为两 ( ) ) *$ ) % + , 部 分5 用汇编语言实现的依赖于 2 3 4 体系结构的代 码& 7和用 2语言实现的代码 & 8 ! * % 6 , * % 6 , 1 9 : ; :的操作 < = > ? 包括以下步骤 5 @ 基本硬件的初始化 " A 屏蔽所有中断 ! B置 2 3 4 速度和时钟频率 ! C 初始化 . /0! D 初始化 E F G 或 4/. H! 为加载 准备 I 8 & * % 6 , . /0 空间 ! @ 复制 & 8到 . * % 6 , /0 中 ! 为执行 2语言代码做准备 ! " J 3 @ 跳转到 & 8的 2语言入口点 ! * % 6 , 的基本操作 1 9 K ; K < = > ? @ 初始化本阶段要用到的硬件设备 ! # $ % & ’上的内核 映像读到预留的 . 先确定这些预留的 /0 空 间 之 前 " 空间哪些被真正映射到了 . /0 . /0 地址单元 ! @ 加载内核映像和根文件系统映像 !

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-mips启动分析

linux-mips启动分析

linux-mips启动分析(1)系统加电起动后,MIPS 处理器默认的程序入口是0xBFC00000,此地址在无缓存的KSEG1的地址区域内,对应的物理地址是0x1FC00000,即CPU从0x1FC00000开始取第一条指令,这个地址在硬件上已经确定为FLASH的位置,Bootloader将Linux 内核映像拷贝到RAM 中某个空闲地址处,然后一般有个内存移动操作,目的地址在arch/mips/Makefile 内指定:load-$(CONFIG_MIPS_PB1550) += 0xFFFFFFFF80100000,则最终bootloader定会将内核移到物理地址0x00100000 处。

上面Makefile 里指定的的load 地址,最后会被编译系统写入到arch/mips/kernel/vmlinux.lds 中:OUTPUT_ARCH(mips)ENTRY(kernel_entry)jiffies = jiffies_64;SECTIONS{. = 0xFFFFFFFF80100000;/* read-only */_text = .; /* Text and read-only data */.text : {*(.text)...这个文件最终会以参数-Xlinker --script -Xlinker vmlinux.lds 的形式传给gcc,并最终传给链接器ld 来控制其行为。

ld 会将 .text 节的地址链接到0xFFFFFFFF80100000 处。

关于内核ELF 文件的入口地址(Entry point),即bootloader 移动完内核后,直接跳转到的地址,由ld 写入ELF的头中,其会依次用下面的方法尝试设置入口点,当遇到成功时则停止:a. 命令行选项-e entryb. 脚本中的ENTRY(symbol)c. 如果有定义start 符号,则使用start符号(symbol)d. 如果存在 .text 节,则使用第一个字节的地址。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
主要相关的代码是在arch/i386/boot中: bootsect.S:这是linux引导扇区的源代码 setup.S:这是辅助程序的一部分,初始化工作 video.S:这是辅助程序的另外一部分,用于引

导过程中的屏幕显示 另外,子目录compressed中还有两个源代码文件 head.S,misc.c。用于内核映象的解压缩。也 属于辅助程序一部分。
# #
系统引导-bootsect.S中部分代码的解释
这段代码将启动扇区代码由0x7C00移至 0x90000处。 Linux将地址为0x90000的代码段称为 INITSEG。然后跳转到go标志,准备一块堆 栈,栈底位于$INITSEG:0x4000-12
i386内核从实模式开始启动运行
首先看一下什么是实模式 实模式是为了兼容早期的CPU而设臵的 i386系统总是开始于实模式 实模式下
i386内核的启动
启动方式
BIOS 0x7c00: 0x90200: bootsect setup
LILO,Grub等boot loader
加载i386内核的内存布局图
zImage/Image的内核加载器所使用的经 典的内存布局(1M=0x100000)
软盘启动,bootsect.S
setup
32位启动代码 在init目录下
这是体系结构无关部分,i386体系结构相关部分的启动, 其目的就是进入main.c中的start_kernel处执行
Linux系统引导:
过程
定义:指计算机启动时,由系统BIOS以及操作系 统引导程序,将操作系统内核可执行代码逐级装 入内存并开始执行,直到系统控制台显示 “login:”登录提示符位为止的系统引导阶段。几 个阶段:

对于小内核:0x10000(即64K处),称为低装载 对于大内核:0x100000(即1M处),称为高装载
跳转到setup处运行,转入实模式下的系统初始化
系统引导
bootset.S将自身转移到0x90000处,然后跳转到 那里继续执行,并通过BIOS提供的“int 0x13”调 用从磁盘上读入setup.S和内核的映象,然后跳转 到setup.S的代码中,为执行内核映象做准备. bootsect.S中部分代码的解释如下所示:


由BIOS加载操作系统引导程序 由操作系统引导程序加载操作系统内核 内核代码解压缩 内核初始化 生成init进程 系统初始化,shell命令文本的执行 生成各终端进程。
下面根据在bzImage/zImage中的顺序,我们依 次看启动相关的源代码和相关概念



arch/i386/boot/bootsect.S arch/i386/boot/setup.S arch/i386/boot/compressed/head.S arch/i386/kernel/head.S 最后进入kernel/main.C

根据配臵,操作系统可以在软盘/硬盘/CD_ROM上


把对应设备的第一个扇区的内容(boot loader或 部分)拷贝到RAM(0000:0x7c00)处 跳转到0000:0x7c00处执行
BIOS启动
加电开机后,intel CPU在实模式下工作, 只能使用低端的640KB(即0XA0000以下)的 内存空间 由ROM BIOS或者LILO将启动盘的第一扇区 (引导扇区)的内容装入起始地址为0x7c00 的内存空间,然后跳转到0x7c00开始执行引 导扇区的代码 该引导扇区内的代码就是bootset.S汇编后 生成的二进制代码
Linux的Boot Loader
典型的有:LILO和Grub LILO(LInux LOader)

可以被安装在OS分区的第一个扇区(启动扇区) 也可以代替MBR中的引导程序
事实上,LILO的代码尺寸大于一个扇区,因此被分成两个 部分

MBR或启动扇区部分 剩余部分
第一部分也被BIOS装载到RAM中0x7c00的位臵 第一部分在运行时将自己完整的装载到RAM中
第2章 基于i386体系结构的 Linux启动代码分析 系统初始化所包括的内容


基础知识 系统引导 通过LILO进行引导 实模式下的系统初始化 保护模式下的系统初始化 启动核心 Init进程
本章引言
引导涉及到系统各个部分的数据结构,因此,只 有随着课程的深入,才会彻底弄清楚引导程序中 的初始化工作 不同OS的引导程序可能会不同,但是仍有一些普 遍的原理可以遵循 启动核心部分侧重对原理的理解,增强大家的感 性认识
i386的启动代码文件
在arch/i386/boot目录下
I386的体系结构相关部分的启动代码 都采用汇编码写的
启动扇区中的启动代码, 其目标码必然是512字节 I386初始化
在arch/i386/boot/compressed目录下
内核解压缩
在arch/i386/kernel目录下的.S文件

POST自检
POST是系统测试程序,它对系统进行比较全面的测 试,主要包括: 对CPU、定时器、DMA控制器、中断控制器、内存 中的RAM和ROM、键盘、磁盘驱动器、异步通信接 口、打印机配臵台数等十几项内容进行测试。 这个测试过程叫做POST(上电自检)操作。
BIOS启动
加电,RESET引脚 初始化寄存器;CS:IP = 0xfffffff0, in ROM(CPU 要执行的第一条指令) 该地址中有一条JMP指令,跳转地址通常是BIOS的入 口地址(进行POST)。 ROMBIOS BIOS启动内容 POST(上电自检) 初始化硬件设备 搜索一个操作系统来启动
Linux的Boot Loader
通常LILO或GRUB会显示一个已安装操作系统的列 表 按照用户的选择(或者按照缺省项)装载目标操 作系统运行

可能装载操作系统指定的启动代码运行 可能直接装载操作系统内核来运行
LILO的OS启动过程
显示“Loading…” 操作系统前512字节(一个扇区大小,bootsect) 的内容被装载到RAM的0x90000 紧接着的内容(setup)被装在到0x90200 其他操作系统内核被装载到

地址总线:20位 内存范围:0-1MB 逻辑地址 = 段地址 + 段内偏移

段地址 = 段寄存器中的值*16 (或左移4位) 段寄存器: cs/ds/es/fs/gs 段寄存器长度:16bit 段长:16位偏移64KB
实模式下的系统初始化
setup.S连同内核映象由bootsect.S装入。 setup.S从BIOS获取计算机系统的参数,放到内存 参数区,仍在实模式下运行 CPU在setup.S的执行过程中转入32位保护模式的 段式寻址方式 辅助程序setup.S为内核映象的执行做好准备,然 后跳转到0x100000开始内核本身的执行,此后就 是内核的初始化过程
ljmp …… 0x90200 go:…… ljmp ……

0x90000
sys
0x10000
0x7E00
0x7C00 0x4000 0x4000-12
ljmp …… go:…… ljmp ……
cs:ip cs:ip cs:ip cs:ip 低 ss sp
硬盘启动,两阶段引导
装载LILO(LInuxLOader)
系统引导-bootsect.S中的部分代码
movw $BOOTSEG, %ax movw %ax, %ds # %ds = BOOTSEG,将ds段寄存器设为 0x7c00 movw $INITSEG, %ax movw %ax, %es # %ax = %es = INITSEG,将es段寄存器设 为0x9000 movw $256, %cx #移动计数值=256 subw %si, %si #源地址ds:si=0x07c0:0x0000 subw %di, %di #目标地es:di=0x9000:0x0000 cld #清方向标志位 rep #重复执行直到cx=0 movsw #移动1个字 ljmp $INITSEG, $go #间接跳转,INITSEG指出跳转到的段地址 … go: movw $0x4000-12, %di # 0x4000 is an arbitrary value >= # length of bootsect + length of setup + room for stack; 12 is disk parm size. movw %ax, %ds
高 cs:ip cs:ip cs:ip cs:ip
0x7c000x90000 0x7c00, BIOS 0x90000, lilo 堆栈,0x3ff4(0x4000-12), 向下增长 磁盘参数表,12Bytes,0x3ff4~0x4000 显示“Loading” Setup.S0x90200 操作系统内核 小内核,0x10000(64KB处),低装载 大内核,0x100000(1MB处),高装载 setup
内核可执行代码在内存中的首地址是否可随意选 择?为什么?
实模式下的系统初始化-setup.S
版本检查和参数设臵 检查签名“55AA5A5A”,此签名位于setup.S代码段的末尾, 判断安装程序是否完全安装进来 判断内核(kernel)是否为BIG_KERNEL 设臵参数 为进入保护模式做准备,主要包括 关中断 检查自身(setup.S)是否在SETUPSEG处 臵idt(中断描述符表)为空,设臵gdt(全局描述符表) 真正进入保护模式
Linux的Boot Loader
通常LILO或GRUB会显示一个已安装操作系统的列 表 按照用户的选择(或者按照缺省项)装载目标操 作系统运行

可能装载操作系统指定的启动代码运行 可能直接装载操作系统内核来运行
相关文档
最新文档