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 可以完成这样的工作了.
实验结果的理解
具体实验结果参见论文即可. 总体来说, 实验结果 (就是内核接口改变的特点) 如下. 之前的自动化工具没有考虑到有些特点, 所以表现不好.
- 单个 commit 中可能包含多个 pattern.
-
除了 pattern 导致的改变, 还可能有有其他噪音 (如改变内核模块某些算法的实现) 存在. 这两点是自动化工具应当能够处理的.
-
通常 (88% 的) pattern 都会改多个地方. 这一点说明抽取模式工具基本上是靠谱的, 因为有了多个改变的地方之后抽取改动模式也更容易.
-
相当一部分的 pattern 不能通过 “面向 error/warning” 的方法检测出来. 说明 “面向 error/warning” 的方法不太可靠.
-
pattern 改变的地方集中在函数体以及全局变量里, 后者又集中于 struct 的字段改动.
-
相当一部分 pattern 中包含宏. 宏很麻烦, 因为编译方法分析的时候必须展开他们, 但是生成针对源代码的 patch 的时候又不能展开.
- 相当一部分 pattern 需要某种上下文. 通常这就是回调函数, 驱动程序注册进内核的主要方法.