Linux 内核最大的漏洞。一个简单的 USB over IP 示例。
本质上,Linux 中的驱动程序是内核的一部分。从现在开始,我将引用Linux 基金会的《Linux 驱动程序模型:更好的设备支持方式》一文,其中 Linux 开发者对 Linux 和 Windows 的驱动程序模型进行了比较。缺点是需要重新路由。显然,在这种架构下,即使是自动驱动程序更新也只有在 RPM 仓库中所有可能的驱动程序都针对当前内核版本编译的情况下才能实现。顺便说一下,这是 Linux
大家好!我是大聪明-PLUS!
-
为什么Windows版需要付费,而Linux版免费?
-
Linux内核的边界在哪里,兼容性问题又从哪里开始呢?
-
为 Linux 设置 USB 重定向器
-
企鹅展开翅膀了吗?
为什么Windows版需要付费,而Linux版免费?
USB 密钥比软件许可证方便得多,尤其是在虚拟环境中,虚拟机的任何移动都需要重新激活许可证。此外,软件许可证可以方便地绑定到 USB 密钥(任何容量)。缺点是需要重新路由。
Linux 系统中可以通过不同的方式实现 USB 转发。
例如,您可以使用 usbip(usbip win),它被认为是标准库。尝试在 Oracle Linux(Red Hat)版本 9 上安装它。搜索所有推荐的软件仓库后,仍然找不到任何结果。研究发现,需要针对特定的 Linux 内核从源代码编译它。好的,我们继续。
我在 Windows 系统上使用 USB Redirector 的体验不错,但它是付费服务。我试用了 Linux 版本,也很棒——而且是免费的!客户端和服务端都有。不过安装说明有点让人抓狂——你必须从源代码编译,然后还得忍受各种问题。
如果在安装 USB Redirector 后内核进行了更新,则可能需要重新安装程序以重新编译其内核模块!
这是因为Linux内核模块与特定的内核版本绑定,因此需要针对每个新内核重新编译才能正常工作。
好了,你应该明白我的意思了:如果我在 Windows 系统下安装 USB Redirector,我可以轻松地安装更新,并且 99% 确定一切都能正常运行。但是在 Linux 系统下,更新内核时,几乎可以肯定它不会生效。因为 USB Redirector 需要重新编译!那么更新怎么办呢?情况是这样的。
所以我们有两个问题
-
缺少当前内核所需程序的rpm包。
-
更新时需要不断重新编译。
造成这种情况的原因完全在于Linux系统的构建理念。Linux社区有一篇很棒的文章解答了这个问题:为什么Linux会是这样?当然,通往系统管理员地狱的道路,其设计初衷仅仅是为了打造一个开发者的天堂。
让我们仔细看看 USB 重定向器的部署情况。
Linux内核的边界在哪里,兼容性问题又从哪里开始呢?
所以,我们有一个全新安装的 Oracle Linux 系统,内核版本为
uname -r
5.15.0-302.167.6.el9uek.x86_64
解压后的发行版必须复制到 Linux 服务器,并且必须从发行版目录运行该命令。
sudo ./installer.sh install
理论上,USB Redirector 的客户端和服务器部分应该已经编译、构建和安装完毕,但当然,我却遇到了意想不到的情况。
开发者在说明文档中建议安装软件包。以下是命令行。
yum install make gcc chkconfig kernel-devel-`uname -r`
但是我的内核版本对应的 kernel-devel 软件包找不到。而且这还是在全新安装的 Oracle Linux 系统上!
顺便说一下,这是 Linux 中通过未编译模块分发软件的“正常”做法,而且正如你稍后将看到的,原因不是软件开发人员的懒惰,而是内核的理念。
要理解这一点,就必须达到一定的理解水平。这就像物理学一样:要理解宇宙中的各种过程,就必须首先研究原子核、基本粒子等等内部的过程。
从现在开始,我将引用Linux 基金会的《Linux 驱动程序模型:更好的设备支持方式》一文,其中 Linux 开发者对 Linux 和 Windows 的驱动程序模型进行了比较。如果您想更深入地了解 Linux 开发者的动机,我建议您阅读英文版。

