原标题:从新手村开始,手把手带你入门梳理内核代码

在上一期内容中,Java离Linux内核有多远? 我们介绍了从 JVM 到内核的编译原理,告诉大家应用和系统工程师如何接触到内核。

本文将 从一个简单的底层硬件模块入手,一步步教大家如何梳理内核代码。适合精力集中在内核,不太需要关心用户空间的工程师,比如驱动工程师、嵌入式工程师等,以及想往这方面学习发展的朋友。

1

初探内核

版本信息与往期一致:

在往期的访谈 中,我们讨论过如何阅读内核代码,在这里按照之前讨论的思路详细扩展下。

在 drivers/input/keyboard 下面的文件是键盘驱动,我们选择 lm8333.c 吧(没什么特殊理由,其他的也可以)。

找到 module_init,xxx_init,module_xxx,这些就是模块(驱动也是一种模块)的入口(进阶点 1,系统的启动过程),lm8333.c 内对应的是 module_i2c_driver(lm8333_driver),注册 driver。

lm8333_driver 定义如下:

staticstructi2c_driverlm8333_driver = {

.driver = {

.name = "lm8333",

},

.probe = lm8333_probe,

.remove = lm8333_remove,

.id_table = lm8333_id,

};

驱动和设备匹配后,会回调 probe(进阶点 2,Linux Device Driver,LDD),也就是 lm8333_probe,它的关键代码如下:

staticint lm8333_probe( structi2c_client*client, conststructi2c_device_id*id) //1

{

conststructlm8333_platform_data*pdata = dev_get_platdata(&client->dev);

structlm8333*lm8333;

structinput_dev*input;

lm8333 = kzalloc(sizeof(*lm8333), GFP_KERNEL); //7

input = input_allocate_device; //8

lm8333->client = client; //10

lm8333->input = input; //11

input->name = client->name; //13

input->dev.parent = &client->dev; //14

input->id.bustype = BUS_I2C; //15

input_set_capability(input, EV_MSC, MSC_SCAN); //16

err = matrix_keypad_build_keymap(pdata->matrix_data, …, input); //18

if(pdata->debounce_time) {

err = lm8333_write8(lm8333, LM8333_DEBOUNCE,

pdata->debounce_time / 3); //22

}

if(pdata->active_time) {

err = lm8333_write8(lm8333, LM8333_ACTIVE,

pdata->active_time / 3); //27

}

err = request_threaded_irq(client->irq, NULL, lm8333_irq_thread,

IRQF_TRIGGER_FALLING | IRQF_ONESHOT,

"lm8333", lm8333); //32

err = input_register_device(input); //34

i2c_set_clientdata(client, lm8333); //36

return0;

}

probe 的任务是驱动的初始化和设置,初学阶段,并不需要每一行代码都深入学习,可以先尝试将代码分类,以 lm8333_probe 为例。

第 1 行,函数的参数类型是固定的,背后是 LDD。

第 7 行,申请内存,背后是内存管理。暂且把它当成c语言的 malloc 也无妨。

第 8/13~16/18/34,input 相关,背后是 input 子系统。

第 22/27 行,写寄存器,背后是 i2c 总线。

第 32 行,request_threaded_irq,背后是中断处理。

这些背后的机制每一个都是一个进阶点。

初始化完毕,中断产生后,会调用 request_threaded_irq 时传递的 lm8333_irq_thread,继续梳理它的逻辑。

staticirqreturn_t lm8333_irq_thread( intirq, void*data)

{

structlm8333* lm8333= data;

u8 status = lm8333_read8(lm8333, LM8333_READ_INT);

if(!status)

returnIRQ_NONE;

if(status & LM8333_ERROR_IRQ) {

//省略

}

if(status & LM8333_KEYPAD_IRQ)

lm8333_key_handler(lm8333);

returnIRQ_HANDLED;

}

可以看到 lm8333_irq_thread 先读寄存器来判断产生中断的原因,是 ERROR 还是 KEYPAD,如果是后者,调用 lm8333_key_handler。

staticvoid lm8333_key_handler(struct lm8333 *lm8333)

{

structinput_dev *input = lm8333->input;

u8keys[LM8333_FIFO_TRANSFER_SIZE];

u8code, pressed;

inti, ret;

ret= lm8333_read_block(lm8333, LM8333_FIFO_READ,

LM8333_FIFO_TRANSFER_SIZE,keys);

for(i = 0; i < LM8333_FIFO_TRANSFER_SIZE && keys[i]; i++) {

pressed= keys[i] & 0x80;

code= keys[i] & 0x7f;

input_event(input,EV_MSC, MSC_SCAN, code);

input_report_key(input,lm8333->keycodes[code], pressed);

}

input_sync(input);

}

