重建所有已安装内核的DKMS模块的命令是什么?

偶尔,我的系统会出现一些内核缺少一个或两个模块的情况,因为DKMS不知何故忘记为该内核编译这些模块。与其花时间诊断问题,如果有一个单一的命令可以重新构建每个已安装内核的每个受DKMS控制的模块,那就太好了。是否有这样的命令?

我总是得到输出模块broadcom-sta/5.100.82.112已经安装在内核2.6.38jon-64/x86_64上。我真的想要一个--force或者--rebuild --just-do-what-i-say选项 ;) - user140350
7个回答

我想出了一个用一行命令来完成的shell脚本:
ls /var/lib/initramfs-tools | \
    sudo xargs -n1 /usr/lib/dkms/dkms_autoinstaller start

这个方法有效是因为在/var/lib/initramfs-tools目录中的文件夹名称正好是你需要传递给dkms_autoinstaller的内核版本名称,以便告诉它重新构建所有这些内核版本的模块。请注意,如果你已经卸载了一些旧的内核版本,它们的文件夹可能仍然存在并导致报告一些错误,但这不是问题,因为dkms_autoinstaller对于未安装的内核版本将不会执行任何操作。

1由于出现“headers-xxx”和“headers-xxx-generic”的错误,它报了一些错误,但似乎在错误的情况下重建了正确的内容。 - frankster
@frankster 在安装新内核时,我发现了多个“错误!找不到dkms.conf文件。”之后,我能够使用dkms status列出的先前的内核模块安装到新内核中,具体操作是从“/usr/src”目录下选择特定的模块。需要根据实际情况更改参数 -c、-m、-v。以下是nvidia-384-384.90模块的示例: ls /var/lib/initramfs-tools | \ sudo xargs -n1 /usr/sbin/dkms install -c /usr/src/nvidia-384-384.90/dkms.conf -m nvidia -v 384-384.90 -k - m1st0
1/var/lib/initramfs-toolsLTS 22.04 和 Debian bullseye不再被填充(目录已丢失)。 - Tino
在进入我需要构建的内核后,执行/usr/lib/dkms/dkms_autoinstaller start命令成功运行。 - tmm1

看起来dkms命令不允许你这样做。我创建了一个小的Python脚本,应该可以实现你想要的功能。你可以在~/.bashrc中设置一个别名。
alias dkms-buildall='sudo ./wherever/your/script/is'

当然,您需要先将其设置为可执行文件。以下是代码:
#!/bin/env python
#
# NOTE: This assumes that all modules and versions are built for at
#       least one kernel. If that's not the case, adapt parsing as needed.
import os
import subprocess

# Permission check.
if os.geteuid() != 0:
    print "You need to be root to run this script."
    exit(1)

# Get DKMS status output.
cmd = ['dkms', 'status']
process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
dkms_status = process.communicate()[0].strip('\n').split('\n')
dkms_status = [x.split(', ') for x in dkms_status]

# Get kernel versions (probably crap).
cmd = ['ls', '/var/lib/initramfs-tools/']
# Alternative (for use with Arch Linux for example)
# cmd = ['ls', '/usr/lib/modules/']
process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
kernels = process.communicate()[0].strip('\n').split('\n')

# Parse output, 'modules' will contain all modules pointing to a set
# of versions.
modules = {}

for entry in dkms_status:
   module = entry[0]
   version = entry[1].split(': ')[0]
   try:
      modules[module].add(version)
   except KeyError:
      # We don't have that module, add it.
      modules[module] = set([version])

# For each module, build all versions for all kernels.
for module in modules:
   for version in modules[module]:
      for kernel in kernels:
         cmd = ['dkms', 'build', '-m', module, '-v', version, '-k', kernel]
         ret = subprocess.call(cmd)

测试了一下,在这里,看起来工作得很好。
$ dkms status
nvidia-current, 275.09.07, 3.0.0-5-generic, x86_64: installed
virtualbox, 4.0.10, 3.0.0-5-generic, x86_64: installed

$ sudo python dkms.py
...

$ dkms status
nvidia-current, 275.09.07, 3.0.0-5-generic, x86_64: installed
nvidia-current, 275.09.07, 3.0-2-generic, x86_64: built
nvidia-current, 275.09.07, 3.0-3-generic, x86_64: built
virtualbox, 4.0.10, 3.0.0-5-generic, x86_64: installed
virtualbox, 4.0.10, 3.0-2-generic, x86_64: built
virtualbox, 4.0.10, 3.0-3-generic, x86_64: built

