前言
内核开发在很大程度上并不是重新造“轮子”的过程,而是深入理解 并尽量复用现有的内核设计框架 ,然后参照相似的功能模块去添加或改写某项需要的功能。在对内核整体框架以及某些子系统融会贯通以后,我们才有可能站在巨人的肩膀上去改进框架本身,实现自主创新。如果过分强调不必要的“自主创新”,可能会让内核的可维护性变差,最终结果反而得不偿失。
一、设备驱动切入点
1.1 设备驱动入口
阅读内核驱动代码首要就是找到其闪闪发光的入口函数,以相对独立的内核模块为例,在绝大多数情况下,如果模块的名字是 modname,那么模块的入口函数就是modname_init()。模块的入口函数一般会使用module_init(modname_init)进行声明。
#define module_init(x) __initcall(x) //include/linux/init.h
1.2 子系统入口
内核选项的解析完成之后,各个子系统的初始化即进入第二部分------入口函数的调用。通常USB、PCI这样的子系统都会有一个名为subsys_initcall的入口,如果选择它们作为研究内核的切入点,那么就请首先找到它。
1.3 内核本身的入口
初识入口(第一入口)是体系结构相关的,在汇编代码里;
而通用入口(第二入口)是体系结构无关的,在C语言里实际上就是init/main.c里面的start_kernel(), 如下图代码片所示,do_initcalls()就是我们需要寻找的函数了,在这个函数中执行所有使用module_init()声明的函数。
start_kernel
->reset_init();
->kernel_thread(kernel_init, NULL, CLONE_FS);
->kernel_init()
->kernel_init_freeable();
->do_basic_setup();
->do_initcalls();
二、芯片的基本功能模块
在驱动开发过程中,我们最长接触的就是寄存器
。内核与硬件联系紧密,但它本身属于软件,可以说是与硬件联系最紧密的软件。作为软件与硬件沟通,很多情况都是通过读写读写寄存器
实现的。
寄存器
按照功能不同一般可以分为三类:
控制寄存器
:可写,用来控制芯片的行为,工作方式等;状态寄存器
:可读,用来反映芯片当前的统计、异常状态;数据寄存器
:可读可写。读取或修改芯片产生的数据;
每个芯片的数据手册(datasheet)中都有完整的寄存器列表和寄存器数据字节位详细描述,这是人们开发驱动过程中需要关心的重点。
2.1 复位
复位(reset
)是芯片的一项重要功能,在芯片的初始化或从异常状态恢复都需要,在此过程中硬件芯片可以加在古剑、清除状态。它并不是必不可少,但是有了它可以给芯片提供一个额外的保障,当芯片处于异常状态时,可以通过它将芯片的状态复原。
从驱动角度看,支持硬件复位芯片一般都会指定某一个引脚的高低电平变化触发复位,此时我们编写该芯片的复位功能时,只需要将GPIO设置为输出模式并连接到芯片的复位引脚上,然后由GPIO控制电平变化,来实现硬件复位。
除了硬件复位外,有些芯片还支持软件复位(soft reset
),通过写芯片的某些寄存器来触发复位操作。
2.2 中断
中断的意思就是CPU正在运行其他任务或者功能时,芯片突然发出一个通知信号,通知CPU打断当前正在执行的任务,然后执行这个通知信号预先设定好的处理代码,等CPU将这段预先设定的处理代码执行完毕后,CPU再跳回被打断之前正在执行的代码 或任务。
举个现实生活中的例子,我们正在使用手机听音乐,突然来了一个推销电话,这个时候手机将暂停播放音乐而开始播放手机铃声提醒我们接听电话,等我们接通电话发现是一个推销电话后,我们挂了电话,手机的音乐播放器又开始播放之前暂停的音乐。这个过程就类似CPU的中断的过程。
我们可以通过读取/proc/interrupts 获取系统的中断状态,如下图所示:
芯片的中断注册成功之后就会出现在上表中。表中的前几列数字分别表示该中断再某个CPU
上发生的次数,借此调试驱动,可以帮助我们在不添加log
重新编译的情况下,初步判定问题所在。比如用户空间没有收到芯片产生的数据,可以先读取该文件确定芯片产生的中断次数是否与预期次数相等,如果不相等,检查中断和设置是否正确或者电压是否正常。如果相等,说明中断工作正常,有必要检查中断处理是否正常。
除此之外,读取/proc/stat
文件可以得到中断状态,以intr
开始的一行按照顺序列举了系统中中断发生的次数。这些数字是中端发生的总次数,部分CPU
,虽然省去了 加法运算,但不够直观。
intr
:系统启动以来的 所有interrupts的次数情况ctxt
:系统上下文切换次数bttime
:系统启动时间戳,格林威治时间戳。processes
:系统启动后所创建过的进程数量。如果短时间内 该值特别大,系统可能出现异常。procs_running
:处于Runnable
状态的进程个数。procs_blocked
:处于等待I/O
完成的进程个数