Zhen Y., Zhang W., Dai Z., Mao J., Chen Y. Is It Possible to Automatically Port Kernel Modules? In 2018 9th Asia-Pacific Workshop on Systems (APSys 2018), ACM SIGOPS.

希望本文能作为额外的资源, 来帮助潜在读者理解这篇论文. 同时也作为我个人的一个备忘吧.

问题背景

内核模块和内核接口

内核模块是一种可以在运行时动态接入的内核组成部分, 它们运行在内核态. 内核模块主要的例子是驱动设备, 当然也有如 ZFS 这样的.

内核模块为了成为内核的一部分, 必须使用内核提供的接口. 而且它们使用的是内核内部接口, 和用户程序使用的系统调用所区别. 接口如函数, struct, 宏等等. 这些符号在内核里面通常使用 EXPORT_SYMBOL 定义, 在运行时可以在 /proc/kallsyms 中看到.

内核接口的改变

但是, 内核提供给内核模块的接口, 是不稳定的, 不向后兼容的. 这有好处就是, Linux 内核没有那么大的历史包袱. 但坏处就是, 一个内核模块通常只能在某些版本的内核上运行, 为了在其他版本上运行 (比如最新版本), 就需要做移植.

通常, 内核接口的改变都不涉及语义, 比如

已有方法

通常现在还是内核模块开发者 (他们不是内核开发者, 他们通常是驱动开发者) 手动升级. 方法也很粗暴, 先编译, 然后把编译错误警告看懂解决, 再编译直到没有错误和警告. 显然这不是个好办法.

另外也有自动工具辅助, 虽然已有的工具都不实用. 一类工具就是模仿这种 “面向 error/warning” 编程.

还有一类工具, 是从内核仓库 中的内核模块, 称为树内模块 (in-tree kernel modules) 的改动中, 学习移植都需要改什么地方. 现在内核代码有超过 70% 是内核模块包括驱动, 所以数据是够多的. 这类工具利用一个很重要的概念 “改变模式”, 下面解释.

改变模式 (change patterns)

具体解释可以参见论文和讲演稿.

简单的说, 就是内核中每次改了一个接口, 比如加了一个参数, 改了函数名, 每一个这样的改动就被称为 “改变模式”.

第二类工具的重点显然就是从树内模块的改动中 (就是从一堆 patch/commit 中), 抽取出改动模式. 之后应用改动模式就是很简单的加个参数, 改个函数名这样的工作 已经有自动工具 coccinelle 可以完成这样的工作了.

实验结果的理解

具体实验结果参见论文即可. 总体来说, 实验结果 (就是内核接口改变的特点) 如下. 之前的自动化工具没有考虑到有些特点, 所以表现不好.

  1. 单个 commit 中可能包含多个 pattern.
  2. 除了 pattern 导致的改变, 还可能有有其他噪音 (如改变内核模块某些算法的实现) 存在. 这两点是自动化工具应当能够处理的.

  3. 通常 (88% 的) pattern 都会改多个地方. 这一点说明抽取模式工具基本上是靠谱的, 因为有了多个改变的地方之后抽取改动模式也更容易.

  4. 相当一部分的 pattern 不能通过 “面向 error/warning” 的方法检测出来. 说明 “面向 error/warning” 的方法不太可靠.

  5. pattern 改变的地方集中在函数体以及全局变量里, 后者又集中于 struct 的字段改动.

  6. 相当一部分 pattern 中包含宏. 宏很麻烦, 因为编译方法分析的时候必须展开他们, 但是生成针对源代码的 patch 的时候又不能展开.

  7. 相当一部分 pattern 需要某种上下文. 通常这就是回调函数, 驱动程序注册进内核的主要方法.

Resources