1 / 87

嵌入式系统 第八章 嵌入式 Linux 的内核

嵌入式系统 第八章 嵌入式 Linux 的内核. 第八章 Agenda. 8.1 Linux 内核概述 8.2 Linux 内核模块简介 8.3 Linux 的编译和定制 8.4 Linux 系统调用举例. 8.1 Linux 内核概述. 8.1.1 Linux 内核和功能结构 8.1.2 Linux 内核源代码布局 8.1.3 内核的移植 . 8.1.1 Linux 内核和功能结构. 内核( kernel )是操作系统的内部核心程序,它向外部提供了对计算机系统资源进行请求和管理的调用接口和服务. 内核. 可以将操作系统的代码分成两部分:

briar
Download Presentation

嵌入式系统 第八章 嵌入式 Linux 的内核

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 嵌入式系统 第八章 嵌入式Linux 的内核

  2. 第八章 Agenda 8.1 Linux 内核概述 8.2 Linux 内核模块简介 8.3 Linux 的编译和定制 8.4 Linux 系统调用举例

  3. 8.1 Linux 内核概述 • 8.1.1 Linux 内核和功能结构 • 8.1.2 Linux 内核源代码布局 • 8.1.3 内核的移植

  4. 8.1.1 Linux 内核和功能结构 • 内核(kernel)是操作系统的内部核心程序,它向外部提供了对计算机系统资源进行请求和管理的调用接口和服务

  5. 内核 • 可以将操作系统的代码分成两部分: • 内核所在的地址空间称为内核空间; • 而在内核以外,剩下的程序统称为外部管理程序,它们大部分是对外围设备的管理和界面操作,外部管理程序与用户进程所占据的地址空间称为外部空间。 • 通常,一个程序会跨越两个空间。 • 当执行到内核空间的一段代码时,称程序处于内核态 • 当程序执行到外部空间代码时,称程序处于用户态。

  6. 8.1.1 Linux 内核和功能结构 • 常见的OS内核有两个模式: • 微内核(micro-kernel) • 单一内核(Monolithic kernel)

  7. 微内核 • 在微内核结构中,操作系统的内核只需要提供最基本,最核心的一部分操作(比如创建和删除任务,内存管理,中断管理等) • 其他的管理程序(如文件系统,网络协议栈等)则尽可能地放在内核以外。这些外部程序可以独立运行,并对应用程序提供操作系统服务,服务之间使用进程间通信机制(IPC)进行交互。只在需要内核的协助时,才通过一套接口对内核发出调用请求。

  8. 微内核优点 • 使操作系统具有良好的灵活性。使得操作系统内部结构变得简单清晰。 • 在内核以外的外部程序分别独立运行,其间并不互相关联。这样,可以对这些程序分别进行维护和拆装,只要遵循已经规定好的界面,就不会对其他程序有任何干扰。这使得程序代码在维护上十分方便,体现了面向对象软件的结构特征。

  9. 微内核的不足 • 首先,程序代码之间的相互隔离,使得整个系统丧失了许多优化的机会; • 其次,部分资源浪费在外部进程之间的通信上(进程间通信的开销要比直接的函数调用大),这样,微内核结构在效率上必然低于传统的单一内核结构,这些效率损失将作为结构精简的代价。 • 总体上说,在当前的硬件条件下,微内核在效率上的损失小于其在结构上获得的效益,故而选取微内核成为操作系统的一大潮流。

  10. 8.1.1 Linux 内核和功能结构 • Linux内核属于单一内核 • 参与Linux系统开发的程序员大多数为世界各地的黑客们。比起结构的清晰,他们更加注重功能的强大和高效率的代码。 • 他们将大量的精力花在优化代码上,而这样的全局性优化必然以损失结构精炼作为代价,导致Linux中的每个部件都不能被轻易拆出,否则必然破坏整体效率。

  11. Linux内核 • 虽然Linux是一个单一内核操作系统,但它与传统的单一内核UNIX操作系统不同。 • 在普通单一内核系统中,所有内核代码都是被静态编译和链接的。 • 而在Linux中,可以动态装入和卸载内核中的部分代码。Linux中将这样的代码段称做模块(module),并对模块给予了强有力的支持。在Linux中,可以在需要时自动装入和卸载模块。

  12. 8.1.1 Linux 内核和功能结构 • Linux内核的结构

  13. 8.1.1 Linux 内核和功能结构 • Linux 内核主要由5 个模块构成,它们分别是: • 进程调度模块:控制进程对CPU 资源的使用 • 内存管理模块:确保所有进程能够安全地共享机器主内存区;虚拟内存管理 • 文件系统模块:支持对外部设备的驱动和存储 • 进程间通信模块:支持多种进程间的信息交换方式 • 网络接口模块:提供对多种网络通信标准的访问并支持许多网络硬件

  14. 内核模块之间的依赖关系

  15. 内核模块之间的依赖关系 • 所有的模块都与进程调度模块存在依赖关系。因为它们都需要依靠进程调度程序来挂起(暂停)或重新运行它们的进程。通常,一个模块会在等待硬件操作期间被挂起,而在操作完成后才可继续运行。

  16. 内核模块之间的依赖关系 • 进程调度子系统需要使用内存管理器来调整一特定进程所使用的物理内存空间。 • 进程间通信子系统则需要依靠内存管理器来支持共享内存通信机制。 • 虚拟文件系统也会使用网络接口来支持网络文件系统(NFS),同样也能使用内存管理子系统来提供内存虚拟盘(ramdisk)设备。 • 而内存管理子系统也会使用文件系统来支持内存数据块的交换操作。

  17. 8.1.2 Linux 内核源代码布局 • 安装的时候,如果选择了Kernel Develop,则会在/usr/scr/linux下找到源代码 • 根据各个目录的名字,可以容易猜出各个目录里面的文件的功能

  18. 8.1.2 Linux 内核源代码布局 • Documentation • arch • drivers • fs • include • init • ipc • lib • mm • net • …

  19. 8.1.2 Linux 内核源代码布局

  20. 8.1.2 Linux 内核源代码布局

  21. 8.1.2 Linux 内核源代码布局

  22. 补充:Linux操作系统的启动 • Boot Loader 把操作系统的代码调入内存后,会把控制权交给操作系统,由操作系统的启动程序来完成剩下的工作。

  23. Linux操作系统启动的步骤 • (1)把控制权交给Setup.S这段程序 • (2)进入保护模式,同时把控制权交给Head.S • (3)Head.S调用/init/main.C中的start_kernel函数,启动程序从start_kernel()函数继续执行 • (4)建立init进程

  24. 进入操作系统(1)Setup.S • 首先,Setup.S对已经调入内存的操作系统代码进行检查,如果没错,它会通过BIOS中断获取内存容量,硬盘等信息(实模式) • 准备让CPU进入保护模式 a.先屏蔽中断信号 b.调用指令lidt和lgdt,对中断向量表寄存器IDTR进行初始化 c.对8259中断控制器进行编程 d.协处理器重新定位 完成这几件事后,Setup.S设置保护模式的标志,重取指令,再用一条跳转指令jmpi 0x100000,KERNEL_CS。进入保护模式下的启动阶段,控制权交给Head.S.

  25. 进入操作系统(2)Head.S • 也要先做屏蔽中断一类的工作 • 然后对中断向量表做一定的处理 • Boot Loader读入内存的启动参数和命令行参数,Head.S把它们保存在empty_zero_page页中 • 检查CPU类型 • 对协处理器进行检查 • 页初始化,调用setup_paging这个子函数 • 因为已进入保护模式,段机制的多任务属性体现

  26. 进入操作系统(3)main.c中的初始化 • Head.S调用/init/main.c中的start_kernel函数,把控制权交给它,这个函数是整个操作系统初始化的最重要的函数,一旦它执行完,整个操作系统的初始化也就完成了。

  27. 进入操作系统(3)main.c中的初始化 • 计算机在执行start_kernel前以进入了保护模式,使处理器完全进入了全面执行操作系统代码的状态。 • 但直到目前为止,这都是针对处理器的。而一旦start_kernel开始执行,Linux内核就一步步展现。 • Start_kernel执行后,就可以以一个用户的身份登陆和使用Linux了

  28. 进入操作系统(3)main.c中的初始化 main.c中其他较为重要的函数如下: • Setup_arch() 最基本硬件的初始化 • Paging_init() 线性地址空间映射 • Trap_init()中断向量表初始化 • Int_IRQ与中断有关的初始化 • Sched_init()进程调度初始化 • Console_init()对中断的初始化 • 对文件系统的初始化 • Inode_initI() i节点管理机制初始化 • Name_cache_init() 目录缓存机制初始化 • Buffer_init() 块缓存机制初始化

  29. 进入操作系统(3)main.c中的初始化 • 启动到了目前这种状态,只剩下运行/etc下的启动配置文件。 • 这时初始化程序并没有完成操作系统各个部分的初始化,更关键的文件系统的安装还没有涉及,这是在init进程建立后完成的。就是start_kernel()最后部分内容。

  30. 进入操作系统(4)建立init进程 • Linux要建立的第一个进程是init进程 • 启动所需的Shell脚本文件 a.Linux系统启动所必须的 b.用户登陆后自己设定的 系统启动所必须的脚本存放在系统默认的配置文件目录/etc下。首先调用的是/etc/inittab.

  31. 8.1.3 内核的移植 • Linux移植: • 就是把Linux操作系统针对具体的目标平台做必要改写之后,安装到该目标平台,使其正确地运行起来,即把内核从一种硬件平台转移到另外一种硬件平台上运行。这个概念目前在嵌入式开发领域讲的比较多。 • 对于嵌入式Linux系统来说,有各种体系结构的处理器和硬件平台,并且用户需要根据需求自己定制硬件板。只要硬件平台有些变化,即使非常小,可能也需要做一些移植工作。

  32. 8.1.3 内核的移植 • 内核移植工作主要是修改跟硬件平台相关的代码,一般不涉及Linux内核通用的程序。移植的难度也取决于两种硬件平台的差异。 • Linux针对于特定的硬件平台的软件包叫做BSP(Board Support Package)。 • 目前Linux内核的社区已经对常见的硬件平台做了很多工作,移植工作已经简单了 • 通常都可以找到相同处理器的参考板,并且可以获取到Linux内核源代码。

  33. 移植的准备工作 • 选择参考板,获取到Linux内核源代码:四点原则 • 分析内核代码,弄清楚哪些设备有驱动程序,哪些还没有。 • 确信Linux对参考板的支持情况,配置编译Linux内核,在目标板上运行测试。 • 可能最新的Linux内核版本支持的最好,但是也可能需要在老内核版本上打补丁。 • 分析平台相关的部分代码实现;分析内核编译组织方式;分析内核启动的初始化程序;分析驱动程序的实现。

  34. 移植过程的基本内容 • 获取某一版本的Linux内核源码,根据具体目标平台对源码进行必要的改写(主要是修改体系结构相关部分) • 然后添加一些外设的驱动,打造一款适合需要的目标平台(可以是嵌入式便携设备也可以是其它体系结构的PC机)的新操作系统,对该系统进行针对具体目标平台的交叉编译,生成一个内核映象文件 • 最后通过一些手段把该映象文件烧写(安装)到目标平台中。 • 通常,对Linux源码的改写工作难度较大,它要求不仅对Linux内核结构要非常熟悉,还要求对目标平台的硬件结构要非常熟悉,同时还要求对相关版本的汇编语言较熟悉,因为与体系结构相关的部分源码往往是用汇编写的。所以这部分工作一般由目标平台提供商来完成。开发者所要做的就是从目标平台提供商的网站上下载相关版本Linux内核的补丁(Patch)。把它打到Linux内核上,再进行交叉编译就行。

  35. 第八章 Agenda 8.1 Linux 内核概述 8.2 Linux 内核模块简介 8.3 Linux 的编译和定制 8.4 Linux 系统调用举例

  36. 8.2 Linux 内核模块简介 • 8.2.1 进程管理模块 • 8.2.2 存储管理模块

  37. 8.2.1 进程管理模块 • 多进程是一个简单的思想(如下图): • 一个进程一直运行,直到它必须等待,通常是等待一些系统资源,等拥有了资源,它才可以继续运行。 • 在一个单进程的系统,比如 DOS , CPU 被简单地设为空闲,这样等待的时间就会被浪费。 • 在一个多进程的系统中,同一时刻许多进程在内存中。当一个进程必须等待时,操作系统将剥夺这个进程对CPU的使用权,并将它交给另一个合适的进程。

  38. 8.2.1 进程管理模块 • 进程的切换: • 当一个进程被剥夺了CPU,而系统转向去运行另外一个进程的时候,就发生了上下文切换,或者叫做进程切换。负责完成新的进程选择和进程切换的代码就是调度程序

  39. 8.2.1 进程管理模块 • 态的切换和上下文切换(书)

  40. 8.2.1 进程管理模块 • Linux的进程状态

  41. task_struct • Linux中,每一个进程用一个task_struct的数据结构来表示,用来管理系统中的进程。 • 当一个新的进程被创建后,从系统内存中分配一个新的task_struct,并增加到task的链表中。 • 为了更容易查找,用current指针指向当前运行的进程。

  42. task_struct • task_struct结构有个成员state表示进程当前的运行状态,已定义的状态如下:(include/linux/sched.h中) • #define TASK_RUNNING 0 • #define TASK_INTERRUPTIBLE 1 • #define TASK_UNINTERRUPTIBLE 2 • #define TASK_ZOMBIE 4 • #define TASK_STOPPED 8

  43. 进程的家庭关系 • 在Linux系统中,没有一个进程是和其他进程完全无关的。 • 系统中的所有进程,除了初始的进程之外,都有一个父进程。新进程不是创建的,而是拷贝,或者说从前一个进程克隆的(cloned)。 • 在每一个进程的task_struct 中都有指向它的父进程和兄弟进程(拥有相同的父进程的进程)以及它的子进程的的指针。 • 在Linux系统中,用户可以用pstree 命令看到正在运行的进程的家庭关系。

  44. 8.2.1 进程管理模块 • Linux将进程的创建与目标程序的执行分成两步。 • 第一步是从已经存在的“父进程”中复制出一个“子进程”,该子进程拥有自己的task_struct结构和系统空间堆栈,但与父进程共享其它所有资源。 • 两个系统调用:fork和clone • 第二步是目标程序的执行。Linux为此提供了一个系统调用execve,让一个进程执行以文件形式存在的一个可执行程序的映像。

  45. 8.2.1 进程管理模块 • 创建了子进程后,父进程有两种选择。 • 一是不受阻的(non-blocking)方式,也称为“异步”方式,即父进程继续走自己的路,与子进程分道扬镳。这种情况下如果子进程先于父进程结束,则内核会向父进程发一个信号告知子进程已经结束。 • 二是受阻的(blocking)方式,也称为“同步”方式,即父进程停下来进入睡眠状态,等待子进程结束,然后父进程再继续运行。Linux为此提供了两个系统调用,wait4和wait3。两个系统调用基本相同,wait4等待某个特定的子进程结束,而wait3则等待任何一个子进程结束。

  46. 8.2.2 存储管理模块 • Linux的虚拟地址空间 • linux的存储管理由两个部分组成: • 第一个是物理内存的管理 • 第二个是虚拟存储器的管理,主要是进程虚拟地址空间的管理

  47. 8.2.2 存储管理模块 • 有了虚拟存储器,程序员在编写程序的时候,就可以不用考虑内存的实际大小和程序的具体运行位置了,这个时候,他所使用的地址被称为了逻辑地址,或者虚拟地址,而所对应的物理地址,指的是程序真正运行的时候所占用的地址。 • 从逻辑地址到物理地址的转换是需要操作系统内核和CPU的硬件部件MMU共同配合来完成的 ,如下MMU的功能说明图–存储模块的硬件基础

  48. 逻辑地址到物理地址的转换

  49. 8.2.2 存储管理模块 • 80386把线性地址空间划分成4K字节的页面,每个页面可以被映射至物理存储空间中任意一块4K字节大小的区间。 • 在段式存管中,连续的逻辑地址经过映射后在线性地址空间还是连续的。 • 但是在页式存管中,连续的线性地址经过映射后在物理空间却不一定连续(其灵活性也在于此)。

  50. 分页机制地址转换

More Related