建立你自己的內核模塊

英文版本由 AkemiYagi 建立。現時由 AlanBartlettAkemiYagi 維護。

請注意,假若你修改你的內核,你將不再穫得 CentOS 開發小組的支援或幫助。這裡所描述的步驟並沒有 CentOS 的官方認可。這個教學文檔的原意是要幫助你建立自己的內核模塊。

/ArtWork/WikiDesign?action=AttachFile&do=get&target=ArtWork/WikiDesign/icon-admonition-info.png

要是你將會在採用 Secure Boot 的系統上安裝自己的模塊,你須要以私鑰簽署該模塊。詳情見 Signing Kernel Modules for Secure Boot

/!\ 本教學文檔以 CentOS-7 作為範例操作系統。

在某些情況下你也許會需要更改或建立一個新的內核模塊。也許你要加入某個功能,或者只是做一個修正。在這件教學文檔內,我們會嘗試將一個錯誤修正加進現有的模塊內,然後安裝它。

這裡假設你已經安裝了整個內核的源代碼。如果你是按照「我需要內核的源代碼」的第 2 部份,它會位於 ~/rpmbuild/BUILD/kernel-3.10.0/linux-3.10.0.`uname -m`/ 這個目錄內。

/!\ 我們強烈反對你以 root 用戶來建立模塊。(見:Building Source RPM as non-root under CentOS

1. 建立一個內核模塊(*.ko)

設我們假設,作為一個範例,你將會針對位於 ~/rpmbuild/BUILD/kernel-3.10.0/linux-3.10.0-957.x86_64/fs/cifs/ 目錄內的 cif 模塊加入修正。

1. 針對源代碼檔進行所需的修正。

2. 進到內核源代碼的主目錄。如果這次是內核首次被編譯,請設定內核,並謹記在設定檔(.conf)內將相關的元件設為模塊。

[user@host]$ cd ~/rpmbuild/BUILD/kernel-3.10.0/linux-3.10.0-957.x86_64
[user@host linux-3.10.0-957.x86_64]$ make oldconfig
[user@host linux-3.10.0-957.x86_64]$ make prepare

3. 建立編譯外置模塊時所需的檔案。

[user@host linux-3.10.0-957.x86_64]$ make modules_prepare

4. 透過指定模塊的 Makefile 及源代碼的相對路徑來編譯模塊。

[user@host linux-3.10.0-957.x86_64]$ make M=fs/cifs

註:藏有模塊的目錄可以位於任何一個位置。舉個例說,假如它位於 ~/mycifs/,你可以在內核源代碼的主目錄裡執行以下的指令:

[user@host linux-3.10.0-957.x86_64]$ make M=~/mycifs

5. 除非你編譯這個模塊作偵錯用途,否則你應該移除不必要的符號。

[user@host linux-3.10.0-957.x86_64]$ strip --strip-debug fs/cifs/cifs.ko

6. 這個範例建立了 cifs.ko 這個檔案。請以 root 的身份將 .ko 檔案複製到 /lib/modules/<內核版本>/extra/ 目錄內。

[root@host linux-3.10.0-957.x86_64]# cp fs/cifs/cifs.ko /lib/modules/`uname -r`/extra

7. 以 root 的身份執行 depmod 指令來更新模塊的依賴檔。

[root@host linux-3.10.0-957.x86_64]# depmod -a

2. 利用動態內核模塊支援(DKMS)建立一個內核模塊

上面所描述的方法是為某個特定的內核版本建立一個模塊。一旦你將內核升級或者更改硬件的架構,你將會須要將模塊重新建立。動態內核模塊支援(DKMS)這個架構基本上是在內核源代碼以外的地方複製一棵目錄樹,當中藏有某個模塊的源代碼及編譯了的二進制程式。DKMS 可以用來建立、安裝及解除安裝模塊。DKMS 需要在系統上找到模塊的源代碼。DKMS 程式庫亦能代你建立及安裝模塊到你系統上的任何內核裡。

在這裡我們會採用與上面相同的範例來建立及安裝 cifs 這個模塊。在下面整個部份,你都須要有 root 的權限。

1. 安裝所有與這個模塊的目標內核版本相乎的 kernel-devel 套件。

2. 安裝 EPEL 軟件庫(見 軟件庫頁)內的 dkms 套件。

3. 建立一個 /usr/src/<模塊>-<模塊版本>/ 的目錄

[root@host]# mkdir /usr/src/cifs-1.45fixed/

4. 將模塊的源代碼複製到這個目錄。

[root@host]# cd ~/rpmbuild/BUILD/kernel-2.6.18/linux-2.6.18-i686/fs/cifs
[root@host cifs]# cp -a * /usr/src/cifs-1.45fixed/

5. 在 /usr/src/<模塊>-<模塊版本>/ 這個目錄內建立 dkms.conf

[root@host cifs]# cd /usr/src/cifs-1.45fixed
[root@host cifs-1.45fixed]# vi dkms.conf

dkms.conf 檔須要包含以下內容:

PACKAGE_NAME="cifs"
PACKAGE_VERSION="1.45fixed"
BUILT_MODULE_NAME[0]="cifs"
DEST_MODULE_LOCATION[0]="/kernel/fs/cifs/"
AUTOINSTALL="yes"

註:DEST_MODULE_LOCATION[0] 這一行將會在安裝模塊時被忽視,因為它一定會是 /lib/modules/<內核版本>/extra/ 這個目錄。然而,這個參數卻指定當模塊被解除安裝時,舊有被儲存的模塊(假若有的話)應該被還原到哪個位置。

6. 將 <模塊>/<模塊版本> 加進 DKMS 的目錄樹。

[root@host cifs-1.45fixed]# dkms add -m cifs -v 1.45fixed

7. 在 DKMS 的控制下編譯模塊。

[root@host cifs-1.45fixed]# dkms build -m cifs -v 1.45fixed

8. 在 DKMS 的控制下安裝模塊。

[root@host cifs-1.45fixed]# dkms install -m cifs -v 1.45fixed

其它值得注意的 DKMS 動作包括 uninstallremovestatusmkrpm。它們分別會將模塊從內核移除,將 <模塊>/<模塊版本> 從 DKMS 目錄樹移除,顯示 DKMS 的現時狀況,及在 /var/lib/dkms/<模塊>/<模塊版本>/rpm/ 這個目錄內建立一個 rpm 檔案。

請亦參閱:

3. 建立一個內核模塊的 rpm 套件(kmod)

你亦可以為內核模塊建立 rpm 檔案。這個內核模塊的套件(kmod)便可以像其它套件一樣透過 rpm 指令來安裝。然而,包裝內核模塊的 rpm 與標準的包裝過程有不同的處理方法。以下的範例提供了基本的內核模塊 rpm 包裝技巧。

再一次,我們會以 cifs 模塊作為範例來建立一套 kmod-cifs 的 rpm 檔。

1. 安裝所有對應你現有內核的 kernel-devel 套件。

2. 正如上面的第 1 部份,已修正的源代碼位於 ~/rpmbuild/BUILD/kernel-2.6.18/linux-2.6.18.i686/fs/cifs/ 這個目錄內。請複製這個目錄。

[user@host]$ mkdir ~/cifs-1.45/
[user@host]$ cd ~/rpmbuild/BUILD/kernel-2.6.18/linux-2.6.18.i686/fs/cifs
[user@host]$ cp -a * ~/cifs-1.45/

3. 建立一個藏有來源目錄的 bzip2 壓縮檔。

[user@host]$ cd
[user@host]$ tar -jcf cifs-1.45.tar.bz2 cifs-1.45/

4. 將壓縮檔複製到 SOURCES 目錄內。

[user@host]$ cp cifs-1.45.tar.bz2 ~/rpmbuild/SOURCES/

5. 複製 kmodtool 這個腳本(它是 redhat-rpm-config 套件的一部份)到 SOURCES 目錄內。

[user@host]$ cd ~/rpmbuild/SOURCES/
[user@host SOURCES]$ cp /usr/lib/rpm/redhat/kmodtool kmodtool-cifs-el5.sh

6. 編輯你所複製的 kmodtool-cifs-el5.sh 檔案,並確定當中提及 kmod-common 的行都被改為註釋。然後加入一行在 %files 內會擴充成指向 kmod-cifs.conf 檔的指令。

[user@host SOURCES]$ vi kmodtool-cifs-el5.sh

由第 105 行起 ——

#
# RHEL5 - Remove common package requirement on general kmod packages.
# Requires: ${kmod_name}-kmod-common >= %{?epoch:%{epoch}:}%{version}
#

由第 168 行起 ——

echo "%files -n kmod-${kmod_name}${dashvariant}"

if [ "" == "$kmp_override_filelist" ];
then
    echo "%defattr(644,root,root,755)"
    echo "/lib/modules/${verrel}${variant}/"
    echo "%config /etc/depmod.d/kmod-${kmod_name}.conf"
    #BZ252188 - I've commented this out for the moment since RHEL5 doesn't
    # really support external firmware e.g. at install time. If
    # you really want it, use an override filelist solution.
    #echo "/lib/firmware/"
else
    cat "$kmp_override_filelist"
fi

7. 在 SPECS 目錄內建立 cifs-kmod.spec 檔。

[user@host SOURCES]$ cd ~/rpmbuild/SPECS/
[user@host SPECS]$ vi cifs-kmod.spec

# Define the kmod package name here.
%define kmod_name cifs

# If kversion isn't defined on the rpmbuild line, define it here.
%{!?kversion: %define kversion 2.6.18-8.el5}

Name:    %{kmod_name}-kmod
Version: 1.45
Release: 1%{?dist}
Group:   System Environment/Kernel
License: GPLv2
Summary: %{kmod_name} kernel module(s)
URL:     http://www.centos.org/

BuildRequires: redhat-rpm-config
BuildRoot:     %{_tmppath}/%{name}-%{version}-%{release}-build-%(%{__id_u} -n)
ExclusiveArch: i686 x86_64

# Sources.
Source0:  %{kmod_name}-%{version}.tar.bz2
Source10: kmodtool-%{kmod_name}-el5.sh

# Define the variants for each architecture.
%define basevar ""
%ifarch i686
%define paevar PAE
%endif
%ifarch i686 x86_64
%define xenvar xen
%endif

# If kvariants isn't defined on the rpmbuild line, build all variants for this architecture.
%{!?kvariants: %define kvariants %{?basevar} %{?xenvar} %{?paevar}}

# Magic hidden here.
%{expand:%(sh %{SOURCE10} rpmtemplate_kmp %{kmod_name} %{kversion} %{kvariants})}

# Disable the building of the debug package(s).
%define debug_package %{nil}

# Define the filter.
%define __find_requires sh %{_builddir}/%{buildsubdir}/filter-requires.sh

%description
This package provides the CentOS-5 bug-fixed %{kmod_name} kernel module (bug #1776).
It is built to depend upon the specific ABI provided by a range of releases
of the same variant of the Linux kernel and not on any one specific build.

%prep
%setup -q -c -T -a 0
for kvariant in %{kvariants} ; do
    %{__cp} -a %{kmod_name}-%{version} _kmod_build_$kvariant
done
echo "/usr/lib/rpm/redhat/find-requires | %{__sed} -e '/^ksym.*/d'" > filter-requires.sh
echo "override %{kmod_name} * weak-updates/%{kmod_name}" > kmod-%{kmod_name}.conf

%build
for kvariant in %{kvariants} ; do
    KSRC=%{_usrsrc}/kernels/%{kversion}${kvariant:+-$kvariant}-%{_target_cpu}
    %{__make} -C "${KSRC}" %{?_smp_mflags} modules M=$PWD/_kmod_build_$kvariant
done

%install
%{__rm} -rf %{buildroot}
export INSTALL_MOD_PATH=%{buildroot}
export INSTALL_MOD_DIR=extra/%{kmod_name}
for kvariant in %{kvariants} ; do
    KSRC=%{_usrsrc}/kernels/%{kversion}${kvariant:+-$kvariant}-%{_target_cpu}
    %{__make} -C "${KSRC}" modules_install M=$PWD/_kmod_build_$kvariant
done
%{__install} -d %{buildroot}%{_sysconfdir}/depmod.d/
%{__install} kmod-%{kmod_name}.conf %{buildroot}%{_sysconfdir}/depmod.d/
# Set the module(s) to be executable, so that they will be stripped when packaged.
find %{buildroot} -type f -name \*.ko -exec %{__chmod} u+x \{\} \;

%clean
%{__rm} -rf %{buildroot}

%changelog
* Wed Jan 05 2011 Alan Bartlett <ajb@elrepo.org> - 1.45
- Revised this specification file.

* Fri May 18 2007 Akemi Yagi <toracat@centos.org> - 1.45
- Initial el5 build of the kmod package.

8. 建立套件。

[user@host SPECS]$ rpmbuild -bb --target=`uname -m` cifs-kmod.spec 2> build-err.log | tee build-out.log

你果你不想建立一個對應使用中的內核的 kmod 套件,你可以在指令行上指定內核的版本。例如:

[user@host SPECS]$ rpmbuild -bb --target=`uname -m` --define 'kversion 2.6.18-371.el5' cifs-kmod.spec 2> build-err.log | tee build-out.log

這樣做便會為 2.6.18-371.el5 內核建立 kmod 套件。

在 CentOS 7 及內含 kmodtool 腳本的較新版本,請用以下方式提供內核版本

[user@host SPECS]$ rpmbuild -bb --target=`uname -m` --define 'kernel_version 3.10.0-862.el7' cifs-kmod.spec 2> build-err.log | tee build-out.log

這樣做便會為 3.10.0-862.el7 內核建立 kmod 套件。

利用同一個方法,你可以選擇建立哪一個內核類型的 kmod 套件。例如:

[user@host SPECS]$ rpmbuild -bb --target=`uname -m` --define 'kvariants ""' cifs-kmod.spec 2> build-err.log | tee build-out.log

這樣做便只會建立基本內核的 kmod 套件。

當編譯完成後,~/rpmbuild/RPMS/`uname -m`/ 目錄內將會藏有一套 kmod-cifs 的 rpm 檔。

請亦參閱:

Translation of revision 168

zh-tw/HowTos/BuildingKernelModules (last edited 2019-08-08 10:08:00 by TimothyLee)