一、内核态和用户态
1.Kernel Mode(内核态)
内核模式下(执行内核空间的代码),代码具有对硬件的所有控制权限。可以执行所有CPU指令,可以访问任意地址的内存。
2.User Mode(用户态)
在用户模式下(执行用户空间的代码),代码没有对硬件的直接控制权限,也不能直接访问地址的内存;
只能访问映射其地址空间的页表项中规定的在用户态下可访问页面的虚拟地址;
程序是通过调用系统接口(System Call APIs)来达到访问硬件和内存。
二、Linux利用CPU实现内核态和用户态
ARM:
内核态(svc模式)
用户态(usr模式)
x86 :
内核态(ring 0 )
用户态(ring 3)// x86有ring 0 - ring3四种特权等级
三、Linux实现内核态和用户态切换
ARM Linux的系统调用实现原理是采用swi软中断从用户态切换至内核态;
x86是通过int 0x80中断进入内核态;
四、Linux只能通过系统调用和硬件中断从用户空间进入内核空间
执行系统调用的内核代码运行在进程上下文中,他代表调用进程执行操作,因此能够访问进程地址空间的所有数据;
处理硬件中断的内核代码运行在中断上下文中,他和进程是异步的,与任何一个特定进程无关通常,一个驱动程序模块中的某些函数作为系统调用的一部分,而其他函数负责中断处理。
五、Linux下应用程序调用驱动程序流程
应用程序(API函数)--->对应的库函数--->通过系统调用进入内核--->驱动程序中的函数--->硬件
1.Linux下进行驱动开发,完全将驱动程序与应用程序隔开,中间通过C标准库函数以及系统调用完成驱动层和应用层的数据交互;
2.驱动加载成功以后会在"/dev"目录下生成一个相应的文件,应用程序通过对"/dev/xxx"(xxx是具体的驱动文件名字)的文件进行相应的操作即可实现对硬件的操作;
3.用户空间不能直接对内核进行,因此必须使用一个叫做“系统调用”的方法来实现从用户空间“陷入”到内核空间,这样才能实现对底层驱动的操作;
4.每一个系统调用,在驱动中都有与之对应的一个驱动函数,在Linux内核文件include/linux/fs.h中有个叫做file_operations的结构体,此结构体就是Linux内核驱动操作函数集合。
大概流程:
1.加载一个驱动模块,产生一个设备文件,有唯一对应的inode结构体;
2.应用层调用open函数打开设备文件,对于上层open调用到内核时会发生一次软中断,从用户空间进入到内核空间;
3.open函数会调用到sys_open(内核函数),sys_open根据文件的地址,找到设备文件对应的struct inode结构体描述的信息,可以知道接下来要操作的设备类型(字符设备还是块设备),还会分配一个struct file结构体;
4.根据struct inode结构体里面记录的主设备号和次设备号,在驱动链表(管理所有设备的驱动)里面,根据找到字符设备驱动;
5.每一个字符设备都有一个struct cdev结构体。此结构体描述了字符设备所以信息,其中最重要的一项就是字符设备的操作函数接口;
6.找到struct cdev结构体后,Linux内核就会将struct cdev结构体所在的内存空间首地址记录在struct inode结构体i_cdev成员中,将struct cdev结构体中的记录函数操作接口地址记录在struct file结构体的f_ops成员中;
7.执行xxx_open驱动函数。