
Linux 驱动课程火热更新中:放大招!瑞芯微底层驱动课程~

在 Linux 操作系统中,应用进程运行于用户空间,无法直接访问或操作硬件设备。当进程需要进行硬件相关操作(例如读取磁盘文件、发送网络数据包、接收键盘输入等)时,必须通过特定的机制,从用户态切换至内核态。
这个过程通常遵循以下步骤:
用户态发起请求:应用程序通过调用标准库函数(如 read()、write()、send() 等)发起硬件访问请求。
触发系统调用:库函数内部会触发一个软中断或使用专门的指令,发起系统调用。系统调用是操作系统内核为用户空间提供的一组受控的、预先定义好的接口,它是用户程序进入内核的唯一入口。
上下文切换:CPU 接收到中断信号后,会进行上下文切换。这包括:
模式切换:将 CPU 的执行模式从低权限的用户态切换到高权限的内核态。在内核态下,代码可以执行特权指令并访问所有硬件资源。
保存现场:保存当前进程在用户态的寄存器状态、程序计数器等信息,以便后续能正确返回。
跳转执行:CPU 转而执行内核中对应的系统调用处理函数。
内核态执行与硬件交互:
内核中的相应子系统接收请求,并调用对应的设备驱动程序。
驱动程序与硬件控制器进行通信(通过读写特定的I/O端口或内存映射寄存器),发起实际的硬件操作(如磁盘寻道、DMA数据传输等)。
内核可能会在此过程中管理缓冲区、进行协议封装/解封装等复杂工作。
返回结果与切换回用户态:
硬件操作完成后(可能是同步等待,也可能是异步通知),驱动程序将结果返回给内核。
内核恢复之前保存的用户态进程上下文。
CPU 执行模式从内核态切换回用户态。
控制权交还给用户进程,并携带操作结果或错误码。
为何要这样设计?
这种严格的隔离机制是操作系统稳定性和安全性的基石:
安全性:防止用户程序直接操作硬件导致系统崩溃或数据损坏。
稳定性:内核统一管理和仲裁所有硬件访问,避免资源冲突。
抽象性:为应用程序提供统一、简洁的设备访问接口,无需关心硬件具体细节。
多任务与虚拟化:内核可以公平调度多个进程的硬件请求,并实现资源的虚拟化。
因此,“用户态-内核态”切换和系统调用机制,是 Linux 在提供强大硬件访问能力的同时,确保系统整体可靠、安全和高效运行的核心设计。
如上图所示,应用程序控制硬件的必要的必要因素包括应用程序、设备文件、设备驱动程序和硬件,前面三个部分在控制硬件时的具体作用和形式如下
上图展示了应用程序控制硬件的必要组成部分,包括应用程序、设备文件、设备驱动程序和硬件。其中,前三者在控制流程中的作用与形式具体如下:
应用程序:指为实现特定功能而调用系统函数或自定义函数执行的程序。在 Linux 系统中,应用程序在用户空间以进程形式运行,仅能访问系统分配给它的内存和文件资源,无法直接操控硬件。
设备文件:与普通文件不同,设备文件在内核的文件系统结构中充当桥梁,连接应用程序与设备驱动程序。应用程序通过对设备文件执行低级文件 I/O 操作(读取或写入数据),间接触发相应的设备驱动程序函数,从而实现对硬件的控制。
设备驱动程序:驱动程序以内核模块形式动态加载,或直接编译在内核中。应用程序通过关联的设备文件调用驱动程序,进而操控硬件。根据数据传输方式,驱动程序可分为字符设备驱动等多种类型。
进而我们对上面进行抽象:
应用程序利用 open 函数打开设备文件后获取类型信息和主设备号,再利用该信息得到注册在 chrdevs 的设备驱动程序 index,接着利用获取到的 index 值,获得注册在 chrdevs 变量上的 file_operations 结构体地址,该结构体记录了字符设备驱动程序使用其注册函数设定相应低级文件输入输出函数的内容。
如下图所示,表示了设备驱动程序的运作方式。
在底层驱动里使用ioctl函数来对设备进行控制,ioctl的运行概念结构如图:
ioctl 函数上传送的变量 cmd 是应用程序用于区别向设备驱动程序请求处理内容的值,cmd 除了可区别的数字外,还包含有助于处理的几种相应的信息。cmd 的大小为 32 位,位的结构如下图: