Linux设备驱动基础
- 明确:Linux系统的两个空间:用户态(用户空间)和内核态(内核空间)。
- 推荐:
- 《Unix环境高级编程》第三版
- 《Linux设备驱动程序》第三版
- 《Linux内核设计与实现》第三版
用户空间的特点
- 用户空间包含的软件就是各种应用程序(ls/cd/,),也包括自己编写的UC程序,QT程序,静态库,动态库等等,这些软件在运行的时候就运行在用户空间。
- 用户空间的软件在运行的时候,对应的CPU核的工作模式就是user用户模式。
- 用户空间的软件不允许直接访问内核空间的代码,地址和数据。要想访问内核空间必须只能通过系统调用。
- Linux系统的4G虚拟地址空间中,用户空间每个进程独占前3G,用户虚拟地址范围:0x0000000xBFFFFFF。内核空间进程共享后1G,内核虚拟地址为:0xC0000000xCFFFFFF。
- 用户空间的软件同样不允许直接访问外设的物理地址,要想访问外设的物理地址必须将物理地址映射到用户空间的虚拟地址上(简称用户虚拟地址),将来访问虚拟映射的用户虚拟地址就是在访问物理地址。
- 用户空间的软件如果对内存进行非法访问,不会造成Linux系统的奔溃,但是进程将会被系统干掉。
- 用户空间软件类似网络编程的客户端,它总是向Linux内核空间的软件发起一个服务请求,然后Linux内核空间根据请求完成需求,并且最终的结果返回给用户空间的软件。
Linux内核空间特点
- 内核空间包含的软件就是uImage。
- 包含七大子系统的内容,
- 设备驱动程序(.ko)属于内核空间软件。
- 内核空间的软件在运行的适合,CPU核的工作模式为SVC管理模式,内核空间的软件权限最高。
- 内核空间的软件同样不允许直接访问外设的物理地址,要想访问外设的物理地址必须将物理地址映射到内核虚拟地址上,0XC000000~0XFFFFFFF。
- 内核空间的软件如果对内存进行非法的访问,直接会导致Linux系统奔溃,类似Windows蓝屏。
Linux内核驱动开发基础:
int module_init(void); void module_exit(void); MODULE_LICENSE("GPL");
- 内核程序使用的头文件位于Linux内核源码中。
- 内核程序的入口函数要用module_init宏来修饰,并且入口函数的返回数据必须是int型。
- 入口函数的形参列表为void。
- 入口函数执行成功返回0,失败返回负值。
- 当执行insmod安装内核程序到内核uImage中,或者采用编译在一起的方式(选择),当内核uImage在启动的时候,这两种情况内核uImage会自动调用入口函数。
- 切记:入口函数执行成功,这个内核程序的生命周期才刚刚开始,并且内核程序会存储在内存中等待应用程序的访问(通过系统调用来间接访问)。
- 内核程序的出口函数用module_exit修饰,出口函数的返回值和形参都是void,当执行rmmod从内核uImage中卸载内核程序或者采用不选择的方式编译uImage后。这两种情况内核uImage会自动调用出口函数。
- 切记:一旦出口函数被调用,这个内核程序将会从内存中彻底清除,不能再为应用程序服务。
- 任何内核程序,只要是一个.c文件,必须添加MODULE_LICENSE(“GPL”),否则内核会出错并且部分内核函数及变量无法使用。
- 内核打印函数printk,只要是内核程序调用的函数,不是自己编写的就是内核源码提供的,一律不允许调用标准C库的函数。
Linux内核程序的编译
- 内核程序的编译一定要关联内核源码(kernel),头文件和调用的函数都是位于内核源码中。
- 回顾内核程序的编译步骤:
- 拷贝内核程序到内核源码的某个目录下。
- 修改Kconfig添加配置菜单。
- 修改Makefile添加编译支持。
- 对内核源码使用make menuconfig选择添加模块。
- make uImage; make modules;
- 总结:这种内核程序的编译过程极其繁琐,简化只需要一个Makefile即可解决。
编写解决问题的Makefile
cd /opt/drivers/day01/ vim Makefile #将helloworld.o和uImage分开编译,单独编译 obj-m += helloworld.o #all:伪目标,对应的命令当前执行make或者make all时执行。 #-C /opt/kernel :进入/opt/kernel目录下。 #SUBDIRS:子目录。 all: make -C /opt/kernel SUBDIRS=$(PWD) modules #当执行make clean时进行清除操作 clean: make -C /opt/kernel SUBDIRS=$(PWD) clean //将内核程序目标文件拷贝到根文件系统进行安装和卸载。 mkdir /opt/rootfs/home/drivers cp helloworld.ko /opt/rootfs #下位机测试: #重启下位机,进入uboot命令执行: setenv bootargs root=/dev/nfs nfsroot=192.168.1.8:/opt/rootfs ip=192.168.1.110:192.168.1.8:192.168.1.1:255.255.255.0 init=/linxurc console=ttySAC0,115200 maxcpus=1 saveenv tftp 0x48000000 uImage bootm 0x48000000 #下位机Linux系统启动后执行: cd /home/drivers ls helloworld.ko #将内核程序安装到uImage中 insmod helloworld.ko lsmod rmmod helloworld #从内核中卸载内核程序。 lsmod