lm8333_key_handler 读寄存器,然后根据寄存器的值判断实际的按键,调用 input_report_key 报告数据。

好了,lm8333.c 的逻辑我们清楚了:初始化、设置中断、读取数据并 report。

我们从 lm8333 的硬件角度看看,它是一个比较简单的芯片,datasheet也并不复杂,摘取其中一段。

n ACCESS.bus (I2C-compatible) communication interface to the host

n Four general purpose host programmable I/O pins with two optional (slow) external Interrupts

n 16 byte FIFO buffer to store key pressed and key released events

n Host programmable active time and debounce time

兼容 i2c 总线,支持中断,16 字节的 buffer,主机可编程有效时间和去抖时间。

再看看寄存器(这个文档称之为 command)表

再看看代码里出现的 i2c 读写的地址 LM8333_DEBOUNCE(0x22)和 LM8333_FIFO_READ(0x20)这些,这个表就是依据。

驱动做的事情可以分为两个方面,一方面是处理芯片本身的逻辑,比如中断、i2c、寄存器和时序等;另一方面是系统方面的,驱动和设备匹配、中断处理、数据传递(报告)等。

lm8333 比较简单,但复杂的芯片多如牛毛,所以驱动工程师也可以分为两类,一类比较专注于芯片本身的逻辑,另一类游到内核的大海中去了。

复杂的芯片本身就是一个完整的系统,成千上万的寄存器,错综复杂的模块,能将这些弄清楚也是有很大挑战的。

除此之外,复杂的芯片很多都有配套的软件架构,比如 ISP(Camera)相关的 V4L2(Video For Linux 2),GPU 相关的 DRM(Direct Rendering Manager)。

很明显,芯片本身的逻辑并不是本文的重点,我们更关心如何到内核。

2

进入第二个阶段,最好先从与日常工作关系最密切的模块入手。比如 lm8333,连接在 i2c 总线上,获取数据后通过 input 子系统 report,就可以从 i2c 和 input 入手。

学习 i2c 的过程中,还要解决 i2c 总线和 lm8333 的关系,这就涉及到 LDD。

深入 input 子系统的过程中,如果你对用户空间得到数据的过程感兴趣,就涉及到文件系统、poll/epoll 等。

当然了,在这个阶段,最好还是把文件系统这些复杂的模块当作黑盒。小碎步前进,不断有收获。

稍微复杂些的驱动可能还会有电源管理、工作队列和等待队列等机制,也可以在这个阶段内梳理它们的原理,至于它们背后的进程管理这些也可以先放放。

有了这一身装备,应付副本里的小 BOSS 也绰绰有余了,相比新手村那会也更有成就感,可以仗剑天涯了。

3

第三个阶段就是解决之前遗留的疑问了,将内存管理、文件系统和进程管理等一一拿下,比如 lm8333_probe 调用的 kzalloc、input 子系统涉及的 sysfs 文件系统、工作队列和中断处理相关的进程调度,一步步深入挖掘。

在之前的问答活动里我曾说过,“我已经把自己看过的代码的截图放在随书资料中了,算是一小段捷径吧。这些截图里面,某函数、它调用的函数等函数调用关系使用红线标示(如下图),内容包括内存管理、文件系统和进程管理三大模块。”

PS:这些截图是随书资料,但并不是光盘那种。想要获取资料的朋友欢迎在“ 开源中国”公众号对话框回复关键词: 0811,资料会自动发给大家。

作者介绍:

姜亚华(@二如公子 ),《精通 Linux 内核——智能设备开发核心技术》的作者,一直从事与 Linux 内核和 Linux 编程相关的工作,研究内核代码十多年,对多数模块的细节如数家珍。曾负责华为手机 Touch、Sensor 的驱动和软件优化(包括 Mate、荣耀等系列),以及 Intel 安卓平台 Camera 和 Sensor 的驱动开发(包括 Baytrail、Cherrytrail、Cherrytrail CR、Sofia 等)。现负责 DMA、Interrupt、Semaphore 等模块的优化与验证(包括 Vega、Navi 系列和多款 APU 产品)返回搜狐,查看更多

责任编辑:

