1、1嵌入式系统嵌入式系统第八章第八章 嵌入式嵌入式Linux Linux 的内核的内核2第八章 Agenda8.1 Linux 内核概述8.2 Linux 内核模块简介8.3 Linux 的编译和定制8.4 Linux 系统调用举例38.1 Linux 内核概述o 8.1.1 Linux 内核和功能结构o 8.1.2 Linux 内核源代码布局o 8.1.3 内核的移植48.1.1 Linux 内核和功能结构o 内核(kernel)是操作系统的内部核心程序,它向外部提供了对计算机系统资源进行请求和管理的调用接口和服务 5内核o 可以将操作系统的代码分成两部分:n内核所在的地址空间称为内核空间;n
2、而在内核以外,剩下的程序统称为外部管理程序,它们大部分是对外围设备的管理和界面操作,外部管理程序与用户进程所占据的地址空间称为外部空间。o 通常,一个程序会跨越两个空间。n当执行到内核空间的一段代码时,称程序处于内核态n当程序执行到外部空间代码时,称程序处于用户态。68.1.1 Linux 内核和功能结构o 常见的OS内核有两个模式:n 微内核(micro-kernel) n 单一内核(Monolithic kernel) 7微内核o 在微内核结构中,操作系统的内核只需要提供最基本,最核心的一部分操作(比如创建和删除任务,内存管理,中断管理等)o 其他的管理程序(如文件系统,网络协议栈等)则尽
3、可能地放在内核以外。这些外部程序可以独立运行,并对应用程序提供操作系统服务,服务之间使用进程间通信机制(IPC)进行交互。只在需要内核的协助时,才通过一套接口对内核发出调用请求。8微内核优点o 使操作系统具有良好的灵活性。使得操作系统内部结构变得简单清晰。o 在内核以外的外部程序分别独立运行,其间并不互相关联。这样,可以对这些程序分别进行维护和拆装,只要遵循已经规定好的界面,就不会对其他程序有任何干扰。这使得程序代码在维护上十分方便,体现了面向对象软件的结构特征。9微内核的不足o 首先,程序代码之间的相互隔离,使得整个系统丧失了许多优化的机会;o 其次,部分资源浪费在外部进程之间的通信上(进程
4、间通信的开销要比直接的函数调用大),这样,微内核结构在效率上必然低于传统的单一内核结构,这些效率损失将作为结构精简的代价。o 总体上说,在当前的硬件条件下,微内核在效率上的损失小于其在结构上获得的效益,故而选取微内核成为操作系统的一大潮流。108.1.1 Linux 内核和功能结构o Linux内核属于单一内核n 参与Linux系统开发的程序员大多数为世界各地的黑客们。比起结构的清晰,他们更加注重功能的强大和高效率的代码。n 他们将大量的精力花在优化代码上,而这样的全局性优化必然以损失结构精炼作为代价,导致Linux中的每个部件都不能被轻易拆出,否则必然破坏整体效率。 11Linux内核o 虽
5、然Linux是一个单一内核操作系统,但它与传统的单一内核UNIX操作系统不同。n 在普通单一内核系统中,所有内核代码都是被静态编译和链接的。n 而在Linux中,可以动态装入和卸载内核中的部分代码。Linux中将这样的代码段称做模块(module),并对模块给予了强有力的支持。在Linux中,可以在需要时自动装入和卸载模块。 128.1.1 Linux 内核和功能结构o Linux内核的结构138.1.1 Linux 内核和功能结构o Linux 内核主要由5 个模块构成,它们分别是:n 进程调度模块:控制进程对CPU 资源的使用 n 内存管理模块:确保所有进程能够安全地共享机器主内存区;虚拟
6、内存管理 n 文件系统模块:支持对外部设备的驱动和存储 n 进程间通信模块:支持多种进程间的信息交换方式 n 网络接口模块:提供对多种网络通信标准的访问并支持许多网络硬件 14内核模块之间的依赖关系 15内核模块之间的依赖关系 o 所有的模块都与进程调度模块存在依赖关系。因为它们都需要依靠进程调度程序来挂起(暂停)或重新运行它们的进程。通常,一个模块会在等待硬件操作期间被挂起,而在操作完成后才可继续运行。16内核模块之间的依赖关系o 进程调度子系统需要使用内存管理器来调整一特定进程所使用的物理内存空间。o 进程间通信子系统则需要依靠内存管理器来支持共享内存通信机制。o 虚拟文件系统也会使用网络
7、接口来支持网络文件系统(NFS),同样也能使用内存管理子系统来提供内存虚拟盘(ramdisk)设备。o 而内存管理子系统也会使用文件系统来支持内存数据块的交换操作。178.1.2 Linux 内核源代码布局o 安装的时候,如果选择了Kernel Develop,则会在/usr/scr/linux下找到源代码o 根据各个目录的名字,可以容易猜出各个目录里面的文件的功能188.1.2 Linux 内核源代码布局o Documentation o arch o drivers o fs o include o init o ipc o lib o mm o net o 198.1.2 Linux 内
8、核源代码布局208.1.2 Linux 内核源代码布局218.1.2 Linux 内核源代码布局22补充:Linux操作系统的启动o Boot Loader 把操作系统的代码调入内存后,会把控制权交给操作系统,由操作系统的启动程序来完成剩下的工作。23Linux操作系统启动的步骤o (1)把控制权交给Setup.S这段程序o (2)进入保护模式,同时把控制权交给Head.So (3)Head.S调用/init/main.C中的start_kernel函数,启动程序从start_kernel()函数继续执行o (4)建立init进程24进入操作系统(1)Setup.So 首先,Setup.S对已
9、经调入内存的操作系统代码进行检查,如果没错,它会通过BIOS中断获取内存容量,硬盘等信息(实模式)o 准备让CPU进入保护模式 a.先屏蔽中断信号 b.调用指令lidt和lgdt,对中断向量表寄存器IDTR进行初始化 c.对8259中断控制器进行编程 d.协处理器重新定位 完成这几件事后,Setup.S设置保护模式的标志,重取指令,再用一条跳转指令jmpi 0 x100000,KERNEL_CS。进入保护模式下的启动阶段,控制权交给Head.S.25进入操作系统(2)Head.So 也要先做屏蔽中断一类的工作o 然后对中断向量表做一定的处理o Boot Loader读入内存的启动参数和命令行参
10、数,Head.S把它们保存在empty_zero_page页中o 检查CPU类型o 对协处理器进行检查 o 页初始化,调用setup_paging这个子函数 o 因为已进入保护模式,段机制的多任务属性体现 26进入操作系统(3)main.c中的初始化o Head.S调用/init/main.c中的start_kernel函数,把控制权交给它,这个函数是整个操作系统初始化的最重要的函数,一旦它执行完,整个操作系统的初始化也就完成了。27进入操作系统(3)main.c中的初始化o 计算机在执行start_kernel前以进入了保护模式,使处理器完全进入了全面执行操作系统代码的状态。o 但直到目前为
11、止,这都是针对处理器的。而一旦start_kernel开始执行,Linux内核就一步步展现。o Start_kernel执行后,就可以以一个用户的身份登陆和使用Linux了28进入操作系统(3)main.c中的初始化main.c中其他较为重要的函数如下:o Setup_arch() 最基本硬件的初始化o Paging_init() 线性地址空间映射o Trap_init()中断向量表初始化o Int_IRQ与中断有关的初始化o Sched_init()进程调度初始化o Console_init()对中断的初始化o 对文件系统的初始化o Inode_initI() i节点管理机制初始化o Nam
12、e_cache_init() 目录缓存机制初始化o Buffer_init() 块缓存机制初始化29进入操作系统(3)main.c中的初始化o 启动到了目前这种状态,只剩下运行/etc下的启动配置文件。o 这时初始化程序并没有完成操作系统各个部分的初始化,更关键的文件系统的安装还没有涉及,这是在init进程建立后完成的。就是start_kernel()最后部分内容。30进入操作系统(4)建立init进程o Linux要建立的第一个进程是init进程o 启动所需的Shell脚本文件 a.Linux系统启动所必须的 b.用户登陆后自己设定的系统启动所必须的脚本存放在系统默认的配置文件目录/etc下
13、。首先调用的是/etc/inittab.318.1.3 内核的移植o Linux移植:n就是把Linux操作系统针对具体的目标平台做必要改写之后,安装到该目标平台,使其正确地运行起来,即把内核从一种硬件平台转移到另外一种硬件平台上运行。这个概念目前在嵌入式开发领域讲的比较多。 o 对于嵌入式Linux系统来说,有各种体系结构的处理器和硬件平台,并且用户需要根据需求自己定制硬件板。只要硬件平台有些变化,即使非常小,可能也需要做一些移植工作。328.1.3 内核的移植o 内核移植工作主要是修改跟硬件平台相关的代码,一般不涉及Linux内核通用的程序。移植的难度也取决于两种硬件平台的差异。n Lin
14、ux针对于特定的硬件平台的软件包叫做BSP(Board Support Package)。o 目前Linux内核的社区已经对常见的硬件平台做了很多工作,移植工作已经简单了n 通常都可以找到相同处理器的参考板,并且可以获取到Linux内核源代码。33移植的准备工作o 选择参考板,获取到Linux内核源代码:四点原则o 分析内核代码,弄清楚哪些设备有驱动程序,哪些还没有。o 确信Linux对参考板的支持情况,配置编译Linux内核,在目标板上运行测试。n可能最新的Linux内核版本支持的最好,但是也可能需要在老内核版本上打补丁。o 分析平台相关的部分代码实现;分析内核编译组织方式;分析内核启动的初
15、始化程序;分析驱动程序的实现。 34移植过程的基本内容o获取某一版本的Linux内核源码,根据具体目标平台对源码进行必要的改写(主要是修改体系结构相关部分)o然后添加一些外设的驱动,打造一款适合需要的目标平台(可以是嵌入式便携设备也可以是其它体系结构的PC机)的新操作系统,对该系统进行针对具体目标平台的交叉编译,生成一个内核映象文件o最后通过一些手段把该映象文件烧写(安装)到目标平台中。o通常,对Linux源码的改写工作难度较大,它要求不仅对Linux内核结构要非常熟悉,还要求对目标平台的硬件结构要非常熟悉,同时还要求对相关版本的汇编语言较熟悉,因为与体系结构相关的部分源码往往是用汇编写的。所
16、以这部分工作一般由目标平台提供商来完成。开发者所要做的就是从目标平台提供商的网站上下载相关版本Linux内核的补丁(Patch)。把它打到Linux内核上,再进行交叉编译就行。 35第八章 Agenda8.1 Linux 内核概述8.2 Linux 内核模块简介8.3 Linux 的编译和定制8.4 Linux 系统调用举例368.2 Linux 内核模块简介o 8.2.1 进程管理模块o 8.2.2 存储管理模块378.2.1 进程管理模块o 多进程是一个简单的思想(如下图):n 一个进程一直运行,直到它必须等待,通常是等待一些系统资源,等拥有了资源,它才可以继续运行。n 在一个单进程的系统
17、,比如 DOS , CPU 被简单地设为空闲,这样等待的时间就会被浪费。n 在一个多进程的系统中,同一时刻许多进程在内存中。当一个进程必须等待时,操作系统将剥夺这个进程对CPU的使用权,并将它交给另一个合适的进程。 388.2.1 进程管理模块o 进程的切换:n当一个进程被剥夺了CPU,而系统转向去运行另外一个进程的时候,就发生了上下文切换,或者叫做进程切换。负责完成新的进程选择和进程切换的代码就是调度程序 398.2.1 进程管理模块o 态的切换和上下文切换(书)n 408.2.1 进程管理模块o Linux的进程状态41task_structo Linux中,每一个进程用一个task_st
18、ruct的数据结构来表示,用来管理系统中的进程。o 当一个新的进程被创建后,从系统内存中分配一个新的task_struct,并增加到task的链表中。o 为了更容易查找,用current指针指向当前运行的进程。 42task_structo task_struct结构有个成员state表示进程当前的运行状态,已定义的状态如下:(include/linux/sched.h中)n#define TASK_RUNNING0n#define TASK_INTERRUPTIBLE1n#define TASK_UNINTERRUPTIBLE2n#define TASK_ZOMBIE4n#define TA
19、SK_STOPPED843进程的家庭关系o 在Linux系统中,没有一个进程是和其他进程完全无关的。o 系统中的所有进程,除了初始的进程之外,都有一个父进程。新进程不是创建的,而是拷贝,或者说从前一个进程克隆的(cloned)。o 在每一个进程的task_struct 中都有指向它的父进程和兄弟进程(拥有相同的父进程的进程)以及它的子进程的的指针。o 在Linux系统中,用户可以用pstree 命令看到正在运行的进程的家庭关系。 448.2.1 进程管理模块o Linux将进程的创建与目标程序的执行分成两步。n 第一步是从已经存在的“父进程”中复制出一个“子进程”,该子进程拥有自己的task_
20、struct结构和系统空间堆栈,但与父进程共享其它所有资源。o 两个系统调用:fork和clone n 第二步是目标程序的执行。 Linux为此提供了一个系统调用execve,让一个进程执行以文件形式存在的一个可执行程序的映像。 458.2.1 进程管理模块o 创建了子进程后,父进程有两种选择。n 一是不受阻的(non-blocking)方式,也称为“异步”方式,即父进程继续走自己的路,与子进程分道扬镳。这种情况下如果子进程先于父进程结束,则内核会向父进程发一个信号告知子进程已经结束。n 二是受阻的(blocking)方式,也称为“同步”方式,即父进程停下来进入睡眠状态,等待子进程结束,然后父
21、进程再继续运行。Linux为此提供了两个系统调用,wait4和wait3。两个系统调用基本相同,wait4等待某个特定的子进程结束,而wait3则等待任何一个子进程结束。 468.2.2 存储管理模块o Linux的虚拟地址空间o linux的存储管理由两个部分组成:n 第一个是物理内存的管理n 第二个是虚拟存储器的管理,主要是进程虚拟地址空间的管理478.2.2 存储管理模块o 有了虚拟存储器,程序员在编写程序的时候,就可以不用考虑内存的实际大小和程序的具体运行位置了,这个时候,他所使用的地址被称为了逻辑地址,或者虚拟地址,而所对应的物理地址,指的是程序真正运行的时候所占用的地址。 o 从逻
22、辑地址到物理地址的转换是需要操作系统内核和CPU的硬件部件MMU共同配合来完成的 ,如下MMU的功能说明图 存储模块的硬件基础48逻辑地址到物理地址的转换 498.2.2 存储管理模块o 80386把线性地址空间划分成4K字节的页面,每个页面可以被映射至物理存储空间中任意一块4K字节大小的区间。o 在段式存管中,连续的逻辑地址经过映射后在线性地址空间还是连续的。o 但是在页式存管中,连续的线性地址经过映射后在物理空间却不一定连续(其灵活性也在于此)。 50分页机制地址转换518.2.2 存储管理模块o Linux的存储管理模块 52内存映射 o 当一个映像执行时,执行映像的内容必须放在进程的虚
23、拟地址空间中。对于执行映像连接到的任意共享库,情况也是一样。执行文件实际并没有放到物理内存,而只是被连接到进程的虚拟内存。这样,只要运行程序引用了映像的部分,这部分映像就从执行文件中加载到内存中。这种映像和进程虚拟地址空间的连接叫做内存映射。 53mm_structo 进程的虚拟内存用mm_struct 数据结构表示。它包括当前执行的映像的信息(例如 bash )和指向一组 vm_area_struct 结构的指针。o 每一个 vm_area_struct 的数据结构都描述了内存区域的起始、进程对于内存区域的访问权限和对于这段内存的操作。o 这些操作是一组例程,用于管理这段虚拟内存。例如其中一
24、种虚拟内存操作就是当进程试图访问这段虚拟内存时,如果发现(通过 page fault )内存不在物理内存中,所必须执行的正确操作,这个操作叫做nopage 操作。 Linux 请求把执行映像的页加载到内存中的时候用到 nopage 操作。 54vm_area_structo 当一个执行映像映射到进程的虚拟地址空间时,产生一组 vm_area_struct 数据结构。每一个vm_area_struct 结构表示执行映像的一部分:执行代码、初始化数据(变量)、未初始化数据等等。 Linux 支持一系列标准的虚拟内存操作,当 vm_area_struct 数据结构创建时,一组正确的虚拟内存操作就和它
25、们关联在一起。o 只要执行映像映射到进程的虚拟内存中,它就可以开始运行。因为只有映像的最开始的部分是放在物理内存中,很快就会访问到还没有放在物理内存的虚拟空间区。当进程访问没有有效页表条目的虚拟地址的时候,处理器向 Linux 报告 page fault 。 Page fault 描述了发生 page fault 的虚拟地址和内存访问类型。 55Linux的虚拟地址空间的映射o从页的分配角度来说。虚拟地址中的页的类型有下面三个类型:如图所示:n未分配:n已分配了的、但是没有缓存的n已分配的且被缓存的 56第八章 Agenda8.1 Linux 内核概述8.2 Linux 内核模块简介8.3 L
26、inux 的编译和定制8.4 Linux 系统调用举例578.3 Linux 的编译和定制o 8.3.1 Linux 内核编译概述o 8.3.2 编译内核前的准备工作o 8.3.3 编译内核第一步:配置内核o 8.3.4 编译内核第二步:编译内核o 8.3.5 编译内核后生成的文件588.3.1 Linux 内核编译概述o 如果用户想要使用内核的一些新特性,或想根据自己的系统量身定制一个更高效或更稳定的内核,就需要重新编译内核。 o 编译内核包含两大项内容:n 第一步:配置内核n 第二步:编译内核。 598.3.2 编译内核前的准备工作o 编译内核前先要了解自己系统的硬件配置情况,比如CPU的
27、类型、主板芯片、显卡和声卡的型号以及其他相关参数等。o 也要了解现有内核的版本号n uname -a n Linux localhost localdomain 2.4.20-8 #1 Thu Mar 13 17:54:28 EST 2003 i686 i686 i386 GNU/Linux 608.3.2 编译内核前的准备工作o 把下载好的打包的内核解开。压缩的内核、补丁和解开的源代码总共要占70M 左右的硬盘空间;用root 登录,解开的源代码应该在/usr/src/Linux 2.4.20-8 下面: n #tar zxvf Linux 2.4.20-8.tar.gz 或者n #gzip
28、 t Linux 2.4.20-8.tar.gz n #tar x Linux 2.4.20-8.tar 618.3.3 编译内核第一步:配置内核o 在开始配置内核之前,首先需要通过下面的命令清除所有的临时文件、中间件和配置文件。对于一个刚从网上下载的内核来说,它肯定是干净的,这么做只会多此一举。但是这是一个良好习惯,而且不会有坏结果。 n #make mrproper 628.3.3 编译内核第一步:配置内核o make menuconfig:一个文本模式、简单的菜单模式的配置界面638.3.3 编译内核第一步:配置内核o make xconfig: 基于Tcl/Tk的X图形配置界面 648
29、.3.3 编译内核第一步:配置内核o 根据自己的需要,进行各个选择和子选项的配置。对每一个内核选项,可以有三个选择:不包括(N),build-in(Y),和模块化支持(M)。 658.3.4 编译内核第二步:编译内核o 编译内核的软件环境是kbuild系统,它泛指构建一个完整并能够运行的Linux内核所需要的一切资源。这些资源包括构建程序、脚本、中间件、配置文件和Makefile。o 编译过程:在/usr/src/linux下依次输入以下命令(功能请参考书上描述)nmake depnmake cleannmake bzImagenmake installnmake modulesnmake m
30、odules_install668.3.5 编译内核后生成的文件1. vmlinuz o vmlinuz是可引导的、压缩的内核。“vm”代表“Virtual Memory”。 o vmlinuz的建立有两种方式:n一是编译内核时通过“make zImage”创建,然后通过:“cp /usr/src/linux-2.4/arch/i386/linux/boot/zImage /boot/vmlinuz”产生。zImage适用于小内核的情况,它的存在是为了向后的兼容性。n二是内核编译时通过命令make bzImage创建,然后通过:“cp /usr/src/linux-2.4/arch/i386/
31、linux/boot/bzImage /boot/vmlinuz”产生。bzImage是压缩的内核映像,需要注意,bzImage不是用bzip2压缩的,bzImage中的bz容易引起误解,bz表示“big zImage”。bzImage中的b是“big”意思。 67vmlinuzo zImage(vmlinuz)和bzImage(vmlinuz)都是用gzip压缩的。它们不仅是一个压缩文件,而且在这两个文件的开头部分内嵌有gzip解压缩代码。所以不能用gunzip 或 gzip dc解包vmlinuz。o 内核文件中包含一个微型的gzip用于解压缩内核并引导它。两者的不同之处在于,老的zIma
32、ge解压缩内核到低端内存(第一个640K),bzImage解压缩内核到高端内存(1M以上)。如果内核比较小,那么可以采用zImage 或bzImage之一,两种方式引导的系统运行时是相同的。大的内核采用bzImage,不能采用zImage。 o vmlinux是未压缩的内核,vmlinuz是vmlinux的压缩文件。 688.3.5 编译内核后生成的文件o 2. initrd-x.x.x.img n initrd-x.x.x.img是用gzip压缩的文件。 n initrd是“initial ramdisk”的简写。initrd一般被用来临时引导硬件驱动到实际内核vmlinuz,使其能够接管并
33、继续引导的状态。n initrd实现加载一些模块和安装文件系统等功能。 initrd映象文件是使用mkinitrd创建的。 698.3.5 编译内核后生成的文件o 3. System.mapn是一个特定内核的内核符号表,是由“nm vmlinux”产生的n在进行程序设计时,会命名一些变量名或函数名之类的符号。Linux内核是一个很复杂的代码块,有许多的全局符号。 nLinux内核不使用符号名,而是通过变量或函数的地址来识别变量或函数名。比如不是使用size_t BytesRead这样的符号,而是像c0343f20这样引用这个变量。 n对于使用计算机的人来说,更喜欢使用那些像size_t Byt
34、esRead这样的名字,而不喜欢像c0343f20这样的名字。内核主要是用c写的,所以编译器/连接器允许我们编码时使用符号名,当内核运行时使用地址。 70System.mapo在有的情况下,我们需要知道符号的地址,或者需要知道地址对应的符号。这由符号表来完成,符号表是所有符号连同它们的地址的列表。Linux 符号表使用到2个文件: n/proc/ksyms nSystem.map o/proc/ksyms是一个“proc file”,在内核引导时创建。实际上,它并不真正的是一个文件,它只不过是内核数据的表示,却给人们是一个磁盘文件的假象,这从它的文件大小是0可以看出来。o而System.map
35、是存在于你的文件系统上的实际文件。当你编译一个新内核时,各个符号名的地址要发生变化,老的System.map具有的是错误的符号信息。每次内核编译时产生一个新的System.map,你应当用新的System.map来取代老的System.map。 o虽然内核本身并不真正使用System.map,但其它程序比如klogd、lsof和ps等软件需要一个正确的System.map。 71第八章 Agenda8.1 Linux 内核概述8.2 Linux 内核模块简介8.3 Linux 的编译和定制8.4 Linux 系统调用举例728.4 Linux 系统调用举例o 8.4.1 Linux系统调用介绍
36、o 8.4.2 给内核增加新的系统调用的实例738.4.1 Linux系统调用介绍o API函数与系统调用既有区别又有联系。n API是用于某种特定目的的函数,供应用程序调用,而系统调用供应用程序直接进入系统空间。 n 系统包含了一些供应用程序调用的API函数库,其中的一些仅用于提供系统调用,因此每个系统调用都与一个API函数相对应,反之则不成立 n 大多数系统调用的返回值是一个整数,通常返回1表示系统调用失败,返回非负数表示函数执行成功。n 系统调用虽然是为应用程序主动进入系统空间而设置,但在内核的开发中也可以使用某些系统调用(如open,read,write等函数) 748.4.1 Lin
37、ux系统调用介绍o 系统调用的功能图n应用程序和系统调用及其内核实现之间的层次关系 758.4.2 给内核增加新的系统调用的实例o 需要实际操作完成o 需要理解每一个修改点的含义和作用76系统调用o与系统调用相关的源程序文件包括以下这些:narch/i386/boot/bootsect.Snarch/i386/Kernel/setup.Snarch/i386/boot/compressed/head.Snarch/i386/kernel/head.S ninit/arch/i386/kernel/arch/i386/kernel/entry.Snarch/i386/kernel/irq.hni
38、nclude/asm-386/unistd.h o当然,这只是涉及到的几个主要文件。而事实上,增加系统调用真正要修改文件只有include/asm-386/unistd.h 和arch/i386/kernel/entry.S两个 771 修改kernel/sys.c文件 o 在Red Hat 9.0的/usr/src/linux-2.4.20-8/kernel/sys.c文件中增加一个新的自定义系统调用函数:asmlinkage long sys_testsyscall(int num)return num;782 修改include/asm-i386/unistd.h文件 o 在unix系统
39、标准头文件/usr/src/linux/include/asm-i386/unistd.h 的末尾,增加一行。o 该文件中包含了系统调用清单,用来给每个系统调用分配一个唯一的号码。文件中每一行的格式如下:#define _NR_name NNNo 需要说明的是:具体的数值要看读者所采用的内核版本而定,一般都是在系统已有的编号的基础上加1。n#define _NR_testsyscall 259793 修改arch/i386/kernel/entry.S文件 o 在/usr/src/linux/arch/i386/kernel/entry.S的文件末尾增加一行o 该文件中有类似如下的清单:.lo
40、ng SYMBOL_NAME()该清单用来对sys_call_table数组进行初始化。该数组包含指向内核中每个系统调用的指针。这样就在数组中增加了新的内核函数的指针。 o 下面这行代码导出了上面新添加的系统调用函数。n.long SYMBOL_NAME(sys_testsyscall) 804 编译内核o 正确的编译过程大致如下:nmake mrpropernmake xconfigo 增加initrd.img的support o 如果是在VMWare下,一般默认的硬盘都是SCSI的,所以,需要把SCSI Support选中,并在其Low SCSI 选项中,把BusLogic Support
41、选中。然后进行新内核的编译: nmake depnmake bzImagenmake modulesnmake modules_installnmake install 815. 启动内核o 内核编译成功后,需要修改BootLoader来启动这个新的内核,以lilo为例,修改后的lilo.conf如下所示: 825. 启动内核o reboot,下面就会出现linux-2.4.20-8custom的一个内核启动选项,就是刚刚新编译生成的。 836. 测试新增系统调用 o #include #include int errno;_syscall1(long,testsyscall,int,num);main()printf(“%dn”,testsyscall(10011);84o 编辑完成后,保存。编译a.c程序,gcc I/usr/src/linux-2.4.20-8/include a.c,生成a.out。执行./a.out后,出现下面的结果 85讨论o 如果硬件平台没有MMU,对操作系统会有什么影响?86作业o 1.完成Linux内核的Struct task的代码浏览o 2.完成嵌入式Linux内核的编译o 3.完成嵌入式Linux内核的API的增加实验87End of Chapter 8