如何构建一个Linux内核模块,使其兼容所有内核版本?

7
我想构建一个内核模块,它能与所有内核版本兼容。例如,如果我在内核版本3.2.0-29上构建了一个内核模块并尝试在3.2.0-86上加载它,则会出现以下错误:
modprobe my_driver FATAL: Error inserting my_driver (/lib/modules/3.2.0-86-generic/kernel/fs/my_drv/my_drv.ko): Invalid module format [ 在日志信息中:my_drv: disagrees about version of symbol module_layout ]
如何在3.2.0-29上构建一个适用于所有3.2.0版本的内核模块?

“-29”和“-86”不是内核版本的一部分。也许你应该提到你正在谈论哪个发行版。 - Ignacio Vazquez-Abrams
即使您成功禁用了版本检查,您仍需要避免所有可能更改的API(这些API都是如此)。 - CL.
“modprobe -f my_driver”(注意-f)有什么区别吗? - Giuseppe Gorgoglione
不,还是同样的错误。我认为解决方法在于构建内核模块的方式上,但我找不到任何有用的信息。 - kobi
1
你的问题表述不够清晰。你提到要为内核版本3.2.0-29构建一个模块。这是内核版本3,补丁级别2,子级别0和额外版本29。而你只想支持不同的额外版本。你不应该担心任何API的变化。 - sawdust
显示剩余3条评论
3个回答

6
简而言之:你几乎不可能编写有用的内核模块,可以加载到相对广泛版本的内核中。
当你针对使用CONFIG_MODVERSIONS编译的内核构建模块(就像你的情况一样),对于从内核导出的每个符号,都会在模块文件中存储此符号的CRC。 CRC是某种控制和,其中考虑了用于函数参数的类型布局,以及其他因素。例如,如果两个内核中假想的struct A的布局不同,则这些内核中函数f(struct A *a)的CRC也会有所不同。
当模块加载到运行中的内核中时,将比较模块中所有函数的CRC与内核中的CRC。如果它们不同,内核将拒绝加载模块。要了解有关此机制的更多信息,请参阅内核文档(Documentation/kbuild/modules.txt)。
因此,要使模块可以加载到两个不同的内核中,您只能使用参数布局在两个内核中相同的函数。特别地,如果类型struct module的布局不同,则不能为两个内核加载单个模块。
有几种方法可以为多个内核提供适用的驱动程序。最简单的方法是提供驱动程序的源代码并将其添加到dkms中。这样,如果运行的内核没有构建驱动程序,则该驱动程序将使用其源代码自动编译。

谢谢您的回答。在我的情况下,我不能将源代码提供给客户。还有其他方法吗? - kobi
如果您不想分发源代码,那么您应该为每个您想要支持的内核版本(和配置)分发(二进制)驱动程序。Linux内核甚至没有稳定的API,即使在小的错误修复中也可能会更改其ABI。 - Tsyvarev
您也可以考虑将驱动程序部分分发为源代码,部分分发为二进制 blob 文件。假设二进制文件不使用内核 API,则它们可能与各种内核版本兼容。文档Documentation/kbuild/modules.txt还描述了如何使用二进制 blob 文件构建模块。 - Tsyvarev
对于一些安全敏感的结构(例如struct task_structstruct mm_struct),其成员字段的顺序是随机化的(由一个随机种子确定)。这个特性被称为randstruct,请参阅https://lwn.net/Articles/722293/。 - youfu

2
Linux内核模块API的不稳定性是一种设计选择

这在源代码树中明确解释,位于Documentation/stable_api_nonsense.txt。摘要如下:

Executive Summary
-----------------
You think you want a stable kernel interface, but you really do not, and
you don't even know it.  What you want is a stable running driver, and
you get that only if your driver is in the main kernel tree.  You also
get lots of other good benefits if your driver is in the main kernel
tree, all of which has made Linux into such a strong, stable, and mature
operating system which is the reason you are using it in the first
place.

但是需要进行重要的澄清:

  The kernel to userspace interface is the one that application programs use,
  the syscall interface.  That interface is **very** stable over time, and
  will not break.  I have old programs that were built on a pre 0.9something
  kernel that still work just fine on the latest 2.6 kernel release.
  That interface is the one that users and application programmers can count
  on being stable.

不必担心!我们友好的Linux开发人员在同一文档中解释了解决方案。
What to do
----------

So, if you have a Linux kernel driver that is not in the main kernel
tree, what are you, a developer, supposed to do?  Releasing a binary
driver for every different kernel version for every distribution is a
nightmare, and trying to keep up with an ever changing kernel interface
is also a rough job.

Simple, get your kernel driver into the main kernel tree (remember we
are talking about GPL released drivers here, if your code doesn't fall
under this category, good luck, you are on your own here, you leech
<insert link to leech comment from Andrew and Linus here>.)  If your
driver is in the tree, and a kernel interface changes, it will be fixed
up by the person who did the kernel change in the first place.  This
ensures that your driver is always buildable, and works over time, with
very little effort on your part.

Linux内核版本宏

尝试使用:

#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16)

如在这个问题中提到的一样,有一个宏定义可以检查Linux内核版本。


-1
在Makefile中添加一个环境变量,即“K_VERSION”。
通过将版本作为环境变量传递来构建已安装内核的模块。
例如:
make K_VERSION=`uname -r`

针对当前正在运行的内核进行构建。

构建完成后,可以使用modprobe实用程序。


请参考O'Reilly出版社的《Linux设备驱动程序》第2章。 - sulimo
这似乎并没有回答实际提出的问题。 - davmac
我认为我的问题没有表达清楚。我想在3.2.0-29上构建一个内核模块,并将此模块提供给使用3.2.0-*内核(例如3.2.0-86)的客户。 - kobi
有一个选项 --force-vermagic,虽然据说不安全,请参考(http://linux.die.net/lkmpg/x380.html) - sulimo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接