内核驱动程序和内核模块有什么区别?

Joh*_*mBF 83 linux drivers kernel kernel-modules

当我lspci -k使用 3.2.0-29-generic 内核在我的 Kubuntu 上做一个时,我可以看到这样的东西:

01:00.0 VGA compatible controller: NVIDIA Corporation G86 [Quadro NVS 290] (rev a1)
    Subsystem: NVIDIA Corporation Device 0492
    Kernel driver in use: nvidia
    Kernel modules: nvidia_current, nouveau, nvidiafb
Run Code Online (Sandbox Code Playgroud)

有一个内核驱动程序nvidia和内核模块nvidia_currentnouveaunvidiafb

现在我想知道内核驱动程序和内核模块之间可能有什么区别?

War*_*ung 93

内核模块是一些编译后的代码,可以在运行时插入到内核中,例如使用insmodmodprobe

驱动程序是在内核中运行以与某些硬件设备通信的一小段代码。它“驱动”硬件。您计算机中的大多数硬件都有一个关联的驱动程序。¹ 正在运行的内核的很大一部分是驱动程序代码。²

驱动程序可以静态构建到磁盘上的内核文件中。³ 驱动程序也可以构建为内核模块,以便以后可以动态加载。(然后可能卸载了。)

标准做法是尽可能将驱动程序构建为内核模块,而不是将它们静态链接到内核,因为这提供了更大的灵活性。但是,有充分的理由不这样做:

  • 有时,给定的驱动程序对于帮助系统启动是绝对必要的。由于initrd功能,这不会像您想象的那样经常发生。

  • 静态构建的驱动程序可能正是您在静态范围内的系统(例如嵌入式系统)中想要的。也就是说,如果您事先确切地知道将始终需要哪些驱动程序并且这永远不会改变,那么您就有充分的理由不去打扰动态内核模块。

  • 如果您静态构建内核并禁用 Linux 的动态模块加载功能,则可以防止内核代码的运行时修改。这以牺牲灵活性为代价提供了额外的安全性和稳定性。

并非所有内核模块都是驱动程序。例如,Linux 内核中一个相对较新的特性是您可以加载不同的进程调度程序。另一个例子是,更复杂的硬件类型通常有多个通用层,位于低级硬件驱动程序和用户空间之间,例如USB HID 驱动程序,它实现了 USB 堆栈的特定元素,独立于底层硬件。


旁白:

  1. 有一个例外,这个广泛的说法是CPU芯片,它没有“驱动程序”本身。您的计算机也可能包含您没有驱动程序的硬件。

  2. 操作系统内核中的其余代码提供通用服务,如内存管理IPC调度等。这些服务可能主要服务于用户级应用程序,如前面链接的示例,或者它们可能是驱动程序或其他内部服务使用的内部服务。内核基础设施。

  3. 引导过程中/boot引导加载程序在引导时将其中的一个加载到 RAM

  • 这是一个很好的总体轮廓,但我有与OP完全相同的问题,然后遇到这个答案,但仍然不知道为什么“使用中的驱动程序”与“模块”不同。相比之下,@Jim Paris 的答案是正确的。来自`man lspci`:“-k 显示处理**每个设备的内核**驱动程序以及能够处理**它的内核**模块。” 您可以将其解读为:“显示驱动程序**当前/实际处理**设备以及**所有可以/旨在处理**它的模块”。 (3认同)

Jim*_*ris 24

要回答有关lspci输出的具体问题,“内核驱动程序”行指的是当前绑定到卡的nvidia驱动程序,在这种情况下是专有驱动程序。“内核模块”行列出了已知能够绑定到该卡的所有驱动程序。在这里,专有驱动程序显示了一个不同的名称,这可能是由于lspci驱动程序及其文件名与编码到驱动程序本身中的名称的发现方式有关。


Cir*_*郝海东 8

内核模块可能根本不是设备驱动程序

“内核驱动程序”不是一个定义明确的术语,但让我们试一试。

这是一个不驱动任何硬件的内核模块,因此不能被合理地视为“设备驱动程序”:

#include <linux/module.h>
#include <linux/kernel.h>

MODULE_LICENSE("GPL");

static int myinit(void)
{
    printk(KERN_INFO "hello init\n");
    return 0;
}

static void myexit(void)
{
    printk(KERN_INFO "hello exit\n");
}

module_init(myinit)
module_exit(myexit)
Run Code Online (Sandbox Code Playgroud)

构建后,您可以将其用于:

insmod hello.ko
Run Code Online (Sandbox Code Playgroud)

它打印hello initdmesg.

然而,有些内核模块不是设备驱动程序,但实际上很有用,例如,公开内核调试/性能信息的模块。

设备驱动程序通常也是内核模块。

“设备驱动程序”的示例有点难以生成,因为它需要硬件来驱动,并且硬件描述往往很复杂。

然而,使用 QEMU 或其他模拟器,我们可以构建真实或简化硬件的软件模型,这是学习如何与硬件对话的好方法。这是一个最小 PCI 设备驱动程序的简单示例:https : //github.com/cirosantilli/linux-kernel-module-cheat/blob/6788a577c394a2fc512d8f3df0806d84dc09f355/kernel_module/hello.c

然后我们看到,在 x86 中,与硬件的对话归结为:

这些操作通常不能从用户空间完成,如:用户空间和内核空间有什么区别?但是也有一些例外:https : //stackoverflow.com/questions/7986260/linux-interrupt-handling-in-user-space

然后,内核提供更高级别的 API 以使此类硬件交互更容易和更便携:

  • request_irq 处理中断
  • ioreadX 和 IO 内存映射
  • 用于流行协议(如 PCI 和 USB)的更高级别的接口


use*_*726 5

根据这个不错的教程

...一种类型的模块是设备驱动程序,它允许内核访问连接到系统的硬件。

因此,如果我们尝试绘制一棵树,我们将拥有从(扩展)模块继承的“设备驱动程序”,并且具有更具体的特征,在这些特征之间我们会发现“访问硬件”......