uclinux表示micro-control linux.即“微控制器领域中的Linux系统”,是Lineo公司的主打产品,同时也是开放源码的嵌入式Linux的典範之作。uCLinux主要是针对目标处理器没有存储管理单元MMU(Memory Management Unit)的嵌入式系统而设计的。它已经被成功地移植到了很多平台上。由于没有MMU,其多任务的实现需要一定技巧。
基本介绍
- 外文名:uclinux
- 全称:micro-Conrol-Linux
- 优点:良好的移植性、优秀的网路功能
- 作业系统:嵌入式
简介
Linux是一种很受欢迎的作业系统,它与UNIX系统兼容,开放原始码。它原本被设计为桌面系统,现在广泛套用于伺服器领域。而更大的影响在于它正逐渐的套用于嵌入式设备。uClinux正是在这种氛围下产生的。在uClinux这个英文单词中u表示Micro,小的意思,C表示Control,控制的意思,所以uClinux就是Micro-Control-Linux,字面上的理解就是"针对微控制领域而设计的Linux系统"。
uClinux是嵌入式Linux领域非常重要的分支,已成功套用于路由器、机顶盒、PDA等领域,与标準Linux在记忆体管理方面有着本质的区别。
uCLinux是一种优秀的嵌入式Linux版本,是micro-Controller-Linux的缩写。它秉承了标準Linux的优良特性, 经过各方面的小型化改造,形成了一个高度最佳化的、代码紧凑的嵌入式Linux。虽然它的体积很小,却仍然保留了Linux的大多数的优点:稳定、良好的移植性、优秀的网路功能、对各种档案系统完备的支持和标準丰富的API。它专为嵌入式系统做了许多小型化的工作,目前已支持多款CPU。 其编译后目标档案可控制在几百KB数量级,并已经被成功地移植到很多平台上。
uClinux从Linux 2.0/2.4核心派生而来,沿袭了Linux的绝大部分特性。它是专门针对没有MMU(记忆体管理单元)的CPU,并且为嵌入式系统做了许多小型化的工作。它通常用于具有很少记忆体或Flash的嵌入式作业系统。在GNU通用许可证的保证下,运行uClinux作业系统的用户可以使用几乎所有的Linux API函式。由于经过了裁剪和最佳化,它形成了一个高度最佳化,代码紧凑的嵌入式Linux。它具有体积小、稳定、良好的移植性、优秀的网路功能、完备的对各种档案系统的支持,以及丰富的API函式等优点。uClinux与Linux在兼容性方面表现出色,uClinux除了不能实现fork()外,其余uClinux的API函式与标準Linux完全相同。
针对没有MMU的CPU
全球每年生产的CPU的数量大概在二十亿颗左右,其中大部分是套用于专用性很强的各类嵌入式系统。大部分嵌入式系统为了减少系统複杂程度、降低硬体及开发成本和运行功耗,在硬体设计中取消了记忆体管理单元(MMU)模组。最初,运行于这类没有MMU的CPU之上的都是一些很简单的单任务作业系统,或者更简单的控制程式,甚至根本就没有作业系统而直接运行应用程式。在这种情况下,系统无法运行複杂的应用程式,或者效率很低,并且所有的应用程式需要重新开发,还要求开发人员十分了解硬体特性。这些都阻碍了不含MMU的嵌入式产品开发的速度和套用水平。
uClinux专门针对没有MMU的CPU,并且为嵌入式系统做了许多小型化的工作。uClinux是一个完全符合GNU/GPL公约的项目,完全开放代码。
最初的uClinux仅仅支持Palm硬体系统,基于Linux 2.0核心。随着系统的日益改进,支持的核心版本从2.0、2.2、2.4一直到现在最新的2.6。系统的开发人员从两人增加到了目前的12人,支持的硬体系统也从一种增加到了目前的十余种(支持的硬体平台如Motorola公司的M68328、M68EN322、MC68360、DragonBall系列如68EZ328、68VZ328,ColdFire系列的如5272、5307,ARM 7TDMI、MC68EN302、ETRAX、Intel i960、PRISMA、Atari 68k等等。)
根据Linuxdevices网站2004年3月的调查,uClinux在全球嵌入式Linux市场所占的份额已位居第二,仅仅落后于定製Linux(即自己下载源码进行修改定製)。同时Linux在全球嵌入式作业系统的市场份额依然处于统治地位(占40%以上),领先第二名微软公司的嵌入式作业系统三倍以上(市场份额约13%)。
特点
标準Linux可能採用的小型化方法
1. 重新编译核心
Linux核心採用模组化的设计,即很多功能块可以独立的加上或卸下,开发人员在设计核心时把这些核心模组作为可选的选项,可以在编译系统核心时指定。因此一种较通用的做法是对Linux核心重新编译,在编译时仔细的选择嵌入式设备所需要的功能支持模组,同时删除不需要的功能。通过对核心的重新配置,可以使系统运行所需要的核心显着减小,从而缩减资源使用量。
2. 製作root档案系统映象
Linux系统在启动时必须载入根(root)档案系统,因此剪裁系统同时包括root file system的剪裁。在x86系统下,Linux可以在Dos下,使用Loadlin档案载入启动,
uClinux採用的小型化方法
1.uClinux的核心载入方式
uClinux的核心有两种可选的运行方式:可以在flash上直接运行,也可以载入到记忆体中运行。这种做法可以减少记忆体需要。
Flash运行方式:把核心的可执行映象烧写到flash上,系统启动时从flash的某个地址开始逐句执行。这种方法实际上是很多嵌入式系统採用的方法。
核心载入方式:把核心的压缩档案存放在flash上,系统启动时读取压缩档案在记忆体里解压,然后开始执行,这种方式相对複杂一些,但是运行速度可能更快(ram的存取速率要比flash高)。同时这也是标準Linux系统採用的启动方式。
2.uClinux的根(root)档案系统
uClinux系统採用romfs档案系统,这种档案系统相对于一般的ext2档案系统要求更少的空间。空间的节约来自于两个方面,首先核心支持romfs档案系统比支持ext2档案系统需要更少的代码,其次romfs档案系统相对简单,在建立档案系统超级块(superblock)需要更少的存储空间。Romfs档案系统不支持动态擦写保存,对于系统需要动态保存的数据採用虚拟ram盘的方法进行处理(ram盘将採用ext2档案系统)。
3.uClinux的应用程式库
uClinux小型化的另一个做法是重写了应用程式库,相对于越来越大且越来越全的glibc库,uClibc对libc做了精简。
uClinux对用户程式採用静态连线的形式,这种做法会使应用程式变大,但是基于记忆体管理的问题,不得不这样做(这将在下文对uClinux记忆体管理展开分析时进行说明),同时这种做法也更接近于通常嵌入式系统的做法。
系统特点
嵌入式作业系统比较
由表1可以看出,对于嵌入式套用,高端平台可直接採用Linux系统,其兼容性和可移植度都较高,但对硬体处理速度和存储空间要求较高。
低端平台的最佳选择是uClinux,其性能稳定、移植性好、功能强大。
低端平台如果对实时性要求较高、套用相对简单,则可採用uc/os或其他作业系统。
基本架构
uClinux的系统与标準Linux的架构完全一致。
档案系统
uClinux系统多採用Romfs档案系统,Romfs是一种相对简单、占用空间较少的档案系统。空间的节约来自于两个方面:首先核心支持Romfs档案系统比支持ext2档案系统需要更少的代码;其次romfs档案系统相对简单,在建立档案系统超级块(Superblock)需要更少的存储空间。Romfs是唯读的档案系统,禁止写操作,因此系统同时需要虚拟盘(RAMDISK)支持临时档案和数据档案的存储。
随着技术的发展,近年来日誌档案系统在uClinux系统上得到了较多的套用,其中以支持NOR FLASH的JFFS、JFFS2档案系统和支持NAND FLASH的YAFFS最为流行。这些档案系统都支持掉电档案保护,同时支持标準的MTD驱动。
开发环境
GNU开发套件
Gnu开发套件作为通用的Linux开放套件,包括一系列的开发调试工具。主要组件:
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中的空间分布。
执行档格式
先对一些名词作一些说明:
coff(common object file format):一种通用的对象档案格式
elf(excutive linked file):一种为Linux系统所採用的通用档案格式,支持动态连线
flat:elf格式有很大的档案头,flat档案对档案头和一些段信息做了简化
uClinux系统使用flat执行档格式,gcc的编译器不能直接形成这种档案格式,但是可以形成coff或elf格式的执行档,这两种档案需要coff2flt或elf2flt工具进行格式转化,形成flat档案。
当用户执行一个套用时,核心的执行档案载入器将对flat档案进行进一步处理,主要是对reloc段进行修正(执行档载入器的详见fs/binfmt_flat.c)。以下对reloc段进一步讨论。
需要reloc段的根本原因是,程式在连线时连线器所假定的程式运行空间与实际程式载入到的记忆体空间不同。假如有这样一条指令:
jsr app_start;
这一条指令採用直接定址,跳转到app_start地址处执行,连线程式将在编译完成是计算出app_start的实际地址(设若实际地址为0x10000),这个实际地址是根据ld档案计算出来(因为连线器假定该程式将被载入到由ld档案指明的记忆体空间)。但实际上由于记忆体分配的关係,作业系统在载入时无法保证程式将按ld档案载入。这时如果程式仍然跳转到绝对地址0x10000处执行,通常情况这是不正确的。一个解决办法是增加一个存储空间,用于存储app_start的实际地址,设若使用变数addr表示这个存储空间。则以上这句程式将改为:
movl addr, a0;
jsr (a0);
增加的变数addr将在数据段中占用一个4位元组的空间,连线器将app_start的绝对地址存储到该变数。在执行档载入时,执行档载入器根据程式将要载入的记忆体空间计算出app_start在记忆体中的实际位置,写入addr变数。系统在实际处理是不需要知道这个变数的确切存储位置(也不可能知道),系统只要对整个reloc段进行处理就可以了(reloc段有标识,系统可以读出来)。处理很简单只需要对reloc段中存储的值统一加上一个偏置(如果载入的空间比预想的要靠前,实际上是减去一个偏移量)。偏置由实际的物理地址起始值同ld档案指定的地址起始值相减计算出。
这种reloc的方式部分是由uClinux的记忆体分配问题引起的,这一点将在uClinux记忆体管理分析时说明。
针对实时性的解决方案
uClinux本身并没有关注实时问题,它并不是为了Linux的实时性而提出的。另外有一种Linux--Rt-linux关注实时问题。Rt-linux执行管理器把普通Linux的核心当成一个任务运行,同时还管理了实时进程。而非实时进程则交给普通Linux核心处理。这种方法已经套用于很多的作业系统用于增强作业系统的实时性,包括一些商用版UNIX系统,Windows NT等等。这种方法优点之一是实现简单,且实时性能容易检验。优点之二是由于非实时进程运行于标準Linux系统,同其它Linux商用版本之间保持了很大的兼容性。优点之三是可以支持硬实时时钟的套用。uClinux可以使用Rt-linux的patch,从而增强uClinux的实时性,使得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%)。
uClinux针对NOMMU的特殊处理
对于uClinux来说,其设计针对没有MMU的处理器,即uClinux不能使用处理器的虚拟记忆体管理技术(应该说这种不带有MMU的处理器在嵌入式设备中相当普偏)。uClinux仍然採用存储器的分页管理,系统在启动时把实际存储器进行分页。在载入应用程式时程式分页载入。但是由于没有MMU管理,所以实际上uClinux採用实存储器管理策略(real memeory management)。这一点影响了系统工作的很多方面。
uClinux系统对于记忆体的访问是直接的,(它对地址的访问不需要经过MMU,而是直接送到地址线上输出),所有程式中访问的地址都是实际的物理地址。作业系统对记忆体空间没有保护(这实际上是很多嵌入式系统的特点),各个进程实际上共享一个运行空间(没有独立的地址转换表)。
一个进程在执行前,系统必须为进程分配足够的连续地址空间,然后全部载入主存储器的连续空间中。与之相对应的是标準Linux系统在分配记忆体时没有必要保证实际物理存储空间是连续的,而只要保证虚存地址空间连续就可以了。另外一个方面程式载入地址与预期(ld档案中指出的)通常都不相同,这样relocation过程就是必须的。此外磁碟交换空间也是无法使用的,系统执行时如果缺少记忆体将无法通过磁碟交换来得到改善。
uClinux对记忆体的管理减少同时就给开发人员提出了更高的要求。如果从易用性这一点来说,uClinux的记忆体管理是一种倒退,退回了到了UNIX早期或是Dos系统时代。开发人员不得不参与系统的记忆体管理。从编译核心开始,开发人员必须告诉系统这块开发板到底拥有多少的记忆体(假如你欺骗了系统,那将在后面运行程式时受到惩罚),从而系统将在启动的初始化阶段对记忆体进行分页,并且标记已使用的和未使用的记忆体。系统将在运行套用时使用这些分页记忆体。
由于应用程式载入时必须分配连续的地址空间,而针对不同硬体平台的可一次成块(连续地址)分配记忆体大小限制是不同(目前针对ez328处理器的uClinux是128k,而针对coldfire处理器的系统记忆体则无此限制),所以开发人员在开发应用程式时必须考虑记忆体的分配情况并关注应用程式需要运行空间的大小。另外由于採用实存储器管理策略,用户程式同核心以及其它用户程式在一个地址空间,程式开发时要保证不侵犯其它程式的地址空间,以使得程式不至于破坏系统的正常工作,或导致其它程式的运行异常。
从记忆体的访问角度来看,开发人员的权利增大了(开发人员在编程时可以访问任意的地址空间),但与此同时系统的安全性也大为下降。此外,系统对多进程的管理将有很大的变化,这一点将在uClinux的多进程管理中说明。
虽然uClinux的记忆体管理与标準Linux系统相比功能相差很多,但应该说这是嵌入式设备的选择。在嵌入式设备中,由于成本等敏感因素的影响,普偏的採用不带有MMU的处理器,这决定了系统没有足够的硬体支持实现虚拟存储管理技术。从嵌入式设备实现的功能来看,嵌入式设备通常在某一特定的环境下运行,只要实现特定的功能,其功能相对简单,记忆体管理的要求完全可以由开发人员考虑。
标準Linux系统的进程、执行绪
进程:进程是一个运行程式并为其提供执行环境的实体,它包括一个地址空间和至少一个控制点,进程在这个地址空间上执行单一指令序列。进程地址空间包括可以访问或引用的记忆体单元的集合,进程控制点通过一个一般称为程式计数器(program counter,PC)的硬体暂存器控制和跟蹤进程指令序列。
fork:由于进程为执行程式的环境,因此在执行程式前必须先建立这个能"跑"程式的环境。Linux系统提供系统调用拷贝现行进程的内容,以产生新的进程,调用fork的进程称为父进程;而所产生的新进程则称为子进程。子进程会承袭父进程的一切特性,但是它有自己的数据段,也就是说,儘管子进程改变了所属的变数,却不会影响到父进程的变数值。
父进程和子进程共享一个程式段,但是各自拥有自己的堆叠、数据段、用户空间以及进程控制块。换言之,两个进程执行的程式代码是一样的,但是各有各的程式计数器与自己的私人数据。
当核心收到fork请求时,它会先查核三件事:首先检查存储器是不是足够;其次是进程表是否仍有空缺;最后则是看看用户是否建立了太多的子进程。如果上述说三个条件满足,那幺作业系统会给子进程一个进程识别码,并且设定cpu时间,接着设定与父进程共享的段,同时将父进程的inode拷贝一份给子进程运用,最终子进程会返回数值0以表示它是子进程,至于父进程,它可能等待子进程的执行结束,或与子进程各做个的。
exec系统调用:该系统调用提供一个进程去执行另一个进程的能力,exec系统调用是??序的堆叠、数据段与程式段都会被修改,只有用户区维持不变。
vfork系统调用:由于在使用fork时,核心会将父进程拷贝一份给子进程,但是这样的做法相当浪费时间,因为大多数的情形都是程式在调用fork后就立即调用exec,这样刚拷贝来的进程区域又立即被新的数据覆盖掉。因此Linux系统提供一个系统调用vfork,vfork假定系统在调用完成vfork后会马上执行exec,因此vfork不拷贝父进程的页面,只是初始化私有的数据结构与準备足够的分页表。这样实际在vfork调用完成后父子进程事实上共享同一块存储器(在子进程调用exec或是exit之前),因此子进程可以更改父进程的数据及堆叠信息,因此vfork系统调用完成后,父进程进入睡眠,直到子进程执行exec。当子进程执行exec时,由于exec要使用被执行程式的数据,代码覆盖子进程的存储区域,这样将产生防写错误(do_wp_page)(这个时候子进程写的实际上是父进程的存储区域),
这个错误导致核心为子进程重新分配存储空间。当子进程正确开始执行后,将唤醒父进程,使得父进程继续往后执行。
uClinux的多进程处理
uClinux没有mmu管理存储器,在实现多个进程时(fork调用生成子进程)需要实现数据保护。
uClinux的fork和vfork:uClinux的fork等于vfork。实际上uClinux的多进程管理通过vfork来实现。这意味着uClinux系统fork调用完程后,要幺子进程代替父进程执行(此时父进程已经sleep)直到子进程调用exit退出,要幺调用exec执行一个新的进程,这个时候将产生执行档的载入,即使这个进程只是父进程的拷贝,这个过程也不能避免。当子进程执行exit或exec后,子进程使用wakeup把父进程唤醒,父进程继续往下执行。
uClinux的这种多进程实现机制同它的记忆体管理紧密相关。uClinux针对nommu处理器开发,所以被迫使用一种flat方式的记忆体管理模式,启动新的应用程式时系统必须为应用程式分配存储空间,并立即把应用程式载入到记忆体。缺少了MMU的记忆体重映射机制,uClinux必须在执行档载入阶段对执行档reloc处理,使得程式执行时能够直接使用物理记忆体。
uClinux是专门针对没有MMU的处理器而设计的,即uClinux无法使用处理器的虚拟记忆体管理技术。实际上uClinux採用实存储器管理策略,通过地址汇流排对物理记忆体进行直接访问。所有程式中访问的地址都是实际的物理地址,所有的进程都在一个运行空间中运行(包括核心进程),这样的运行机制给程式设计师带来了不小的挑战,在作业系统不提供保护的情况下必需小心设计程式和数据空间,以免引起应用程式进程甚至是核心的崩溃。
uClinux仍然採用存储器的分页管理,系统在启动时把实际存储器进行分页,在载入应用程式时程式分页载入。一个进程在执行前,系统必须为进程分配足够的连续地址空间,然后全部载入主存储器的连续空间中。系统不含MMU带来的另外一个问题是磁碟交换空间无法使用,对于资源有限的嵌入式系统而言,系统执行时如果缺少记忆体将无法通过磁碟交换来得到改善。
MMU的省略虽然带来了系统及应用程式开发的限制,但对于成本和体积敏感的嵌入式设备而言,其套用环境和套用需求并不要求複杂和相对昂贵的硬体体系,对于功能简单的专用嵌入式设备,记忆体的分配和管理完全可以由开发人员考虑。
多进程管理
由于uClinux没有MMU管理存储器,在实现多个进程时需要实现数据保护。uClinux的虽然支持fork函式,但其实质是和vfork:实际上uClinux所有的多进程管理都通过vfork来实现。
vfork不拷贝父进程的页面,只是初始化私有的数据结构与準备足够的分页表。调用完成后父子进程事实上共享同一块存储器,因此子进程可以更改父进程的数据及堆叠信息,所有父进程进入睡眠,直到子进程执行exec。当子进程正确开始执行后,将唤醒父进程,使得父进程继续往后执行。这意味着uClinux系统fork调用完成后,要幺子进程代替父进程执行(此时父进程已经休眠)直到子进程调用exit退出,要幺调用exec执行一个新的进程。
vfork是uClinux与标準Linux应用程式的开发中最重要的不同之处,只有对vfork与fork两个函式的差异和程式处理有详细的了解才能顺利地完成从Linux到uClinux的程式移植。
缺点
正如中国古语云“人无完人”,uClinux也有一些不足之处:
文档的不足
与Linux及其他自由软体类似,uClinux的文档十分不足:缺乏组织和一致的文档、热门技术和分类文档众多而杂乱无章、非热点部分文档缺失甚至没有文档。对于开发人员而言,往往要深入程式的原始码找寻有用的资料。
Bug问题
uClinux与硬体平台直接相关。对于有商业公司赞助的硬体平台,其相关代码和Bug更新较快,编译和执行都十分顺利;但对于非商业支持的硬体平台,其核心和应用程式代码都得不到及时更新和排错。这种现象在核心原始码树还不是十分普遍,但在uClinux自带的应用程式库中却经常发生编译错误,往往是增加了一个应用程式或改变了运行库便导致无法编译。这就需要开发者投入足够的时间和精力进行排错和修改,也会导致开发进度的延误。
实时性讨论
与Linux一样,uClinux本身并不支持实时性套用,但通过实时性的修改(RTLinux或RTAI)可以提供基于核心空间和用户空间的硬实时和软实时的系统调用。