这张图表明,在操作系统(Windows 和 Linux)中存在一些概念。
API – 应用程序编程接口
ABI – 应用程序二进制接口
此外,它们可以存在于两个区域:Linux 内核到用户空间,Linux 内核到内部空间。
因此,在 Windows 系统中,ABI(应用程序二进制接口)意味着即使在不同的 Windows 版本之间也能保持稳定性。
在 Linux 中,即使跨内核版本,ABI 稳定性也无法保证,这是一种刻意的策略。
Linux 在应用程序二进制接口 (ABI) 和应用程序接口 (API) 方面都缺乏稳定性,这在驱动程序开发中尤为明显,但它在用户空间接口方面却非常稳定。这使得编译后的应用程序能够在不同的 Linux 版本上正常运行,但编译后的驱动程序却无法做到这一点。
本质上,Linux 中的驱动程序是内核的一部分。“驱动程序”这个术语甚至已被主线内核所接受。在 Windows 系统中,驱动程序和内核是严格隔离的——驱动程序开发者看不到内核代码,内核开发者也看不到驱动程序代码。而在 Linux 系统中,通常会提供驱动程序代码供用户编译以适配特定的内核。
那么让我们根据标准来比较这些模型。
已编译驱动程序的兼容性。
-
Windows系统的兼容性更高。
-
在 Linux 系统中,这种兼容性并不存在;只能通过重新编译驱动程序来实现。
跨平台
-
在 Windows 系统上,这个数值较低,因为 ABI 只能在该平台内保持稳定。
-
Linux 具有高度跨平台性,因为构建发行版需要编译适用于该平台的源代码。
与旧设备的兼容性。
-
在 Windows 系统中,该驱动程序可以正常工作,但一旦与新版本的操作系统出现致命的不兼容问题,就会失效。
-
在 Linux 系统中,可以通过重新编译来解决这个问题;如果用户数量变得非常少,则会排除旧的驱动程序。
对更新的容忍度。
-
Windows 具有很高的兼容性,因为它意味着驱动程序的接口兼容性。
-
在 Linux 系统中,这取决于更新的位置:用户空间还是内核(内部)。如果驱动程序包含在软件仓库中,则不会有问题;否则,您需要重新编译驱动程序。由于为每个内核版本创建 RPM 包是无偿劳动,因此您将获得源代码进行编译。
模型对误差的鲁棒性。
-
在 Windows 系统中,编译完成后,开发者可以测试驱动程序并将其交给管理员。无需分发源代码——ABI 在不同的内核之间是兼容的。
-
在Linux系统中,编译完成后,管理员根本没有办法测试驱动程序。谁来给他们提供测试用例呢?
Linux 开发人员认为他们的 ABI 模型具有进步性,原因如下:
-
完全开源的内核和驱动程序代码可以实现最佳的编写效果。
-
无需实施过时的规范(例如 USB 规范),您可以立即实施新的规范。
-
将驱动程序包含在内核中有助于实现操作系统稳定性,因为它可以提供来自双方的出色代码审查。
-
具有高度跨平台兼容性,包括对未来几代设备的兼容性。
为了与 Windows 进行比较,文中提到了 Windows Vista 驱动程序的情况——从过渡到 64 位系统到后续支持。的确,Windows 驱动程序开发商会一直支持驱动程序,直到设备停止销售为止。
然而,出于某种原因,Linux 开发人员并没有关注这两个问题。
-
驱动程序编译本应作为 DevOps 流程的一部分进行测试,但实际上,用户端编译的内容和方式根本没有经过任何测试。这意味着我们最初会将未经测试的驱动程序版本部署到操作系统中。
-
如果驱动程序是操作系统内核的一部分,那么在更新 Linux 操作系统时就会出现不兼容问题。
-
如果编译过程始终顺畅,就像安装带有依赖控制的 RPM 包一样,这种情况尚可接受。但正如您将在下文看到的,实际情况要糟糕得多。
事实上,ABI 模型也决定了 Linux 的未来。它会成为便捷的 IT 基础设施工具,还是会局限于“一劳永逸”的设备?更新方面,它只会是“运行正常,无需维护”。为了帮助您得出自己的结论,让我们来看看 USB 重定向器的安装过程。需要注意的是,未找到与当前内核兼容的内核开发版本。
启用默认禁用的存储库
dnf repolist
/etc/yum.repos.d
Enable=1
dnf install https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm
如果你尝试这样查询软件包,你找不到 kernel-devel,而是会找到其他东西。奇怪吧?
dnf list kernel-* | grep 5.15.0-302.167.6
kernel-uek.x86_64 5.15.0-302.167.6.el9uek @anaconda
kernel-uek-core.x86_64 5.15.0-302.167.6.el9uek @anaconda
kernel-uek-modules.x86_64 5.15.0-302.167.6.el9uek @anaconda
dnf list --showduplicates kernel*devel*
dnf install <package_name>-<version>-<release>.<architecture>
dnf list kernel-uek-devel-5.15.0-302.167.6.el9uek.x86_64
Last metadata expiration check: 0:23:18 ago on Wed 12 Nov 2025 12:13:52 AM +06.
Available Packages
kernel-uek-devel.x86_64 5.15.0-302.167.6.el9uek ol9_UEKR7
安装软件包
dnf install kernel-uek-devel-5.15.0-302.167.6.el9uek.x86_64
Last metadata expiration check: 0:23:48 ago on Wed 12 Nov 2025 12:13:52 AM +06.
Dependencies resolved.
==============================
Package Architecture Version Repository Size
==============================
Installing:
kernel-uek-devel x86_64 5.15.0-302.167.6.el9uek ol9_UEKR7 41 M
Running transaction
Preparing : 1/1
Installing : kernel-uek-devel-5.15.0-302.167.6.el9uek.x86_64 1/1
Running scriptlet: kernel-uek-devel-5.15.0-302.167.6.el9uek.x86_64 1/1
Verifying : kernel-uek-devel-5.15.0-302.167.6.el9uek.x86_64 1/1
Installed:
kernel-uek-devel-5.15.0-302.167.6.el9uek.x86_64
Complete!
再次尝试运行 ./installer.sh 脚本时,安装程序提示安装 dkms。
Dkms 允许您自动重新编译所需的模块,而不是整个内核项目。最好安装它,否则您将不得不面对更高层次的复杂问题。
安装 dkms
dnf install dkms
我不会给出结论,但它确实会与之前的内核版本产生依赖关系——这看起来很可怕,时间会证明会产生什么副作用。
我们再次安装 USB Redirector,太好了,安装成功了!
./installer.sh install
*** Installing USB Redirector for Linux v3.12
*** Destination dir: /usr/local/usb-redirector
*** Checking installation...
*** Detecting system...
*** distribution: redhat
*** init: systemd
*** kernel: 5.15.0-302.167.6.el9uek.x86_64
*** dkms: dkms-3.2.2
*** Configuring kernel module...
*** Compiling kernel module with dkms...
*** Kernel module successfully compiled
*** Creating directories...
*** Preparing scripts...
*** Copying files...
*** Installing daemon...
*** Starting daemon...
*** Please allow incoming connections on 32032 port for the USB server to be able to accept connections from remote clients.
*** INSTALLATION SUCCESSFUL! To uninstall, run /usr/local/usb-redirector/uninstall.sh
如您所见,“正在使用 dkms 编译内核模块...”进程正在进行中。恭喜,您已完成部分开发职责,但尚未进行任何测试。希望接下来一切顺利。
为 Linux 设置 USB 重定向器
设置时,我们假设有一台服务器存放密钥,另一台机器(虚拟机)使用这些密钥。USB重定向器安装在两台机器上——它只是在服务器(密钥服务器)上共享密钥,然后连接到应用程序服务器(客户端)。
别忘了使用密钥检查服务器上的“32032”端口。
firewall-cmd --query-port=32032/tcp
firewall-cmd --zone=public --permanent --add-port=32032/tcp
firewall-cmd --zone=public --permanent --add-port=32032/tcp
firewall-cmd –reload
首先,检查已安装软件的状态(在两台服务器上);该服务应已启用。
systemctl status usbsrvd
● usbsrvd.service - IncentivesPro USB Redirector for Linux
Loaded: loaded (/usr/lib/systemd/system/usbsrvd.service; enabled; preset: disabled)
Active: active (running) since Wed 2025-11-12 00:57:35 +06; 16min ago
Process: 119397 ExecStartPre=/sbin/modprobe usbcore (code=exited, status=0/SUCCESS)
Process: 119398 ExecStartPre=/bin/sh -c [ -f /usr/local/usb-redirector/bin/tusbd.ko ] && /sbin/insmod /usr/local/usb-redirector/bin/tusbd.ko >/dev/null 2>&1 && exit 0;>
Process: 119400 ExecStart=/usr/local/usb-redirector/bin/usbsrvd (code=exited, status=0/SUCCESS)
Main PID: 119401 (usbsrvd)
Tasks: 8 (limit: 21297)
Memory: 3.5M
CPU: 47ms
CGroup: /system.slice/usbsrvd.service
└─119401 /usr/local/usb-redirector/bin/usbsrvd
Nov 12 00:57:35 ERP1CUX systemd[1]: Starting IncentivesPro USB Redirector for Linux...
Nov 12 00:57:35 ERP1CUX systemd[1]: Started IncentivesPro USB Redirector for Linux.
接下来,从 /usr/local/usb-redirector 应用程序目录中,添加密钥所在服务器的地址。USB 重定向服务器也应以相同方式安装在此处。 现在,我们将把 USB 重定向服务器(即 USB 密钥实际所在的服务器)的地址添加到 USB 重定向客户端。
./usbclnt -add-server 192.168.0.157:32032
====================== OPERATION SUCCESSFUL =====================
USB server has been added
===================== ======================= ===================
我们检查哪些密钥可用。首先,我们需要在服务器上创建一个包含这些密钥的共享文件夹。在这种情况下,我们将转发准备好的密钥。
./usbclnt -list-devices
================== LIST OF REMOTE USB DEVICES ===================
1: USB server at 192.168.0.157:32032
Mode: manual-connect Status: connected
|
|- 2: Sentinel HL
| Vid: 0529 Pid: 0001 Port: 3-11
| Mode: manual-connect Status: available for connection
|
`- 3: Sentinel HL
Vid: 0529 Pid: 0001 Port: 3-12
Mode: manual-connect Status: available for connection
===================== ======================= ===================
现在你需要转发这些密钥(连接到它们),它们就会在 1C 服务器上可用。
./usbclnt -connect 1-2
====================== OPERATION SUCCESSFUL =====================
USB device connected
===================== ======================= ===================
./usbclnt -connect 1-3
====================== OPERATION SUCCESSFUL =====================
USB device connected
===================== ======================= ===================
./usbclnt -list-devices
================== LIST OF REMOTE USB DEVICES ===================
1: USB server at 192.168.0.157:32032
Mode: manual-connect Status: connected
|
|- 2: Sentinel HL
| Vid: 0529 Pid: 0001 Port: 3-11
| Mode: manual-connect Status: connected
|
`- 3: Sentinel HL
Vid: 0529 Pid: 0001 Port: 3-12
Mode: manual-connect Status: connected
===================== ======================= ===================
你看,它现在能用,但直到下一次内核版本更新为止。
企鹅展开翅膀了吗?
我想,当你走过这条路之后,就会明白Linux的入门门槛相当高。所以,不要对Linux桌面系统普及率一直很低感到惊讶,因为在这种情况下,安装驱动程序会直接将用户引导到内核。你奶奶用的也是Linux系统,她能按照你的指示安装你发给她的驱动程序吗?
显然,在这种架构下,即使是自动驱动程序更新也只有在 RPM 仓库中所有可能的驱动程序都针对当前内核版本编译的情况下才能实现。这只是理想状态,因此源代码分发包将会长期存在。
因此,就像 USB 的例子一样,会产生太多尴尬的问题:
-
如果驱动程序的免驱动代码以源代码和发行版的形式在 Linux 中分发,编译后如何保证其有效性?
-
管理员需要从源代码编译代码,这如何与DevOps自动化测试的要求相契合?他从哪里获取自动化测试用例?
-
如果从源代码编译驱动程序在 Linux 中是正常的,那么为什么 kernel-devel 没有单一的掩码,并且在安装后不能立即以正确的格式和版本存在呢?
-
为什么默认情况下没有安装dkms?
这对我来说完全是个谜——自由软件是否存在任何发展策略,或者说,能够自由使用开源软件本身就是一种策略?
很明显,对于开发者来说,查看开源代码并编译他们需要的所有内容是非常方便的。
相反,对于希望在 Linux 上构建服务器基础架构的专业人员(管理员)来说,这在可靠性方面带来了严峻挑战。一方面,他们需要定期安装更新,但另一方面,他们也需要注意更新过程中可能出现的问题。
更多推荐



所有评论(0)