【驱动】01.相关基础
【嵌入式八股】一、语言篇https://www.nowcoder.com/creation/manager/columnDetail/mwQPeM
【嵌入式八股】二、计算机基础篇https://www.nowcoder.com/creation/manager/columnDetail/Mg5Lym
【嵌入式八股】三、硬件篇https://www.nowcoder.com/creation/manager/columnDetail/MRVDlM
【嵌入式八股】四、嵌入式Linux篇(本专栏)https://www.nowcoder.com/creation/manager/columnDetail/MQ2bb0
Linux内核驱动
要从各个系统的总体框架,硬件原理,驱动层,应用层来入手学习
linux内核和驱动编程的几个面试提问,你能回答几个?_哔哩哔哩_bilibili
linux内核和驱动编程的几个提问---部分答案_哔哩哔哩_bilibili
整体
01.某个Linux驱动开发的流程?
- 了解硬件:首先,你需要详细了解你要开发驱动程序的硬件设备,包括其规格、接口、工作原理等信息。
- 安装开发环境:在Linux系统上安装开发驱动所需的工具和编译器,通常使用GCC工具链。
- 查找现有驱动:在Linux内核中查找是否已经存在类似的驱动程序,如果有,可以借鉴或修改现有代码。
- 编写驱动程序:根据硬件的规格和Linux内核的驱动模型,编写新的驱动程序。这包括初始化、数据传输、中断处理等功能。
- 编译驱动:使用编译器将驱动程序源代码编译成可加载的内核模块。
- 加载驱动:使用
insmod
或modprobe
命令将驱动模块加载到Linux内核中。 - 测试驱动:编写测试应用程序或使用现有工具来测试驱动程序的功能和性能。
- 调试驱动:如果出现问题,使用调试工具和技术来诊断和修复驱动程序中的错误。
- 性能优化:优化驱动程序的性能,以确保其在各种工作负载下表现良好。
- 文档编写:编写文档,记录驱动程序的使用说明和相关信息,以供其他开发人员参考。
- 发布驱动:如果你的驱动程序是开源的,可以将其发布到适当的开源社区或仓库中,以供其他人使用和改进。
- 维护驱动:随着Linux内核的更新和硬件的变化,需要定期维护和更新驱动程序。
02.嵌入式常用调试方法
-
打印(确定问题位置、可以添加打印输出函数名,行号),或者用日志的方式
-
打开调试开关echo 10 > /proc/sys/kernel/printk
-
打印堆栈信息,输出函数调用关系
-
读写寄存器列表,写个dump_regs函数
-
寄存器查验,在应用层生成文件(device sys class create),可以在/sys/kernel/debug查看
-
GDB调试、JTAG实时运行、qemu模拟运行调试、
-
逻辑分析仪、示波器、万用表
三打印调堆,两寄列查,GJQ示逻
03.嵌入式常见优化方法
-
启动优化,裁剪掉没必要的
-
数据拷贝优化(使用快速拷贝算法/使用指针(比如网络框架中,增加各种协议包头,就没有频繁拷贝,而是采用修改指针的办法,还有就是内核同步机制中的读拷贝更新RCU机制,也是这样的,因为是读优先,写的时候会写在另一片内存区域,读不忙的时候直接更改指针指向就可以了))DMA方法,解放CPU。
-
内存使用优化:帧缓冲区(参考v4l2的代码,采用了状态机的办法,在不同状态下,这片内存区域由谁使用和管理,是应用层、内核层还是硬件使用)。环形缓冲区 生产者消费者问题。
-
优化进程启动速度,减少加载的动态库的数量,优化加载动态库时的搜索路径,进程改为线程
-
优化代码:if表达式,从左到右对表达式求值。
-
内存操作的优化:尽量使用占用内存少的算法,利用流水线内存存取与计算并行的特点,组合内存访问与计算
-
调整进程的优先级
启拷使动码
内核模块
04.Linux中引入模块机制有什么好处?
Linux是宏内核操作系统的典型代表,所有内核功能都整体编译到一起,优点是效率高,缺点修改一下很麻烦。所以引入内核模块。
- 灵活,缩短模块的开发周期,因为模块的安装和卸载都很方便。编译成模块.ko,放在应用层,用的时候插入内核,不要的时候卸载。更新不用重新修改编译内核源码。
- 节省空间,通用的功能放进内核,多出来的不同版本的特定功能就可以放进内核模块,不用的可以不编译。(也可以用设备树)
- 安全性好,模块有问题不会影响内核其他部分。
- 应用程序在退出时,可以不管资源的释放或者其他的清除工作,而把这些任务交给模块退出函数(exit)。
05.内核模块三要素
module_init(led_init); //模块加载入口声明(入口)
module_exit(led_exit); //模块卸载入口声明(出口)
MODULE_LICENSE("GPL"); //模块免费开源声明(许可证)
06.module_init函数底层实现:orange:
module_init 其实是一个宏,它的作用是:告诉内核,该驱动程序的入口函数地址
实际上驱动的加载分为两种:静态加载、动态加载
静态加载就是把驱动程序直接编译到内核里,系统启动后可以直接调用。静态加载的缺点是调试起来比较麻烦,每次修改一个地方都要重新编译下载内核,效率较低。
动态加载利用了Linux的module特性,可以在系统启动后用insmod命令把驱动程序(.ko文件)加载上去,在不需要的时候用rmmod命令来卸载。
07.驱动程序入口函数的_ init、_ exit起什么作用?
定义为某种段属性,比如"_ section(.init.text)"、 "_section(.exit.text)"
入口函数只用一次,用完后可以释放它所占据的空间
驱动被编进内核的话,出口函数就永远不会被调用
所以入口、出口函数可以放入某个段,以后可以释放些个段
08.编译的两种方法:heart:
- 编译成模块
固定的makefile,只需修改Linux源码路径,交叉编译路径
- 编译进内核
放到内核的驱动目录下、写个Kconfig、再make menuconfig中对应选上生成.config配置文件(make的时候就会根据.config编译进去了)make之前写个makefile,并修改上一级makefile。然后make,生成.bin文件,做成镜像。
09.常用的驱动模块开发指令
make
:使用 Makefile 编译模块。如果 Makefile 配置正确,该命令将生成模块的 .ko 文件。make clean
:清除模块的目标文件、中间文件和其他临时文件。insmod
:将模块插入内核。使用此命令需要超级用户权限。rmmod
:从内核中移除模块。使用此命令需要超级用户权限。lsmod
:列出当前加载的所有模块。modprobe
:自动加载模块及其依赖项,并将其添加到内核模块列表中。modinfo
:显示有关模块的信息,例如版本号、作者、描述等。dmesg
:显示内核消息缓冲区的内容,可用于查找与模块相关的错误或调试信息。
10.insmod,rmmod一个驱动模块,会执行模块中的哪个函数?在设计上要注意哪些问题?:heart:
当使用 insmod
命令将一个驱动模块插入内核时,模块中的 _init
函数将会被调用。这个函数用于初始化模块并向内核注册模块所提供的设备驱动程序。
当使用 rmmod
命令从内核中移除一个驱动模块时,模块中的 _exit
函数将会被调用。这个函数用于清理和卸载模块。
要注意的就是,尽量使在 init函数中出现的资源申请及使用,都要有对应的释放操作在exit中,即init申请,eixt释放。
11.驱动卸载异常可能是由什么原因引起的?
- 驱动程序中存在缺陷,导致无法正确地卸载。
- 与驱动程序相关的其他进程或服务正在运行,阻止了驱动程序的卸载。
- 驱动程序未正确注册或未正确初始化,导致卸载时出现错误。
- 与驱动程序相关的硬件设备或其他软件组件出现故障或异常,导致无法正确卸载。
- 驱动程序未正确处理信号或中断,导致出现异常。
- 系统内存或其他资源不足,导致卸载时出现错误。
12.Linux系统下.o .ko是什么文件?.a .so是什么文件?
.o 是目标文件
.o是Object File的缩写,是编译器生成的中间文件,包含目标文件的代码和数据信息,但未被链接成可执行文件。.o文件包含二进制代码、数据和符号表等信息,用于生成可执行文件或链接成静态库。
.ko是可动态安装卸载的驱动文件
.ko是Kernel Object的缩写,是Linux内核模块的二进制文件格式,用于向内核添加或卸载功能驱动。.ko文件是一种动态加载的内核模块,可以在系统运行时加载和卸载,以扩展或修改内核功能。
.a 是静态库
.a是Archive的缩写,是静态库文件,也称为静态链接库。.a文件包含多个目标文件的代码和数据,以及符号表等信息,通过静态链接可以将库文件中的目标文件和应用程序一起链接成一个可执行文件。
.so是动态链接库,类似于Windows的.dll
.so是Shared Object的缩写,是动态链接库文件,也称为共享库。.so文件包含共享代码和数据,可以被多个进程共享。在程序运行时,操作系统会将共享库映射到内存中,以供进程调用。
字符设备
设备驱动
13.设备驱动的分类
字符设备有哪些?和块设备有什么区别?
(按共性分类方
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
查阅整理上千份嵌入式面经,将相关资料汇集于此,主要包括: 0.简历面试 1.语言篇 2.计算机基础 3.硬件篇 4.嵌入式Linux【本专栏】 (建议PC端查看)