uClinux是针对操控范畴的嵌入式linux操作体系,它从Linux 2.0/2.4内核派生而来,沿用了干流Linux的绝大部分特性。合适不具备内存办理单元(MMU)的微处理器/微操控器。没有MMU支撑是uClinux与干流Linux的根本差异。
规范Linux是针对有MMU的处理器规划的。在这种处理器上,虚拟地址被送到MMU,把虚拟地址映射为物理地址。经过赋予每个使命不同的虚拟-物理地址转化映射,支撑不同使命之间的维护。
对uCLinux 来说,其规划针对没有MMU的处理器,不能运用处理器的虚拟内存办理技能。uCLinux依然选用存储器的分页办理,体系在启动时把实践存储器进行分页。在加载应用程序时程序分页加载。可是因为没有MMU办理,所以实践上uCLinux选用实存储器办理战略。uCLinux体系关于内存的拜访是直接的,一切程序中拜访的地址都是实践的物理地址。操作体系对内存空间没有维护,各个进程实践上同享一个运转空间。一个进程在履行前,体系有必要为进程分配满足的接连地址空间,然后悉数载入主存储器的接连空间中。
一起,uClinux有着特别小的内核和用户软件空间。了解干流Linux的开发者会留意到在 uClinux下作业的细小差异,但相同也能够很快了解uclinux的一些特性。关于规划内核或体系空间的应用程序的开发者,要特别留意uClinux 既没有内存维护,也没有虚拟内存模型,别的,有些内核体系调用也有差异。
1.1 内存维护
没有内存维护(Memory Protection)的操作会导致这样的成果:即便由无特权的进程来调用一个无效指针,也会触发一个地址过错,并潜在地引起程序溃散,乃至导致体系的挂起。明显,在这样的体系上运转的代码有必要细心编程,并深化测验来确保健壮性和安全。
关于一般的Linux来说,需求运转不同的用户程序,假如没有内存维护将大大下降体系的安全性和可*性;可是关于嵌入式uClinux体系而言,因为所运转的程序往往是在出厂前现已固化的,不存在损害体系安全的程序侵入的危险,因而只需应用程序经过较完好的测验,出现问题的概率就能够操控在有限的范围内。
1.2 虚拟内存
没有虚拟内存(Virtual Memory)首要导致下面几个结果:
首要,由内核所加载的进程有必要能够独立运转,与它们在内存中的方位无关。完结这一方针的第一种方法是一旦程序被加载到RAM中,那么程序的基准地址就“固定”下来;另一种方法是发生只运用相对寻址的代码(称为“方位无关代码”,Position Independent Code,简称PIC)。uClinux对这两种形式都支撑。
其次,要处理在扁平(flat)的内存模型中的内存分配和开释问题。十分动态的内存分配会形成内存碎片,并或许耗尽体系的资源。关于运用了动态内存分配的那些应用程序来说,增强健壮性的一种方法是用预分配缓冲区池(Preallocated buffer pool)的方法来替代malloc()调用。
因为uclinux中不运用虚拟内存,进出内存的页面交流也没有完结,因为不能确保页面会被加载到RAM中的相同方位。在一般核算机上,操作体系答应应用程序运用比物理内存(RAM)更大的内存空间,这往往是经过在硬盘上建立交流分区来完结的。可是,在嵌入式体系中,一般都用FLASH存储器来替代硬盘,很难高效地完结内存页面交流的存取,因而,对运转的应用程序都约束其可分配空间不大于体系的RAM空间。
最终,uClinux方针板处理器缺少内存办理的硬件单元,使得Linux的体系接口需求作些改动。有或许最大的不同便是没有fork()和brk()体系调用。调用fork()将仿制出进程来创立一个子进程。在Linux下,fork()是运用copy-on-write页面来完结的。因为没有MMU, uclinux不能完好、可*地仿制一个进程,也没有对copy-on-write的存取。为了补偿这一缺点,uClinux完结了vfork(),当父进程调用vfork()来创立子进程时,两个进程同享它们的悉数内存空间,包含仓库。子进程要么替代父进程履行(此刻父进程现已sleep)直到子进程调用exitI()退出,要么调用exec()履行一个新的进程,这个时分将发生可履行文件的加载。即便这个进程仅仅父进程的复制,这个进程也不能防止。当子进程履行exit()或exec()后,子进程运用wakeup把父进程唤醒,父进程持续往下履行。
留意,多使命并没有受影响。哪些老式的、广泛运用fork()的网络后台程序(daemon)的确是需求修正的。因为子进程运转在和父进程相同的地址空间内,在一些情况下,也需求修正两个进程的行为。
许多现代的程序依靠子进程来履行根本使命,使得即时在进程负载很重时,体系仍能够坚持一种“可交互”的状况,这些程序或许需求实质上的修正来在uClinux下完结相同的使命。假如一个要害的应用程序十分依靠这样的结构,那就不得不对它从头编写了。
假设有一个简略的网络后台程序(daemon),很多运用了fork()。这个daemon总监听一个闻名端口(或套接字)等候网络客户端来衔接。当客户端衔接时,这个daemon给它一个新的衔接信息(新的socket编号),并调用fork()。子进程接下来就会和客户端在新的socket上进行衔接,而父进程被开释,能够持续监听新的衔接。
uClinux 既没有主动成长的仓库,也没有brk()函数,这样,用户空间的程序有必要运用mmap() 指令来分配内存。为了便利,在uclinux的C言语库中所完结的malloc()实质上便是一个mmap()。在编译时,能够指定程序的仓库巨细。