你们好。
做Linux驱动工程师也有一段时间了,明天分享一下我以前入职才晓得的一些事情,算是一个新手的经历吧。
设备树
先前学习Linux驱动,是从最简单的一个.c文件开始。
在.c中实现module_init和module_exit这两个函数,之后在module_init的函数里加个printk,输出个helloworld。
把.c编译成.ko,之后insmod加载驱动,见到有复印,即使成功了。
到了后来,我接触到设备树,加载dtb和.ko驱动,见到有复印,又成功了。心想,设备树应当就是这样吧。
直至有三天,工作中遇见一个uboot没复印问题,主管问我:你设备树编进uboot了吗?
我懵了:把...把Linux的设备树编进uboot里?这能用?
主管:不是啊,uboot自己的设备树啊。
以前我一度以为设备树是Linux的东西,别的程序没有,另外当时对uboot了解很浅,只简单用过几个命令。
直至这天,我才明白,所有的程序都可以有自己的设备树,不仅仅是uboot和linux,只要它实现了设备树这一套机制就可以。包括Linux的那一套Kconfig机制,在其他的一些开源项目中也在广泛应用。
工具链
编译一个程序在开发板上跑linux 输入法,一般我们都是用厂商提供交叉编译工具链。但我们自己换一个工具链,可能编译出的程序在开发板上就跑不了。
因为工作须要linux内核 驱动,有段时间我须要自己弄一个工具链,以前就碰到了各类错误:xxxcommandnotfound、GLIBC2.34VESIONnotfound等等。
另外我们平常用的像arm-linux-gcc这些工具链名称都是缩写,不仅构架和运行平台,看不出其他的区别。
虽然不同的工具链不仅构架和运行平台上的区别,主要还有C库、gcc版本的区别。
交叉工具链在制做的时侯,可以选择具体的C标准库,glibc、uclibc或则musl等等,假若这个工具链的C标准库是glibc的,这么用这个工具链编译下来的程序,就不能在uclibc或则musl那些非glibc库的文件系统下运行,否则都会报commandnotfound错误,明明有这个可执行文件,却说找不到,让你百思不得其解。
交叉工具链所使用的gcc版本影响也很大,由于不同的gcc版本所对应的C库版本不一样。诸如linux环境配置,用gcc10的工具链做的glibc文件系统,上面C库版本只支持到2.30,这时用一个gcc12的工具链编译一个程序在该文件系统上运行,还会提示glibc版本找不到的错误。
文件系统
如同前面说的,工具链和文件系统的关系是很大的。
其实是做驱动开发,并且文件系统的一些东西也是要晓得的。
当初刚入职的时侯,要把一个.ko驱动在系统启动完成前就加载,这时才晓得原先可以把命令放在/etc/init.d/rcS里
作为驱动工程师,可以不用把文件系统了解的太深,但至少要晓得inittab、rcS、passwd和shadow这几个文件的作用,还有就是上面说的C库。
rcS是文件系统启动时要执行的一些命令,inittab、passwd和shadow主要是更改系统登陆时的用户名和密码,包括设置免密登陆等等。
一般把文件系统提供给顾客前,就会把用户名改为root,密码改为自己公司的名子,这时都会用到这几个文件。
驱动如何编进内核
一开始,我晓得Linux内核源码中每位目录下都有一个Makefile,我以为改个Makefile就行了。
但一运行,驱动没生效。后来才晓得,原先还要更改Kconfig。
把驱动编进内核,虽然正确的做法应当是通过menconfig菜单才能配置这个驱动是否编译,即更改Kconfig。
Makefile和Kconfig都更改了,驱动还是没生效?
这时就要看.o文件是否编译下来了,若果编译下来了。进一步看这个驱动的初始化级别,看是module_init、arch_initcall还是其他的级别。
之后加复印,把内核initcall的等级复印下来,看内核是否早已跑了该等级的初始化。假如早已挪到了,再把该等级的initcall执行函数的地址复印下来,之后反汇编vmlinux,看是否早已执行了新加驱动的probe函数。
基本上,通过以上过程的剖析,就能否定位到为题所在。
自动更改defconfig配置引起的问题
编译内核时,一般还会用厂商提供的一个默认配置文件,比如makexxx_defconfig。
但若果我们想这个配置文件中加一个自己的宏,比如CONFIG_XXX=y,之后在代码中判定#ifdefCONFIG_XXX,你会发觉并没有生效,但是原先写的CONFIG_XXX=y也没了。
这是好多菜鸟改defconfig就会碰到的问题,虽然是没有看懂怎样正确更改defconfig文件。
在defconfig中定义了CONFIG_XXX=y后,还要在Kconfig文件中添加一个configXXX的配置才能生效。
另外,怎么某个配置选项存在依赖关系,但依赖的配置选项没打开,也会出现这些不生效的情况。
所以还是建议通过menconfig菜单进行配置,除非真的弄清楚了这种关系能够去自动更改defconfig。
源码阅读/跟踪问题
尽管有一些比较有用的内核调试方法linux内核 驱动,但真正常用的,还是加复印跟踪,其它调试方法归根究竟还是辅助性的,好多情况下还是靠加复印剖析问题。
在跟踪源码,解决一些问题的时侯,也要注意一些方法。
以前在解决一个内核自解压的问题时,加复印跟了好久,甚至跟踪到解压缩算法,但其虚像这些算法性、协议性的东西,尽量少去怀疑它的错误,就是不用花太多时间去跟踪那些合同算法是如何实现的,而是跟踪一些函数传参过程,关注是否正确使用,是否正确传参问题。由于最终解决问题可能只是一个很简单的操作。
须要学会多少个驱动才行?
中级的驱动工程师,只须要会一些简单的驱动,比如一些简单的时钟、定时器、led这种驱动。
但若果往上进阶,就须要会更多的驱动,比如USB、网卡这种复杂的,这种驱动的前提是你得懂时钟、复位、dma等这种驱动,由于会用上这种驱动的插口,所以要求会初一点。
当你还能把握一个比较复杂的驱动,这其中自然都会涉及到其他的一些基础驱动,自然会的更多,这常常对应的是中级工程师。
所以,会多少驱动,这显然是一个进阶的过程。没有说一定要会多少个,但随着工作经验的下降,自身所把握的驱动也会越来越多,承当的责任也越大。
工作职责问题
如上面所说,其实叫Linux驱动工程师,但工作绝不仅仅是写Linux的驱动。
确切的说,应当叫底层开发工程师。由于不仅Linux驱动,uboot、文件系统、系统移植都是要弄的。
我因为部门的特殊性,还须要做一些芯片流片前的验证,就常常须要看一些汇编代码,反汇编程序,通过仿真下来波形进行剖析。流片回去后,还须要bringup。
所以,只要是底层相关的工作,多少还会涉及的,只不过可能有些企业会分得比较细,把系统层和驱动层分开。
假如想厘清楚Linux内核驱动中的奥秘
欢迎你们学习下边