1.1 单片机程序的特点及结构
单片机程序中,比如实际使用ST芯片时,我们常通过CubeMX图形化工具配置工程,CubeMX会自动生成HAL库初始化代码、系统初始化代码以及GPIO初始化代码。我们只需编写核心业务逻辑,比如读取按键的状态,判断是否按下,若按下则控制蓝灯闪烁。
读取按键状态使用的是HAL库函数。这个HAL库函数针对GPIO引脚设计,包含两个参数:一是要读取的GPIO端口(如GPIOA、GPIOB等),二是具体引脚。这些函数由芯片厂商完善,本质上也是通过操作寄存器实现功能,大家查看函数源码就能发现其底层是直接操作寄存器的。
单片机程序的核心特点:main函数中包含的大量硬件初始化工作已由工具生成,我们只需聚焦业务逻辑,通过HAL库或直接操作寄存器操控硬件。寄存器的地址可通过芯片手册查询,直接读写对应地址即可完成硬件操作。
在单片机程序中,原本没有应用程序和驱动程序的明确划分。资深的单片机工程师会出于良好的编程习惯,将程序分为两层:上层是与硬件无关的业务逻辑(比如“读取按键后控制LED闪烁”的逻辑,仅调用read_key和blink_led函数),下层是由底层驱动实现的read_key、blink_led等硬件操作函数。但这两层之间没有严格的界限,仅为编程规范层面的划分。
1.2 Linux系统中应用程序与驱动程序的严格分界
Linux系统则完全不同,应用程序与驱动程序存在严格的界限。从底层硬件操作的本质来看,Linux和单片机一致,都是通过读写寄存器实现硬件控制,但应用程序的操作权限被严格限制。
在单片机系统中,程序通常由少数人开发维护,代码质量可控,出现问题可调试修复后再发布,且产品功能相对简单,因此main函数可直接读写硬件寄存器。
而Linux系统的使用场景更复杂,以安装Windows系统的电脑为例,联想公司生产电脑并预装系统后,用户包括普通使用者和成千上万的开发者,开发者水平参差不齐,甚至存在恶意程序。为保证系统健壮性和安全性,Linux不允许应用程序直接访问硬件,必须通过驱动程序间接访问。
核心原因在于权限差异:单片机通常称为MCU(微控制器单元),而能运行Linux的芯片称为MPU(微处理器单元),MPU的关键特点是具备MMU(内存管理单元),这是实现权限管理的核心。
1.3 MMU(内存管理单元)的作用
我们通过结构对比理解MMU的作用:
单片机系统中,MCU芯片包含CPU、GPIO模块和内存。CPU发出的地址和数据可直接到达对应硬件设备,只要地址在设备的地址范围内,设备就会响应,中间无任何权限管控。
Linux系统中,CPU、GPIO模块和内存之间增加了MMU。CPU发出的地址需先经过MMU判断:若当前CPU运行在用户态(应用程序运行模式),且地址指向硬件设备,MMU会拒绝访问;若运行在内核态(驱动程序运行模式,如SVC管理模式),MMU则允许地址访问硬件。简单来说,MMU实现了权限管控,Linux系统充分利用MMU,从软硬件设计上阻止了应用程序直接访问硬件寄存器。
1.4 Linux中应用程序调用驱动程序的流程
应用程序需通过open、read、write等标准接口调用驱动程序。
应用程序通过open函数打开一个特殊的设备文件(而非普通文件),打开设备文件的过程就是关联驱动程序的过程,之后通过read、write函数与驱动程序交互。
这里需要明确:应用程序无法直接调用驱动程序的函数(如驱动中的driver_open函数)。若允许直接调用,恶意程序可能调用内核或驱动中的危险函数(如电源驱动的关机函数),导致系统崩溃。因此Linux通过“异常触发”实现用户态到内核态的切换,进而调用驱动程序。
具体切换过程:应用程序调用的open、read、write等函数并非用户实现,而是由glibc库实现。这些库函数的核心逻辑是:先设置特定寄存器(如R0寄存器,根据操作类型设置不同值,value1对应open、value2对应read、value3对应write),再执行一条SWI汇编指令。
SWI指令会触发异常,类似单片机中的按键中断——按键按下产生中断后,CPU会立即执行中断服务程序;同理,SWI指令触发异常后,CPU会执行内核提供的SWI异常处理函数,同时切换CPU模式至内核态,获得访问硬件的权限。
异常处理函数的核心逻辑:根据之前设置的寄存器值(如R0的值)分辨应用程序的需求——若R0=value1,说明需要执行open操作,就调用内核中的system_open函数;若R0=value2,说明需要执行read操作,就调用对应的内核函数。
进一步,system_open函数会判断打开的文件类型:若为普通文件(如1.txt),则执行普通文件的读写逻辑;若为设备文件(设备节点),则找到对应的驱动程序,调用驱动程序的open函数。驱动程序的open函数中,即可执行硬件寄存器的读写操作,完成应用程序对硬件的间接控制。
1.5 核心知识点总结
最后重申核心知识点:
- 硬件操作本质一致:单片机和Linux都是通过读写寄存器实现硬件控制。
- 程序分层差异:单片机程序的应用层与驱动层无严格界限,仅为编程习惯;Linux系统则强制分层,应用层与驱动层有不可逾越的界限。
- 权限控制核心:Linux通过MPU的MMU实现权限管理,应用程序运行在用户态,无硬件访问权限;驱动程序运行在内核态,具备硬件访问权限。
- 调用流程:应用程序通过glibc库的open、read、write等标准接口,触发SWI异常切换至内核态,由内核异常处理函数根据寄存器值分辨操作需求,最终调用驱动程序的对应函数完成硬件操作。