本题基于MCU 的嵌入式系统选用uCLinux系统与Linux进行对比。
(一)处理器:
对uCLinux 来说,其设计针对没有MMU的处理器,不能使用处理器的虚拟内存管理技术。uCLinux仍然采用存储器的分页管理,系统在启动时把实际存储器进行分页。 在加载应用程序时程序分页加载。但是由于没有MMU管理,所以实际上uCLinux采用实存储器管理策略。uCLinux系统对于内存的访问是直接的,所有程序中访问的地址都是实际的物理地址。操作系统对内存空间没有保护,各个进程实际上共享一个运行空间。一个进程在执行前,系统必须为进程分配足够的连续地址空间,然后全部载入主存储器的连续空间中。
Linux系统采用的处理器是ARM体系,ARM开发了各种Cortex-M处理器,拥有不同级别的指令集,特性,性能,系统和调试特性。
(二)引导程序:
uCLinux系统引导程序代码量虽少,但是作用非常大,相当 于PC上的BIOS,负责将操作系统内核固化到Flash中和系统初始化工作,然后将系统控制权交给操作系统。
Linux引导程序是存储在 MBR(主引导记录)或 GUID(全局唯一标识符)分区表中的一个小程序,用于帮助把操作系统装载到内存中。
1.GNU GRUB 是一个非常受欢迎,也可能是用的最多的具有多重引导能力的 Linux 引导程序,它以原始的 Eirch Stefan Broleyn 发明的 GRUB为基础。GNU GRUB 增强了原来的 GRUB,带来了一些改进、新的特性和漏洞修复。
2.LILO (全称LInux LOader)是一个简单但强大且非常稳定的 Linux 引导程序。
3.基于 GRUB,BURG 是一个相对来说比较新的引导程序(LCTT 译注:已于 2011 年停止了开发)。由于 BURG 起源于 GRUB, 所以它带有一些 GRUB 主要特性。尽管如此, BURG 也提供了一些出色的特性,比如一种新的对象格式可以支持包括 Linux、Windows、Mac OS、 FreeBSD 等多种平台。
另外,BURG 支持可高度配置的文本和图标模式的引导菜单,计划增加的“流”支持未来可以不同的输入/输出设备一同工作。
4.SyslSyslinux 是一种能从光盘驱动器、网络等进行引导的轻型引导程序。Syslinux 支持诸如 MS-DOS 上的 FAT、 Linux 上的 ext2、ext3、ext4 等文件系统。Syslinux 也支持未压缩的单一设备上的 Btrfs。
(三)开发流程:
1.自动生长的堆栈:
uCLinux没有自动生长的堆栈,也没有brk()函数,这样,用户空间的程序必须使用mmap() 命令来分配内存。为了方便,在uclinux的C语言库中所实现的malloc()实质上就是一个mmap()。在编译时,可以指定程序的堆栈大小。
2.多进程处理:
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处理,使得程序执行时能够直接使用物理内存。
3.通用架构的内核:
在uCLinux的发布中,/linux/mmnommu目录取代了/linux/mm目录,前者是修改后的内存管理子系统被修改,去除了MMU的硬件依赖,并在内核软件自身提供基本的内存管理函数。
很多子系统需要重新修改、添加或重写。内核和用户内存分配及释放进程必须重新实现,对透明交互页面调度的支持也被去除。内核中,加入了支持“内核无关代码(PIC)”的程序支持模块,并使用了新的二进制目标代码格式,称扁平格式,用来支持PIC(有非常紧凑的头部)。
内核也提供了支持ELF格式的程序加载模块,用来支持使用固定基准地址的可执行程序。两种模式各有利弊,传统的PIC运行快,代码紧凑,但有代码大小限制。对于内核开发者来说,uCLinux基本上与Linux没有区别,唯一的区别就是不能利用MMU提供的内存管理。实际上这对内核并没有影响。
Linux下所有标准的可执行文件的格式在uCLinux并不被支持,因为这些格式也用到了虚拟内存的一些功能。uCLinux使用的是另外一种扁平格式。扁平格式是一种简洁高效的可执行文件格式,它值包含可执行的代码和数据,还有一些把可执行文件加载到内存任意位置所需要的可重定位信息。
(四)内存:
1.内存保护:
即使由无特权的进程来调用一个无效指针,也会触发一个地址错误,并潜在地引起程序崩溃,甚至导致系统的挂起。显然,在这样的系统上运行的代码必须仔细编程,并深入测试来确保健壮性和安全。
对于普通的Linux来说,需要运行不同的用户程序,如果没有内存保护将大大降低系统的安全性和可靠性;然而对于嵌入式uClinux系统而言,由于所运行的程序往往是在出厂前已经固化的,不存在危害系统安全的程序侵入的隐患,因此只要应用程序经过较完整的测试,出现问题的概率就可以控制在有限的范围内。
2.虚拟内存:
首先,由内核所加载的进程必须能够独立运行,与它们在内存中的位置无关。 实现这一目标的第一种办法是一旦程序被加载到RAM中,那么程序的基准地址 就“固定”下来;另一种办法是产生只使用相对寻址的代码uClinux对这两种模式都支持。
其次,要解决在扁平(flat)的内存模型中的内存分配和释放问题。非常动态的内存分配会造成内存碎片,并可能耗尽系统的资源。对于使用了动态内存分配的那些应用程序来说,增强健壮性的一种办法是用预分配缓冲区池(Preallocated buffer pool)的办法来取代malloc()调用。
由于uclinux中不使用虚拟内存,进出内存的页面交换也没有实现,因为不能保证页面会被加载到RAM中的同样位置。在普通计算机上,操作系统允 许应用程序使用比物理内存(RAM)更大的内存空间,这往往是通过在硬盘上设立交换分区来实现的。但是,在嵌入式系统中,通常都用FLASH存储器来代替硬盘,很难高效地实现内存页面交换的存取,因此,对运行的应用程序都限制其可分配空间不大于系统的RAM空间。