nullLinux操作系统
——内存寻址 保护模式Linux操作系统分析
——内存寻址 保护模式主要内容主要内容操作系统发展Linux内核组成内存寻址——实模式与保护模式Linux的段与页计算机系统的启动一、操作系统概述一、操作系统概述不同角度看操作系统(地位、作用)
普通用户:图形界面、字符命令界面
开发者:操作系统编程接口
操作系统设计者:设计系统5大功能及结构
计算机系统:硬件与软件的桥梁
发展演变
主旋律:多道、分时
滞后于计算机语言的发展,至今还没有彻底对象化;仍在探索单内核和微内核的最佳组合方式。
linux打破了传统的封闭式开发,开源社区如火如荼nullUnix/Linux系统发展
MULTICS
UNIX
LINUX
POSIX、GNU、GPL
开发模式:遵循GPL,开放源代码、利用internet,通过BBS、新闻组及电子邮件,集体协作地开发。
自由程序员提交代码,只有领导者才有权限决定把哪些代码合并到正式核心版本中。
大量用户测试、高经验程序员裁决,linux的正式发布代码质量很高。null单内核
整个系统是一个大模块,模块间交互时直接调用其他模块的函数。注重执行效率。
微内核
服务尽可能的分离出内核,变成相对独立的模块,模块、微内核通过通信机制交互,相对效率较低些,但方便修改维护。
尚处于发展阶段。null传统Unix内核都是单内核结构,几乎都要硬件提供页机制以管理内存
Linux即不是层次结构,也不是微内核结构,是单内核结构,但也有类似微内核的优点
模块化设计
抢占式内核
支持内核线程
动态装载内核模块二、Linux内核组成二、Linux内核组成nulllinux内核除系统调用外,主要有5个子系统null进程调度:基于优先级和时间片,处于中心地位,其他子系统均依赖于它。
内存管理
虚拟文件系统:
linux把设备看做特殊文件,对设备的操作和文件的操作都是通过虚拟文件系统。该部分可分两部分:逻辑文件系统和设备驱动程序。
4. 网络功能
现代操作系统都支持网络功能,该部分分为:网络协议和网络驱动程序。
5. 进程间通信:协调程序执行不可或缺的一部分null学习Linux操作系统的实现
理解原理
理解运行机制出发,分析数据结构、相关应用等。
参考内核源代码
实现的具体细节最终落到源代码上。在理论明了、有一定的程序基础后再梳理代码,这是需要反复且花功夫的一件事。
Linux源代码的阅读方法Linux源代码的阅读方法阅读顺序
纵向:顺着程序的执行顺序逐步进行。
横向:分模块进行
划分不是绝对的,而是经常结合在一起进行
Linux的启动:顺着linux的启动顺序读,大致
如下(以X86平台为例):
./arch/x86/boot/bootSect.S
./arch/x86/boot/setup.S
./arch/x86/kernel/head.S
./init/main.c中的start_kernel()
对于内存管理等部分,可以单独拿出来按模块进行阅读分析。null阅读工具
帮助追踪复制调用,数据结构定义等
windows环境下利用Source Insight
linux环境下利用lxr(linux cross reference)或glimpse等
开始
一般按顺序阅读启动代码;
然后进行专
阅读,如进程部分,内存管理部分等。在每个功能函数内部应该一步步来
OK,为了更好理解,反复读。null
从启动开始,处理器就在不断的取指令、执行指令。
内存寻址是关系系统运行很重要的一个问题,先进行讨论:
处理器怎么从内存中找到一条指令?三、内存寻址三、内存寻址处理器的发展
操作系统是以硬件为基础的,linux支持的处理器平台有ARM 、i386 、Alpha等
Intel公司的i386是使用最广泛的
intel最早推出了微处理器史上第一款微处理器芯片4004,4位处理器。
随后,intel又推出8位的8080处理器,而今已发展至64位处理器nulln位处理器,一般指ALU宽度。
8位:8080
16位:8086、8088、80286
32位:80386
i386系列开始
内存寻址亦随着处理器变化而变化:
16位、24位、32位
1、内存寻址的演变1、内存寻址的演变8位时代——8080
系统总线的数据线通常与ALU同宽度
从程序设计的角度,地址最好与一个整数的长度一致。
8位数据线、8位地址线?
NO! 8位数据线、16位地址线?
因为8位的cpu寻址能力,仅寻址256B (28)内存太小了。所以地址线采用16位寻址 (216B=64KB) ,于是也造成了cpu内部结构的不均匀性。null8位寄存器如何寻址16位地址?
8080利用1个8位主累加器和若干8位次累加器(寄存器)的计算实现16位内存地址访问。
此时的内存访问是绝对地址访问,程序中的地址就是实际物理地址(程序直接操作寄存器、内存单元等,与硬件相关)。
没有段的概念。
null16位时代——8086、8088
8086处理器是intel x86王朝的开始,因为引入了重要概念——段。段是怎么来的?
8086ALU的宽度即数据总线是16位
内存地址线设置16位的话就一致了。
但16位大小的内存空间似乎满足不了未来需要。Intel最终决定内存寻址目标为20位(1MB)。
位数不一致,相差4位!!!16位的寄存器如何进行20位的地址计算?
null
216
220=24*216=16* 216
启发:
基地址 + 16位偏移
地址一般都是按字节编址0 0000
0 0001
…
0 FFFF1 0000
1 0001
…
1 FFFFF 0000
F 0001
…
F FFFF…内存地址null16位逻辑地址300F的内存寻址过程示意0000
0001
300F
…
FFFF
源程序00000
内存
1M
FFFFF给定一个基地址,进程地址总能访问从此开始的64KB(216)连续地址空间0
7
F
3
F增设4个16位
段寄存器:CS、DS、SS、ESnull1、16位的寄存器如何进行20位的地址计算?
寄存器中的基地址a与指令中的逻辑地址b均是16位,但通过移位处理,可计算得20位物理内存地址
null2、指令中的逻辑地址
寻址中处理的逻辑地址是16位,即>64K的程序是无法编址在一段的,所以程序需被编译成若干段,每段不大于64K。逻辑地址为:段地址+偏移量,每段内偏移量都可从0开始源程序null段式内存管理的好处
程序员开始获得了自由。程序的地址不再需要硬编码了,调试错误也更容易定位了
更可贵的是支持更大的内存地址。16位地址只能定义64KB大小的内存空间,而采用20位则增大了24倍可寻址内存,在当时是很可观的。
问题:修改段寄存器的指令不是特权指令
若用户程序修改了段寄存器里的内容,可以访问内存任何地址不受限制。
缺乏访问限制,其他安全管理更无从谈起
此时的分段,缺乏对内存空间的保护。为区别后来的保护模式,称为“实地址模式”。null过渡——80286
16位处理器24位内存寻址。
不直接从段寄存器中获得段的起始地址,而需要经过额外转换和检查 。保护模式开始出现。
缺陷:
系统启动时处理器处于实模式访问1M空间,经过处理可进入保护模式,访问到16M空间,但是无法从保护模式返回到实模式
每个段大小不超过64k,程序规模受限制。null32位时代——80386
保护模式:
系统态和用户态分离,设定特权指令
关于寻址:
32位数据总线;32位地址线的内存寻址也足够用(4G)
总线宽度一致,是否还分段?是否还要段寄存器?
要向下兼容,支持16位处理器的实模式分段,所以仍需保留分段模式,保留段寄存器
仍用分段机制,实现32位保护模式并支持16位实模式。如何实现呢???null16位实模式
段寄存器存放16位的段基址。
控制寄存器CR0的第0位是一个保护允许
位PE,若为0,则处理器在实模式下运行。
32位指令指针寄存器EIP的低16位作为IP寄存器,存放偏移量,与CS中的段基址加和得到下一条指令地址。null32位保护模式
16位段寄存器存放不了32位的基地址。
另外,为了实现保护,除了段的基地址,还需要更多其他信息:段长、访问权限等。即需要一个数据结构。
16位段寄存器功能变化:存放基地址所在数据结构的地址指针,而不是存段的基地址。+首址null0000 0000
FFFF FFFF
……
0000 0000
内存
FFFF FFFF 累加32位32位
基地址数据结构中某一项的索引号权限
判断越界
判断段相关信息的数据结构GDTR
或LDTR数据结构的首地址段选择符
段描述符表
(段表)寻址过程null过程描述
判断指令类型,确定使用哪个段寄存器
读段寄存器的内容,找到存放段描述信息的数据结构。(GDTR、LDTR)
通过TI标志,判断本次操作所用段是到全局段描述表中找,还是到局部段描述表中找
读GDTR或LDTR寄存器中存放的地址,找到描述表的首地址
根据段寄存器中记录的索引号从描述表首址处偏移,找到第n个描述符,既是要找的段信息。
得到基地址
指令地址做偏移,判断是否长度越界
根据指令性质及段描述符中的访问权限判断是否越权
将基地址与指令中的偏移地址相加得到实际的内存地址,完成地址映射。null相关名词
段描述符表
段描述符:段描述符表中的一项,表示一段的信息:
段的基地址(Base Address):在线性地址空间中段的起始地址。
段的界限(Limit):即段大小,在虚拟地址空间中,段内可以使用的最大偏移量。
段的保护属性(Attribute): 表示段的特性。例如,该段是否可被读出或写入,或者该段是否作为一个程序来执行,以及段的特权级等等。
段选择符(段寄存器 )
包括:索引、TI、RPL
存放段描述符在段表中的索引号(段编号),TI标志用于说明是是GDT还是LDT表,RPL标志权限。
null保护的表现
界限保护
越界判断:段长参数
特权指令:新增的GDTR或LDTR寄存器不存在与旧指令兼容问题,访问他们的指令设定为特权指令,段寄存器的访问属性仍然同以往一样无特权,既保持了兼容,又保证了程序无法故意修改段描述进行越界。
权限保护
根据段的保护属性判断是否具有访问权限。如,只读段中不允许写入。
系统态、用户态分离。段寄存器中的后两位RPL表示请求者操作要求的特权级。GDTR或LDTR中的dpl字段设定了段的访问权限。指令段rpl标志要求的权限应不低于dpl
的级别。特权指令只能在系统态执行。null地址转换及保护图 2、实模式和保护模式2、实模式和保护模式实地址模式
寻址范围1MB(220),始用于16位的8086和8088
用CS、DS、SS、ES解决数据地址总线与ALU宽度不一致问题
有分段雏形,但不具备段的范围限长和特权级别等保护机制
保护模式
始于286,完善于386。继承了实模式下的16位段寄存器
直接寻址范围4GB(232), 虚拟存储空间可达64TB(246=213*232*2)
提供了实现分段和分页的虚拟存储的硬件机制
是操作系统实现多进程存储管理以及提供存储保护的硬件基础null3、IA32处理器中的寄存器
80386以后直至奔腾系列,体系结构均无本质变化,80386以后的处理器统称为IA32(32bit intel architecture)
支持32位处理器,同时要向下支持16位处理器,其内部寄存器相关变化如下:
想一想:上面的分段流程中,相关数据会存放在下面的哪个寄存器中。null①通用寄存器
8个32位通用寄存器:EAX ,EBX ,ECX ,EDX ,EBP ,ESP, ESI及 EDI。支持1、8、16、32位计算及16、32内存寻址
16位操作时,低部分做8个16位寄存器:AX, BX …DI
8位操作时, EAX ,EBX ,ECX ,EDX低16位被分为8位一组的高低两部分,做8个8位寄存器。AH,AL,BH,BL…null②段寄存器
4个16位的段寄存器:CS、DS、SS、ES
16位寻址时,分别用于存放可执行代码的代码段、数据段、堆栈段和其他段的基地址。
32位寻址时,段寄存器中存放的不再是某个段的基地址,而是某个段的选择符(Selector) 。段基地址存放在内存的段描述符表(Descriptor )中,选择符即表索引
null③指令指针寄存器EIP
存放下一条将要执行指令的偏移量(offset ),偏移量加上当前代码段的基地址,就形成了下一条指令的地址。
EIP中的低16位可以被单独访问,给它起名叫指令指针IP寄存器,用于16位寻址。 null④标志寄存器
标志寄存器EFLAGS存放有关处理器的控制标志,很多标志与16位FLAGS中的标志含义一样。
⑤增加4个32位控制寄存器CR0-CR3,用于分页。通过标志位控制,可选择是否分页,以及是实模式还是保护模式寻址。
⑥增加4个系统地址寄存器,8个调式寄存器,2个测试寄存器。null4、物理地址、虚拟地址及线性地址 将主板上的物理内存条所提供的内存空间定义为物理内存空间,其中每个内存单元的实际地址就是物理地址
将应用程序员看到的内存空间定义为虚拟地址空间(或地址空间),其中的地址就叫虚拟地址(或虚地址),分段机制下,一般用“段:偏移量”的形式来描述
线性地址空间是指一段连续的,不分段的,范围为0到n的地址空间,一个线性地址就是线性地址空间的一个绝对地址。举例:程序中的段举例:程序中的段Win32汇编程序:Hello World! .386 .model flat, stdcall option casemap :none ; case sensitive include \masm32\include\windows.inc include \masm32\include\kernel32.inc include \masm32\include\user32.inc
includelib\masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib
.data 数据段
MsgBoxCaption db “Hello”,0 MsgBoxText db “Hello World!”,0 .code 代码段 start: invoke MessageBox, NULL, addr MsgBoxText, addr MsgBoxCaption, MB_OK invoke ExitProcess,0 end start分段的地址转换分段的地址转换源代码
|
编译器
|
虚拟地址—段:偏移
(非线性)
|
(段式)内存寻址
|
物理内存地址
(32位线性空间)null例:c程序helloworld反汇编后代码段的虚拟地址
linux下ELF格式的可执行文件,ld总是从地址0x8000000开始安排程序代码段,每个程序都这样。可以看到段内是线性的。指令地址=段+偏移。null* 段式存储管理的问题:
灵活性和效率都比较差.
一方面 "段"是可变长度的,这就给磁盘交换操作带来了不便;
另一方面,如果为了增加灵活性而将一个进程的空间划分成很多小段,势必要求在程序中频繁地改变段寄存器的内容。同时如果段分小,段数量就多,虽然一个段描述表中可以容纳 8192 个描述项(因为有 13 位下标) ,也未必能保证足够使用。分页的地址转换分页的地址转换内存管理有两种,一种是段式管理,另一种是页式管理,而页式管理更为先进。
从 80 年代中期开始,页式内存管理进入了各种操作系统(以 Unix 为主)的内核,一时成为操作系统的一个热点。
源代码
|
编译器
|
虚拟地址—线性地址
|
(页式)内存寻址
|
物理内存地址
(32位线性空间)null对Intel来说,其 80286 开始实现分段的“保护模式” 。但是很快就发现,光有段式内存管理而没有页式内存管理会使它的 X86 系列逐渐失去竞争力以及作为主流 CPU 产品的地位。
于是,在不久以后的 80386 中就实现了对页式内存管理的支持。null80386 的分页
保护模式的实现是与段式存储密不可分的
如果利用部分已经存在的资源,而不是完全另起炉灶,就无法绕过段式存储管理来实现单纯的页式存储管理。
所以,80386 的系统结构决定了它的页式存储管理只能建立在段式存储 管理的基础上。
分段后分页的地址转换分段后分页的地址转换先用段式管理生成 32 位线性地址
再用页式决定最后的物理地址。
386系列处理器上,无论是 linux还是 windows,都需要分段后分页,才能生成真正的物理地址源代码
|
编译器
|
虚拟地址—段:偏移
(非线性)
|
段机制
|
线性地址
(32位线性空间)
|
(页式)内存寻址
|
物理内存地址
(32位线性空间)null四、Linux中的段与页Linux是怎样处理段机制??null多数平台并不支持分段机制,而是线性地址分页机制
单纯适应IA32结构,则不好移植到其他平台。为了linux具有更好的移植性,需要去掉分段而只使用分页。
IA32的分段机制是不可禁止的,于是Linux设计人员将段基址直接置为0,段界限为4GB。
IA32分段机制要求代码段和数据段各自为段。分段后分页正好可防止覆盖。
null分段平台与分页平台间可方便移植源代码编译器段基址:0分页段基址为0,分段机制下实际上没有什么工作,直接将段偏移量作为虚拟地址,给每段进行分页处理即可。
分段机制与分页机制差别不多,相互移植时不需很多修改工作虚拟地址 —— 重叠的32位线性空间 —— 实际内存32位线性空间null分页机制-页将线性地址空间划分成若干大小相等的片,称为页(Page)
物理地址空间分成与页大小相等的若干存储块,称为(物理)块或页面(Page Frame)
页的大小应该为多少?由谁确定?
系统决定
null分页机制-页表页表是把线性地址映射到物理地址的一种数据结构。
页表中应当包含如下内容:
物理页面基地址:线性地址空间中的一个页装入内存后所对应的物理页面的起始地址。
页的属性:表示页的特性。例如该页是否在内存,是否可被读出或写入等。
页面的大小为4KB ,物理页面基地址需要多少位就可以?null分页机制-页表项结构32位内存空间,页面大小为4KB,则页内12位,页基地址用高20位
物理页面基地址: 指的是页所对应的物理页面在内存的起始物理地址。相当于物理块号(为什么?)
其最低12位全部为0,因此用高20位来描述32位的块首地址即可。 物理页面基地址 属性31 12 11 0 null低12位记录页属性
存在位、权限位等,见书
页保护
内存寻址控制:运行核心态还是用户态
存取控制:读、写两种
null分页机制-两级页表为什么要采用两级页表?
页表占用空间过大,将页表也分页。页目录页表物理页面null
页目录20位块号+12位页属性
页表项32位=4B;一页4KB,页表每分一页可存放1K个块号映射;
页表项中块号的后10位用于区分页表中处于同一页中的块号。页目录的第a项第a项页中第b项b块号+偏移cnull分页机制-线性地址到物理地址的转换 20位页表地址一项就对应
一个页表20位页表地址过程描述过程描述CR3中记录页目录的起始地址(B)。
第一步,用32位线性地址的最高10位第31~22位作为页目录项的索引,将它乘以4,与(B)相加,获得相应目录项在内存的地址(1)。
第二步,从(1)地址开始读取32位页目录项的数据,取出其高20位,再给低12位补0,形成的32位就是页表在内存的起始地址,即块首地址(2)。
第三步,用32位线性地址中的第21~12位作为页表中页表项的索引,将它乘以4,与(2)地址相加,获得相应页表项在内存的地址。
第四步,从这个地址开始读取32位页表项,取出其高20位,再将线性地址的第11~0位放在低12位,形成最终32位页面物理地址。 null分页机制-页面高速缓存 null分页机制-Linux中的分页 Linux主要采用分页机制来实现虚拟存储器管理。许多处理器都采用64位结构,此时分页也到了3级。为了保持可移植性,Linux也发展采用三级分页模式而不是两级 nullLinux系统地址映射示例用Linux的实用程序objdump对C程序Hello.c的执行代码进行反汇编:
% objdump –d hello
08048568
:
8048568: pushl %ebp
8048569: movl %esp, %ebp
804856b: pushl $0x809404
8048570: call 8048474 <_init+0x84>
8048575: addl $0x4, %esp
8048578: leave
8048579: ret
804857a: movl %esi, %esi
0804857c :
804857c: pushl %ebp
804857d: movl %esp, %ebp
804857f: call 8048568
8048584: leave
8048585: ret
8048586: nop
8048587: nop
# include
greeting ( )
{
printf(“Hello,world!\n”);
}
main()
{
greeting();
}nullLinux系统地址映射示例Linux最常见的可执行文件格式为elf(Executable and Linkable Format)。
在elf中,ld总是从0x8000000开始安排程序的“代码段” ,这个地址是虚地址
程序执行时在物理内存中的实际地址,由内核为其建立内存映射时分配。 0x08048568 如何转化为物理地址?0000 1000 0000 0100 1000 0101 0110 1000三、计算机系统的启动三、计算机系统的启动一台出厂计算机(ROM、BIOS)
安装操作系统到硬盘(boot、OS)
加电,BIOS —> boot —> OS
内核映射文件:
./arch/x86/boot/bootSect.S
./arch/x86/boot/setup.S
./arch/x86/kernel/head.S
./init/main.cnullBIOS:
进行硬件检测;
读入引导扇区中的 bootsect的执行代码,到指定地址
bootsect在内存中移动自己;读入setup、system模块执行代码。
启动时内核在内存中位置的变化启动时内核在内存中位置的变化初始化时setup需要绝对地址0开始处1k的中断向量表,读BIOS相关中断参数,所以system一开始不直接加载到0。而是后面由setup移动。
system移动到位,相应的保护模式各种信息也准备好了,head调用main开始执行。
nullCPU在跳转到bootsect尚处于16位的实地址模式,在setup.s中转入32位的保护模式
bootsect和setup在执行中使用BIOS提供的调用来完成象读磁盘之类的工作,一旦转入内核映象本身的执行,就不再使用BIOS
内核自身初始化可以分为三个阶段(保护模式):
第一个阶段主要是CPU本身的初始化,例如页式映射的建立;
第二个阶段主要是系统中一些基础设施的初始化,如内存管理和进程管理的建立和初始化;
最后则是“上层建筑”的初始化,如根设备的安装和外部设备的初始化等
null更多说明
内核装载程序
引导过程
实模式下的初始化( setup.S)
保护模式下的初始化null
试着回答课本课后题
理解相关概念内核装载程序内核装载程序“arch/i386/boot”下的引导程序
包括bootsect.S、setup.S、video.S等
bootsect.S是生成引导扇区的汇编源码,它完成加载动作后直接跳转到setup.S的程序入口
setup.S的主要功能就是将系统参数(包括内存、磁盘等,由BIOS返回)拷贝到特别内存中,以便以后这些参数被保护模式下的代码来读取。
此外,setup.S还将video.S中的代码包含进来,检测和设置显示器和显示模式。
最后,setup.S将系统转换到保护模式,并跳转到 0x100000。
0x100000这个内存地址存放的是解压后的内核内核引导过程内核引导过程当CPU跳到0x100000时,将执行“arch/i386/kernel/head.S”中的startup_32,然后就跳转到start_kernel()中去了。
start_kernel()是“init/main.c”中的函数,调用了一系列初始化函数,以完成kernel本身的设置,建立基本的Linux核心环境。start_kernel()后,基本的Linux核心环境就建立起来了
在start_kernel()最后,通过调用init()函数,系统创建第一个核心线程。核心线程init()主要是来进行一些外设初始化的工作的,包括调用do_basic_setup()完成外设及其驱动程序的加载和初始化。并完成文件系统初始化和root文件系统的安装。
当do_basic_setup()函数返回init(),init()又打开了/dev/console设备,重定向三个标准的输入输出文件stdin、stdout和stderr到控制台,最后,搜索文件系统中的init程序(或者由init=命令行参数指定的程序),并使用 execve()系统调用加载执行init程序。到此init()函数结束,内核的引导部分也到此结束了实模式下的初始化( setup.S)实模式下的初始化( setup.S)版本检查和参数设置
内核的签名和模式检查
内存、键盘、显示器、硬盘等硬件参数设置
lgdt、lidt设置描述符表
对两片8259A进行编程
进入保护模式
保护模式下的初始化保护模式下的初始化第一阶段
CPU本身的初始化
第二阶段
基础设施的初始化,如内存管理和进程管理等
第三阶段
“上层建筑”的初始化,如根设备和外部设备等
保护模式下的第一阶段初始化保护模式下的第一阶段初始化arch\i386\boot\compressed\Head.S
初始化寄存器和数据区BSS(Block Started by Symbol)
内核代码解压缩
arch\i386\kernel\Head.S
初始化页表
初始化idt表(全部用默认表项)
针对CPU类型初始化各寄存器
初始化gdt、ldt
调用start_kernel()启动内核
初始化第二阶段初始化第二阶段实际内核初始化的开始
是较高层次的初始化,用C语言代码
start_kernel()第三阶段的初始化第三阶段的初始化0号进程0号进程0号进程的处理器上的空转进程,所以有几个处理器就有几个0号进程
0号进程都不在进程的家族队列中,也不在就绪进程的队列中,不参与进程的调度
1号进程只有一个,是所有进程的祖宗,从这个进程的开始的所有进程都参与调度
内核源代码的获取 内核源代码的获取 内核获取途径
Linux内核官方网站http://www.kernel.org
国内镜像
安装内核源码
GNU zip(gzip)格式内核解压命令
$tar xvzf linux-x.y.z.tar.gz
bzip2格式解压命令
$tar xvjf linux-x.y.z.tar.bz2
解压后内核源码位于linux-x.y.z目录下
内核源码一般安装在/usr/src/linux目录下
内核源码目录结构内核源码目录结构/usr/src/linux
arch : 核心源代码所支持的硬件体系结构相关的核心代码。如对于X86平台就是x86。
include : 核心的大多数include文件。另外对于每种支持的体系结构分别有一个子目录。
init : 核心启动代码。
mm : 内存管理代码。与具体硬件体系结构相关的内存管理代码位于arch/*/mm目录下,如对应于X86的就是arch/x86/mm/fault.c 。
drivers : 设备驱动都位于此目录中。它又进一步划分成几类设备驱动,每一种也有对应的子目录,如声卡的驱动对应于drivers/sound。
ipc : 核心的进程间通讯代码。nullnullmodules : 包含已建好可动态加载的模块。
fs Linux: 支持的文件系统代码。不同的文件系统有不同的子目录对应,如ext2文件系统对应的就是ext2子目录。
kernel : 主要核心代码。同时与处理器结构相关代码都放在arch/*/kernel目录下。
net : 核心的网络部分代码。里面的每个子目录对应于网络的一个方面。
lib : 核心的库代码。与处理器结构相关库代码被放在arch/*/lib/目录下。
scripts: 用于配置核心的脚本文件。
Documentation :一些文档,起参考作用。内核中使用的C语言(GNU C)内核中使用的C语言(GNU C)内核的主体是以GNU的C语言编写的,GNU对C语言本身在ANSI C的基础上作了不少扩充
内核开发者使用的C语言涵盖了ISO C99标准和GNU C扩展特性
参考网站
http://www.faqs.org/docs/learnc/
http://gcc.gnu.org/onlinedocs/gcc-4.0.0/gcc/C-Extensions.html#C-ExtensionsLinux内核中的汇编语言代码 Linux内核中的汇编语言代码 为什么使用汇编语言?
C语言没有对应的硬件操作语言,如inb,outb等
C语言没有对应的CPU特殊指令,如开关中断、寄存器操作等
提高时间效率,如系统调用的陷入或返回
提高空间效率,如系统第一扇区的引导程序
Linux源代码中汇编语言的使用形式
完全汇编代码
嵌在C语言程序的汇编片断
几个用于引导的Intel格式的汇编语言程序
Linux使用的编译器是GNU的gcc,所以源代码的汇编大多是AT&T格式的GNU汇编语言,与Intel格式有所的区别。