如果你也想安装这些模块,将倒数第二行的 build 替换为 install

结合 @htorque 和 @Ryan Thompson 的答案,这是我的一行代码(以 root 用户身份):
dkms status | sed s/,//g | awk '{print "-m",$1,"-v",$2}' | while read line; do ls /var/lib/initramfs-tools | xargs -n 1 dkms install $line -k; done

没有足够的声望来评论@Ryan Thompson的答案,但这可能对某些人有用。在Ubuntu 22.04中没有/var/lib/initramfs-tools目录,然而,在每个已安装的内核版本中,/boot目录下存在initrd.img-镜像,这些正是dkms(和dkms_autoinstaller)所需的。因此让我们使用它们:

ls /boot/initrd.img-* | cut -d- -f2- | \
    sudo xargs -n1 /usr/lib/dkms/dkms_autoinstaller start

一个由@htorque编辑的脚本。如果你想要对已经构建的模块进行强制重建(和安装),可以使用它。已切换到Python 3,subprocess.run()需要Python 3.5+版本。
#!/usr/bin/env python3
#
# NOTE: This assumes that all modules and versions are built for at
#       least one kernel. If that's not the case, adapt parsing as needed.
import os
import subprocess
import re

# Permission check.
if os.geteuid() != 0:
    print("You need to be root to run this script.")
    exit(1)

# Get DKMS status output.
cmd = ['dkms', 'status']
dkms_status = subprocess.run(cmd, stdout=subprocess.PIPE).stdout.decode("utf-8").strip('\n').split('\n')
dkms_status = [re.split(', |/', x) for x in dkms_status]

##
# Get kernel versions (probably crap).
#cmd = ['ls', '/var/lib/initramfs-tools/']  # Does not work on Ubuntu 22.04
# Alternative (for use with Arch Linux for example)
# cmd = ['ls', '/usr/lib/modules/']
#kernels = subprocess.run(cmd, stdout=subprocess.PIPE).stdout.decode("utf-8").strip('\n').split('\n')
##

## Works on 22.04
prefix = 'initrd.img-'
kernels = [k[len(prefix):] for k in os.listdir('/boot')
           if k.startswith(prefix)]
##

# Parse output, 'modules' will contain all modules pointing to a set
# of versions.
modules = {}

for entry in dkms_status:
   module = entry[0]
   version = entry[1].split(': ')[0]
   try:
      modules[module].add(version)
   except KeyError:
      # We don't have that module, add it.
      modules[module] = set([version])

# For each module, build all versions for all kernels.
for module in modules:
   for version in modules[module]:
      for kernel in kernels:
         for action in ['remove', 'install']:
            cmd = ['dkms', action, '-m', module, '-v', version, '-k', kernel]
            subprocess.run(cmd)

上述方法并不适用于所有变体,在这些情况下,以下方法可能更有帮助...
$modulename="whatever"
$moduleversion=`modinfo $modulename | grep "^version:" | awk '{ print $2 }'`
dkms status | grep $modulename | tr -d ',' | awk '{ print $3 }' | xargs -n1 dkms build $modulename/$moduleversion -k

你能详细说明一下这个方法与其他方法有什么不同吗? - Ryan C. Thompson
1它适用于没有 /usr/src/linux-headers-* 和 /var/lib/initramfs-tools 的系统。 - stu
1考虑到需要在许多不同的Linux发行版上运行,这些发行版只有一个共同点,即(某种程度上)支持DKMS。 - stu

dkms statusdkms_autoinstaller在ubuntu 16.x中无法正常工作。因此,需要一些shell脚本来完成任务。 这个脚本假设您已经正确安装了*-dkms deb软件包,并且使用的是bash shell。

for k in $(ls /var/lib/initramfs-tools) ; do
 for d in $(cd /usr/src; ls -d *-*) ; do
  [[ -f /usr/src/${d}/dkms.conf ]] || continue
  m=$(echo $d | sed -r -e 's/-([0-9]).+//')
  v=$(echo $d | sed -r -e 's/[^0-9]+-([0-9])/\1/')
  sudo /usr/sbin/dkms install -c /usr/src/$d/dkms.conf -m $m -v $v -k $k
 done
done