linux 中国-新手村,从新手村开始,手把手带你入门梳理内核代码相关推荐

  1. 手把手带你入门深度学习(一):保姆级Anaconda和PyTorch环境配置指南

    手把手带你入门深度学习(一):保姆级Anaconda和PyTorch环境配置指南 一. 前言和准备工作 1.1 python.anaconda和pytorch的关系 二. Anconda安装 2.1 ...

  2. RPA之家手把手带你入门Blue Prism教程系列4_认识Blue Prism的界面

    RPA之家手把手带你入门Blue Prism 1. Home & Analytics 2. Studio 2.1 Process 2.2 Object 2.3 Process与Object的关 ...

  3. RPA之家手把手带你入门Blue Prism教程系列7_深入了解Data Item

    RPA之家手把手带你入门Blue Prism 1. Data Item类型 2. Data Item的表现形式 2.1 Environment Variable(环境变量) 2.2 Session V ...

  4. RPA之家手把手带你入门Blue Prism教程系列 -汇总

    RPA之家手把手带你入门Blue Prism 基础篇 -本文章由RPA之家(rpazj.com)提供, 学习交流群QQ群465620839 微信交流群: 基础篇 RPA之家手把手带你入门Blue Pr ...

  5. RPA之家手把手带你入门Blue Prism教程系列3_如何新建用户和配置数据库

    RPA之家手把手带你入门Blue Prism 创建用户 第一步:寻找Security标签下的Users 第二步:配置Users 配置数据库 第一步:新建数据库 第二步:配置数据库 -本文章由RPA之家 ...

  6. RPA之家手把手带你入门Blue Prism教程系列1_如何申请Blue Prism免费试用版

    RPA之家手把手带你入门Blue Prism Blue Prism 免费试用版 第一步:申请一个BluePrism Portal账号 第二步:在DX网站申请一个测试license 第三步:申请成功后, ...

  7. RPA之家手把手带你入门Blue Prism教程系列2_如何安装并且配置Blue Prism免费试用版

    RPA之家手把手带你入门Blue Prism 安装Blue Prism 第一步:下载Licence和试用版的Blue Prism 第二步:安装Blue Prism 第三步:登录并激活Blue Pris ...

  8. RPA之家手把手带你入门Blue Prism教程系列5_认识Process Studio Object Studio

    RPA之家手把手带你入门Blue Prism Process Studio Object Studio -本文章由RPA之家(rpazj.com)提供, 学习交流群QQ群465620839 微信交流群 ...

  9. RPA之家手把手带你入门Blue Prism教程系列6_运行Process并认识Data Item

    RPA之家手把手带你入门Blue Prism 练习一: 打开并运行一段Process Data Item -本文章由RPA之家(rpazj.com)提供, 学习交流群QQ群465620839 微信交流 ...

最新文章

  1. Shiro 那点事儿
  2. Android --- RecyclerViwe中使用SnapHelper报错:“An instance of OnFlingListener already set.”
  3. IntelliJ IDEA for Mac在MacOS模式下的替换快捷键(Replace Shortcut)
  4. win10必须禁用的服务_7寸屏的迷你电脑,就算是8GB运行内存,也必须关闭的系统选项...
  5. droidbox官网
  6. ffmpeg转mp4格式
  7. tinymce移动端使用_关于在移动端避免使用100vh的原因及解决方案
  8. ~~求欧拉函数(附模板题)
  9. StackedGAN详解与实现(采用tensorflow2.x实现)
  10. 12.统计 日志 ip
  11. 初识Git 如何使用Git将本地项目上传到Github
  12. 关于汽油动力汽车和混合动力汽车的环保问题。
  13. 如何查看别人的微信公众号的粉丝数
  14. **传统线上支付 区块链**
  15. 35、矩阵(稀疏矩阵)的压缩存储(一)
  16. Python 爬虫 中文乱码一文通
  17. AWS、Google、Apple云端宕机背后的故事
  18. vs2015+qt5生成ts文件与多语言
  19. (c语言)哈利·波特的考试 (25分)
  20. 豆瓣FM snap应用

热门文章

  1. java编程 队列_5.1、顺序队列(java实现)
  2. 高德地图我的队伍查岗_详细测试高德地图的家人地图后 我学会了画地为牢
  3. 2017.9.21 所驼门王的宝藏 思考记录
  4. 【英语学习】【WOTD】scrumptious 释义/词源/示例
  5. Win知识 - 程序是怎样跑起来的——系统调用和高级编程语言的移植性
  6. 一致性hash算法虚拟节点_一致性Hash算法原理详解
  7. linux 添加路由_在 Linux 上使用开源软件创建 SDN | Linux 中国
  8. load average多少是正常_对 cpu 与 load 的理解及线上问题处理思路解读
  9. 用php写的亲亲鲜花网站_php54鲜花销售网站
  10. 安装了email模块还是报错_官网的Pyngl和Pynio安装方法会报错!正确的在这里!