应用程序与驱动的交互原理
驱动程序:用来与硬件打交道,获取硬件的数据;
应用程序:通过系统调用与驱动程序打交道,借助驱动程序完成对硬件的操作;
在单片机中,驱动程序和应用程序的区分并不明显,因为没有操作系统的存在,因此在单片中将驱动和应用杂糅,形成一个或多个.c文件,共同控制板上资源;
而 在跑Linux的设备中,由于各种原因(比如:处于对于操作系统内核数据的保护,对于操作系统安全的保护),是将驱动和应用完全分开的,驱动程序运行在内核态,而应用程序运行在用户态;
从内核态到用户态,有三种方式能够进入:① 系统调用 ②中断/异常 ③陷入
其中系统调用其实也是通过中断的方式进入内核的,只不过是特定的中断处理函数,用来识别是调用的哪个系统调用号;
应用程序与驱动程序的交互是通过系统调用完成的
下面我们将这部分的流程进行一个整理:反汇编细节请看这篇文章:字符设备驱动之:从用户程序中的系统调用到驱动中的具体实现
重点:
①用户空间调用系统调用函数(如open,read,write等)
②在系统调用函数内展成内嵌汇编,通过(软)中断陷入内核中
③在内核中调用system_call函数,该函数中通过传入的系统调用号查找sys_call_table,调用对应的系统调用函数(比如sys_read、sys_write)
④在sys_read、sys_write函数中,通过fget_light(fd, &fput_needed)
函数得到传入fd对应在内核的file结构体,将其作为入参传入vfs_read(file, buf, count, &pos)
函数
⑤在函数vfs_read(file, buf, count, &pos)
中,通过判断是否定义了对于这个文件的驱动操作来判定,是使用驱动中的read,还是使用默认的系统read
注意:
- 每一个打开的文件在内核中都有一个file结构体来维护,这里就调用fget_light通过read函数提供的fd得到了该文件的file结构体,然后调用了vfs_read函数。
- 在驱动初始化的时候就将file_operations结构体赋值给了这个驱动对应的设备文件的成员f_op,所以这里就调用到了最终的驱动程序中的具体实现函数:xxx_read
- 这也证明了,在后面的实验中,我明明没有写一些设备驱动程序中的对应的file_operations中的处理函数,同样操作系统能够正确的open、close等等;