編程環境搭建: 因為ubuntu 12.04的內核版本已經是3.x,而目前一些講解內核驅動的書都是2.6.x。 嵌入式開發的版本一般都是基於3.14移植的,因為嵌入式是跑在開發板上的,所以開發驅動沒有問題。但是教材的例子一般都是基於PC機的2.6.x版本,雖然內核內部介面相對穩定,但是我也不太清楚。... ...
編程環境搭建:
因為ubuntu 12.04的內核版本已經是3.x,而目前一些講解內核驅動的書都是2.6.x。
嵌入式開發的版本一般都是基於3.14移植的,因為嵌入式是跑在開發板上的,所以開發驅動沒有問題。但是教材的例子一般都是基於PC機的2.6.x版本,雖然內核內部介面相對穩定,但是我也不太清楚。至於低版本的內核驅動是否直接運行在高版本的Ubuntu上,我也不是太瞭解這裡面的對應關係。為了排除無關的干擾,決定虛擬機安裝個Ubuntu 10.04 32位,它對應的內核版本是2.6.32-21,這樣一來,編寫的驅動應該就可以直接在PC上快速測試了。
由於10.04已經停止更新,為了下載軟體包,所以要做些修改,具體看這篇文章Ubuntu 無法找到更新源的問題。
- 更新源
sudo sed -i -e 's/archive.ubuntu.com\|security.ubuntu.com/old-releases.ubuntu.com/g' /etc/apt/sources.list
sudo apt-get update
sudo apt-get upgrade 為了避免出錯,就不更新系統軟體了,也不知道是不是這樣,不既然沒出問題,那麼對系統就能不碰就不碰吧
- 把常用軟體vim/git/ctags/cscope/裝一下
- 安裝編譯工具
在構造和編譯內核模塊之前,應該具備了正確版本的編譯器、模塊工具和其它必要的工具,內核文檔目錄中的Documentation/Changes文件列出了需要的工具版本;在開始構造模塊之前,應該需要查看該文件並確保已安裝了正確的工具,比如:
sudo apt-get install build-essential kernel-package libncurses5-dev
說明:kernel-package是Debian提供的一個編譯Linux內核的一個工具集,安裝kernel-package 會同時安裝上build-essential、libncurses-dev、linux-source等一系列工具。通過apt-cache depends kernel-package可以查看該軟體包的依賴關係:
libncurses5這個軟體包在使用menuconfig配置內核的時候會用到。但預設情況下,apt-get並不安裝推薦和建議的軟體包。如果你沒有修改預設配置,則需要額外的安裝libncurses-dev等工具的操作。
- 終端字體設置一下,解除ScreenSaver
- mkdir kernel code
- 拷貝內核壓縮包到~/kernel
- 烏班圖對應內核版本的介紹:
5.04 2.6.10
6.06 LTS 2.6.15
8.04 LTS 2.6.24
10.04 LTS 2.6.32
12.04 LTS 3.2
14.04 LTS 3.13
處理內核源碼:
- uname -r 查看內核版本,顯示2.6.32
- 使用apt-cache search linux-source命令可以查看可用的源碼包
- 因為ldd3用的是linux-2.6.10,所以先下載個這個版本先用著吧,順便把2.6.32頁下載下來,kernel下載鏈接,不知道2.6.10驅動程式跑在2.6.32版本上會不會出問題,不過介面和數據結構的改動應該不大,先用用看吧
- 或者直接下載 sudo apt-get install linux-source-2.6.32 但是我不知道這個下載下來的是不是linux原版還髮型版,因為書上建議使用原版內核而非經過修改的發行版內核
- sudo cp linux-2.6.10.tar.xz /usr/src
cd /usr/src/
sudo tar xvf linux-2.6.10.tar.xz 超級用戶才能完全解壓
因為版本問題,不知道有沒有用,順便把linux-2.6.32.tar.xz也解壓了吧。
- 上面的幾個步驟是實驗性的,所以中途最好多拍幾個快照,防止虛擬機崩潰。
正確配置和構造內核樹:
ldd3上提到了構造內核樹,不過我已經糊塗了,不知道我下載的版本、開發的驅動和Ubuntu內核版本這三者之間到底是個什麼關係。
Linux驅動學習筆記1:創建Linux內核樹 這篇博客對這個講的比較清楚,具體看連接。
另外還參考的連接先列舉如下
- LDD3構造內核樹(on ubuntu) 這篇操作步驟不錯,沒有太多說明
- 夠建內核樹 這篇是debian的,不過作者好像也沒說明,不過幸好debian這方面和Ubuntu差不多,只是軟體包有差異
- Ubuntu內核源碼樹的構建與安裝 這篇對編譯講的很詳細
操作步驟上面鏈接文章都說的很清楚了,待會列下,這裡主要理清一下幾個概念:
- 下載的版本:linux版本號主要分為4部分,比如2.6.10-5,短線前面的是主要的版本號,比如Ubuntu10.04採用的2.6.32內核,Ubuntu5.04採用的2.6.10內核,也就是不同版本的Ubuntu會採用不同版本的內核。
- Ubuntu其實可以看做類似Android的操作系統,是以linux內核為基礎的文件系統,所以重新替換內核版本,也就是保持版本號前三部分相同,第4部分可以不同,不太清楚如果前三部分的版本號不同可不可以。系統啟動後,Ubuntu是通過內核掛載上去的。
- 原版內核和發行版內核:書上講發行版內核的API可能經過修改,所以最好還是使用原版的內核,既然Ubuntu的內核可以替換,那麼我們就可以替換成Linux官網上下載的原版內核,前提就是創建內核樹。
- 內核樹:我的理解就是內核的源碼經過編譯的狀態就是內核樹。內核安裝主要有兩部分,一個是非模塊部分,一個是模塊部分,這個模塊應該是通用的可以直接安裝到Ubuntu上,非模塊部分就是一個鏡像用於啟動。
- 下麵的步驟主要就是替換Ubuntu的內核為對應版本的原版內核,目前看來應該不能替換成2.6.10了,不過沒試過,不折騰了。
下麵是根據上面幾篇文章整理的編譯步驟和錯誤的編譯步驟(針對x86架構開發,ARM架構這裡不說)
- 導入正確的內核配置文件
先說標準流程,然後再說用的簡便流程:
標準步驟:
- sudo cp /usr/src/linux-headers-2.6.32-38-generic-pae/.config .config 導入系統自帶源碼的配置文件來,使用cp命令拷貝到當前源碼樹的跟目錄下
- make menuconfig 使用make menuconfig來對內核配置文件進行配置
選擇load an Alternate kernel configuration選項載入本地的.config配置文件,然後再選擇save an Alternate kernel configuration再保存退出,並退出配置環境。這樣我們的內核配置就按照原系統的配置選項配置完成。
簡便步驟:
- sudo make oldconfig 配置內核編譯上下文
- 內核編譯
- sudo make -j4 執行完成後會在當前目錄下麵生成一個文件vmlinux.o,如果對內核編譯命令不熟悉,可以在內核源碼樹跟目錄下執行make help查看內核提供的編譯選項
- 內核核心編譯
- make clean 先清除暫存檔,這裡不做
- make vmlinux 未經壓縮的核心,這裡不做
- make modules 僅核心模塊,這裡不做
- make bzImage 經壓縮過的核心,這裡只做這個
- make all 執行上述三個操作,這裡不做
- 說明:常見的在 /boot/ 底下的內核文件,都是經過壓縮過的,因此,上述操作中中比較常用的是 modules 和 bzImage 這兩個。bzImage 可以製作出壓縮過後的內核
- 內核模塊編譯和安裝,編譯完 bzImage後在編譯modules
- make modules 僅僅編譯核心模塊
- make modules_install 模塊安裝到系統,新內核的模塊可以在/lib/modules目錄下
- 安裝完modules再安裝壓縮過的內核核心,內核文件安裝在/boot目錄下
sudo make install
------------------------------------------------------------------------------------------
以下是幾個看了博文之後的錯誤步驟可以忽略,接著是步驟 6.
錯誤步驟:sudo mkinitramfs -o /boot/initrd.img.2.6.32-27 /lib/modules/2.6.32.27
錯誤步驟:sudo gedit /boot/grub/grub.cfg
替換32-21-generic為32.27
錯誤步驟:
------------------------------------------------------------------------------------------
- 生成內核對應的RAMDISK文件,這個估計是用於啟動的
sudo update-initramfs -c -k 2.6.32.27
- 更新grub配置文件
sudo update-grub
說明:參考/boot/grub/grub.cfg文件,可以看到新內核的配置已經加到配置文件中。為避免可能出現的無法開機的情況,Grub.cfg的配置預設值不要設置為新的內核。
- 測試新安裝的內核版本
重啟後,開機也沒有出現選項
- 重新安裝vmware tools因為內核改變了(vim插件也重新安裝下),因為之前安裝過一次,重覆安裝可能會耗時較長些。重啟後,保存快照。
- 測試安裝驅動模塊,以下例子拷貝自文章:LDD3構造內核樹(on ubuntu)
-----------------------------------------------------------
在某一目錄下創建2個文件:hello.c和Makefile
hello.c內容如下:
/*
* $Id: hello.c,v 1.5 2011/06/21 03:32:21 eric $
*/
#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("Dual BSD/GPL");
static int hello_init(void)
{
printk(KERN_ALERT "Hello, world\n");
return 0;
}
static void hello_exit(void)
{
printk(KERN_ALERT "Goodbye, cruel world\n");
}
module_init(hello_init);
module_exit(hello_exit);
Makefile內容如下:
# To build modules outside of the kernel tree, we run "make"
# in the kernel source tree; the Makefile these then includes this
# Makefile once again.
# This conditional selects whether we are being included from the
# kernel Makefile or not.
ifeq ($(KERNELRELEASE),)
# Assume the source tree is where the running kernel was built
# You should set KERNELDIR in the environment if it's elsewhere
KERNELDIR ?= /lib/modules/$(shell uname -r)/build # 內核樹build目錄的位置
# The current directory is passed to sub-makes as argument
PWD := $(shell pwd)
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
modules_install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
# .PHONY修飾的目標就是只有規則沒有依賴
.PHONY: modules modules_install clean
else
# called from kernel build system: just declare what our modules are
obj-m := hello.o # 模塊目標文件
endif
需要註意的是Makefile的格式要正確。
開始編譯模塊:
make
不出現錯誤的話,用ls -al查看該目錄,會產生如下文件:
hello.c hello.mod.c hello.o modules.order
hello.ko hello.mod.o Makefile Module.symvers
現在我們就可以將編譯好的模塊hello載入到內核中去了
sudo insmod ./hello.ko //這個命令把hello.ko載入到內核,模塊裝載觸發hello.c的init()方法
sudo lsmod|grep hello //lsmod 這個命令可以查看當前所有的驅動模塊,結果應該顯示hello 680 0
sudo rmmod hello //這個命令是把hello這個模塊移除掉
程式的輸出結果可以在
/var/log/syslog文件中查看
Hello,World
Goodbye,cruel world
這是程式的輸出。
-----------------------------------------------------------
有用的連接:
- Linux kernel device driver programming [closed] 關於ldd3的一些說明
- API changes in the 2.6 kernel series 關於ldd3內核版本往後的API改動
- 上條連接的最後提到了 ldd3-examples-3.x 作者實現了機會所有最新版本的代碼遷移,所以估計如果對內核驅動框架比較熟悉,那麼不同內核版本差異帶來的驅動差異並不是很大,所以學習驅動不要糾結內核版本