当前位置: 首页 > 科技观察

我可以在没有mmu的情况下运行Linux吗?

时间:2023-03-15 22:38:29 科技观察

为什么需要mmu?我们知道应用程序不能随意访问内存。如果允许应用程序直接访问物理内存,那么计算机是非常危险的,计算机内存中的所有内容将被完全暴露。于是mmu出现了,mmu是内存管理单元,应用程序访问的是虚拟内存。虚拟内存经过mmu转换后,就变成了物理内存。物理内存对应于实际物理存储磁盘上的地址。linux主线代码的正常运行需要mmu机制。但是如果你想在没有mmu的情况下运行linux内核代码,你需要裁剪和配置它。最难的是调试。mmu模块一般是CPU的内部组件。当然,也有mmu所在的情况。如果想运行没有mmu的linux内核代码,不妨看看uclinux什么是uclinux?在英文单词uClinux中,u的意思是Micro,意思是小,C是Control的意思,意思是控制,所以uClinux就是Micro-Control-Linux,字面理解就是“为微控制领域设计的Linux系统”.uclinux和linux的区别在于没有虚拟内存管理单元mmu,运行时不能添加进程栈。不支持分页。可执行程序不是精灵,而是扁平的。他们使用vfork而不是fork。源自Linux2.0/2.4内核,继承了主流Linux的大部分特性。对于没有内存管理单元(MMU)的微处理器/微控制器。缺少MMU支持是uClinux与主流Linux的根本区别。对于uCLinux,它是为没有MMU的处理器设计的,不能使用处理器的虚拟内存管理技术。uCLinux仍然采用内存分页管理,系统在启动时对实际内存进行分页。程序在加载应用程序时加载页面。但由于没有MMU管理,uCLinux实际上采用了真正的内存管理策略。uCLinux系统是直接访问内存的,所有程序访问的地址都是实际的物理地址。操作系统不保护内存空间,每个进程实际上共享一个运行空间。一个进程在执行前,系统必须为进程分配足够的连续地址空间,然后全部加载到主存的连续空间中。内存保护没有内存保护的操作会导致这样的结果:即使非特权进程调用无效指针,也会触发地址错误,并有可能导致程序崩溃,甚至导致系统挂起。显然,在此类系统上运行的代码必须经过仔细编程和全面测试,以确保稳健性和安全性。对于普通Linux,需要运行不同的用户程序,如果没有内存保护,系统的安全性和可靠性会大大降低;但是,对于嵌入式uClinux系统,由于运行的程序往往是被治愈的,不存在程序入侵危及系统安全的隐患,所以只要应用程序通过了比较完善的测试,就可以控制出现问题的概率在有限的范围内。虚拟内存虚拟内存(VirtualMemory)的缺失主要导致以下后果:首先,内核加载的进程必须能够独立运行,而不管它们在内存中的位置。实现此目标的第一种方法是在程序加载到RAM后“固定”程序的基地址;另一种方法是生成仅使用相对寻址的代码(称为“位置无关代码”,PositionIndependentCode,简称PIC)。uClinux支持这两种模式。其次,要解决平面内存模型中的内存分配和释放问题。非常动态的内存分配会产生内存碎片并耗尽系统资源。对于那些使用动态内存分配的应用程序,提高稳健性的一种方法是用预分配的缓冲池替换malloc()调用。由于在uclinux中不使用虚拟内存,页面交换进出内存没有实现,因为不能保证页面将被加载到RAM中的相同位置。在普通计算机上,操作系统允许应用程序使用比物理内存(RAM)更多的内存空间,通常是通过在硬盘上设置交换分区来实现的。然而在嵌入式系统中,通常采用FLASH存储器代替硬盘,难以高效实现内存页交换的访问。因此,运行的应用程序被限制在不超过系统的RAM空间。注意:多任务处理不受影响。那些广泛使用fork()的老式网络守护进程确实需要修改。由于子进程与父进程运行在相同的地址空间中,因此在某些情况下还需要修改两个进程的行为。许多现代程序都依赖子进程来执行基本任务,这样即使进程负载很重,系统仍然可以保持“交互”状态,这些程序可能需要大量修改才能在uClinux下完成相同的任务。如果一个关键的应用程序严重依赖这样的结构,它就必须被重写。假设有一个广泛使用fork()的简单网络后台程序(守护进程)。daemondirector侦听一个众所周知的端口(或套接字),等待网络客户端连接。当客户端连接时,守护进程给它一个新的连接信息(新的套接字号)并调用fork()。然后子进程将连接到新套接字上的客户端,父进程被释放并可以继续侦听新连接。uClinux既没有自动增长栈也没有brk()函数,所以用户空间的程序必须使用mmap()命令来分配内存。为了方便起见,uclinux的C语言库中实现的malloc()本质上就是一个mmap()。在编译时,可以指定程序的堆栈大小。最后,uClinux目标板处理器缺少内存管理硬件单元,这需要对Linux系统接口进行一些改动。最大的区别可能是没有fork()和brk()系统调用。调用fork()将克隆进程以创建子进程。在Linux下,fork()是使用写时复制页面实现的。因为没有MMU,uclinux不能完全可靠的复制一个进程,也就没有copy-on-write的访问权限。为了弥补这个缺陷,uClinux实现了vfork()。当父进程调用vfork()创建子进程时,两个进程共享它们的整个内存空间,包括堆栈。子进程要么代替父进程执行(父进程已经在休眠),直到子进程调用exitI()退出,要么调用exec()执行新进程,此时会加载可执行文件.即使进程只是父进程的副本,也无法避免这个进程。当子进程执行exit()或exec()时,子进程使用wakeup唤醒父进程,父进程继续执行。通用体系结构中的内核更改:在uCLinux发行版中,/linux/mmnommu目录取代了/linux/mm目录。前者是修改后的内存管理子系统,经过修改去除了MMU对硬件的依赖,在内核软件本身中提供。基本的内存管理功能。许多子系统需要重新修改、添加或重写。必须重新实现内核和用户内存分配和释放过程,并且还删除了对透明交互/分页的支持。在内核中,支持“内核独立代码(PIC)”程序支持模块,并使用一种新的二进制目标代码格式,称为平面格式,以支持PIC(具有非常紧凑的标头)。内核还提供了支持ELF格式的程序加载模块,用于支持使用固定基地址的可执行程序。两种模式各有利弊。传统PIC运行速度快,代码紧凑,但有代码大小限制。例如Motorola68K架构的16位相对跳转限制了PIC程序不超过程序代码的大小为32KB,对于使用固定引用地址的方法的程序代码没有大小限制在运行时,但当陈旭被内核加载时,会造成更多的系统开销。对于内核开发者来说,uCLinux与Linux基本相同,唯一不同的是不能使用MMU提供的内存管理。事实上,这对内核没有影响。uCLinux不支持Linux下的所有标准可执行文件格式,因为这些格式也使用部分虚拟内存。功能。uCLinux使用另一种平面格式。平面格式是一种简洁高效的可执行文件格式,它包含可执行代码和数据,以及将可执行文件加载到内存中任意位置所需的一些内容。可重定位信息。总结:在往uClinux移植应用和自己写代码的过程中,我们会一直关注这些特性:1、配置时,如果可能的话,选择--disable-shared和--enable-static。2。将源代码中出现的所有fork()更改为vfork();3。将-Wl,-elf2flt添加到交叉编译器的链接选项和Makefile中的编译选项。尽管这只是一个链接选项,但我在LDFLAGS和CFLAGS甚至CC中都小心地指定了它。选项是将ELF格式转换成uClinux可以识别的FLAT格式。在这个转换过程中,我们不能使用strip去除ELF文件的一些信息,更不能使用-O2选项来优化代码。因为去掉的部分信息可能会导致最终生成的FLAT格式文件运行出现问题。