介绍
uClinux的一些基本情况,包括:小型化的方法、内存管理、
开发环境、多进程等。
-------------------------------------------------------
uClinux小型化的做法 uClinux就是Micro-Control-Linux,字面上的理解就是"针对微控制领域而设计的Linux系统"。
标准Linux可能采用的小型化方法
1. 重新编译内核
Linux内核采用模块化的设计,内核模块作为可选的选项,在编译系统内核时指定。因此一种较通用的做法是对Linux内核重新编译,在编译时仔细的选择嵌入式设备所需要的功能支持模块,同时删除不需要的功能。通过对内核的重新配置,可以使系统运行所需要的内核显著减小,从而缩减资源使用量。
2. 制作root文件系统映象
Linux系统在启动时必须加载根(root)文件系统,因此剪裁系统同时包括root文件系统的剪裁。在x86系统下,Linux可以在Dos下,使用Loadlin文件加载启动。
uClinux采用的小型化方法 1.
uClinux的内核加载方式
uClinux的内核有两种可选的运行方式:可以在flash上直接运行,也可以加载到内存中运行。这种做法可以减少内存需要。
Flash运行方式:把内核的可执行映像烧写到flash上,系统启动时从flash的某个地址开始逐句执行。这种方法实际上是很多嵌入式系统采用的方法。
内核加载方式:把内核的压缩文件存放在flash上,系统启动时读取压缩文件在内存里解压,然后开始执行,这种方式相对复杂一些,但是运行速度可能更快(ram的存取速率要比flash高)。同时这也是标准Linux系统采用的启动方式。
2.uClinux的根(root)文件系统
uClinux系统采用romfs文件系统,相对于ext2文件系统要求更少的空间。空间的节省首先来自内核支持romfs文件系统需要更少的代码,其次 romfs文件系统相对简单,建立文件系统超级块(superblock)需要更少的存储空间。Romfs文件系统不支持动态擦写保存,对于需要动态保存的数据采用虚拟ram盘的方法进行处理(ram盘采用ext2文件系统)。
3.uClinux的应用程序库
uClinux小型化的另一个做法是重写了应用程序库,相对于越来越大且越来越全的glibc库,uClibc对libc做了精简。
uClinux对用户程序采用静态连接的形式,这种做法会使应用程序变大,但是基于内存管理的问题,不得不这样做。
uClinux的开发环境
GNU开发套件
Gnu开发套件包括一系列的开发调试工具。主要组件:
Gcc: 编译器,可做成交叉编译的形式,即在宿主机上开发编译目标上可运行的二进制文件。
Binutils:一些辅助工具,包括objdump(可以反编译二进制文件),as(汇编编译器),ld(连接器)等等。
Gdb:调试器,可使用多种交叉调试方式,gdb-bdm(背景调试工具),gdbserver(使用以太网络调试)。
uClinux的打印终端 uClinux的默认终端通常是串口,内核启动时所有的信息都打印到串口终端(使用printk函数打印),同时也可以通过串口终端与系统交互。
uClinux在启动时启动了telnetd(远程登录服务)。至于是否允许远程登录可以通过烧写romfs文件系统时由用户决定。
交叉编译调试工具
支持一种新的处理器,必须具备一些编译,汇编工具,使用这些工具可以形成可运行于这种处理器的二进制文件。对于内核使用的编译工具同应用程序使用的有所不同。在解释不同点之前,需要对gcc连接做一些说明:
.ld(link description)文件:ld文件是指出连接时内存映象格式的文件。
crt0.S:应用程序编译连接时需要的启动文件,主要是初始化应用程序栈。
pic:position independence code ,与位置无关的二进制格式文件,在程序段中必须包括reloc段,从而使的代码加载时可以进行重新定位。
内核编译连接时,使用ucsimm.ld文件,形成可执行文件映像,所形成的代码段既可以使用间接寻址方式(即使用reloc段进行寻址),也可以使用绝对寻址方式。这样可以给编译器更多的优化空间。因为内核可能使用绝对寻址,所以内核加载到的内存地址空间必须与ld文件中给定的内存空间完全相同。
应用程序的连接与内核连接方式不同。应用程序由内核加载(可执行文件加载器将在后面讨论),由于应用程序的ld文件给出的内存空间与应用程序实际被加载的内存位置可能不同,在应用程序加载的过程中需要一个重新定位的过程,即对reloc段进行修正,使得程序进行间接寻址时不至于出错。(这个问题在i386 等高级处理器上方法有所不同,本文将在后面进一步分析)。
由上述讨论,至少需要两套编译连接工具。在讨论过
uClinux的内存管理后本文将给出整个系统的工作流程以及系统在flash和ram中的空间分布。
uClinux的内存管理 uClinux同标准Linux的最大区别就在于内存管理,由于
uClinux的内存管理引发了一些标准Linux所不会出现的问题。本文将把
uClinux内存管理同标准Linux的那内存管理部分进行比较分析。
标准Linux使用的虚拟存储器技术
标准Linux使用虚拟存储器技术,提供比计算机系统中实际使用的物理内存大得多的内存空间,从而使得编程人员在不用考虑计算机中的物理内存容量。
为了支持虚拟存储管理器的管理,Linux系统采用分页(paging)的方式来载入进程。所谓分页既是把实际的存储器分割为相同大小的段,例如每个段1024个字节,这样1024个字节大小的段便称为一个页面(page)。
虚拟存储器由存储器管理机制及一个大容量的快速硬盘存储器支持。它的实现基于局部性原理,当一个程序在运行之前,没有必要全部装入内存,而是仅将那些当前要运行的那些部分页面或段装入内存运行(copy-on-write),其余暂时留在硬盘上程序运行时如果它所要访问的页(段)已存在,则程序继续运行,如果发现不存在的页(段),操作系统将产生一个页错误(page fault),这个错误导致操作系统把需要运行的部分加载到内存中。必要时操作系统还可以把不需要的内存页(段)交换到磁盘上。利用这样的方式管理存储器,便可把一个进程所需要用到的存储器以化整为零的方式,视需求分批载入,而核心程序则凭借属于每个页面的页码来完成寻址各个存储器区段的工作。
标准Linux是针对有内存管理单元的处理器设计的。在这种处理器上,虚拟地址被送到内存管理单元(MMU),把虚拟地址映射为物理地址。
通过赋予每个任务不同的虚拟--物理地址转换映射,支持不同任务之间的保护。地址转换函数在每一个任务中定义,在一个任务中的虚拟地址空间映射到物理内存的一个部分,而另一个任务的虚拟地址空间映射到物理存储器中的另外区域。计算机的存储管理单元(MMU)一般有一组寄存器来标识当前运行的进程的转换表。在当前进程将CPU放弃给另一个进程时(一次上下文切换),内核通过指向新进程地址转换表的指针加载这些寄存器。MMU寄存器是有特权的,只能在内核态才能访问。这就保证了一个进程只能访问自己用户空间内的地址,而不会访问和修改其它进程的空间。当可执行文件被加载时,加载器根据缺省的ld文件,把程序加载到虚拟内存的一个空间,因为这个原因实际上很多程序的虚拟地址空间是相同的,但是由于转换函数不同,所以实际所处的内存区域也不同。而对于多进程管理当处理器进行进程切换并执行一个新任务时,一个重要部分就是为新任务切换任务转换表。我们可以看到Linux系统的内存管理至少实现了以下功能:
运行比内存还要大的程序。理想情况下应该可以运行任意大小的程序
◇可以运行只加载了部分的程序,缩短了程序启动的时间
◇可以使多个程序同时驻留在内存中提高CPU的利用率
◇可以运行重定位程序。即程序可以方于内存中的任何一处,而且可以在执行过程中移动。
◇写机器无关的代码。程序不必事先约定机器的配置情况。
◇减轻程序员分配和管理内存资源的负担。
◇可以进行共享--例如,如果两个进程运行同一个程序,它们应该可以共享程序代码的同一个副本。
◇提供内存保护,进程不能以非授权方式访问或修改页面,内核保护单个进程的数据和代码以防止其它进程修改它们。否则,用户程序可能会偶然(或恶意)的破坏内核或其它用户程序。
虚存系统并不是没有代价的。内存管理需要地址转换表和其他一些数据结构,留给程序的内存减少了。地址转换增加了每一条指令的执行时间,而对于有额外内存操作的指令会更严重。当进程访问不在内存的页面时,系统发生失效。系统处理该失效,并将页面加载到内存中,这需要极耗时间的磁盘I/O操作。总之内存管理活动占用了相当一部分cpu时间(在较忙的系统中大约占10%)。