1. 程式人生 > >Linux 內核編譯步驟及配置詳解

Linux 內核編譯步驟及配置詳解

crc校驗 內存管理 tools 內核空間 -m 2.6 系統 腳本 查看

linux 系統體系結構:

技術分享圖片

linux kernel體系結構:
arm有7種工作模式,x86也實現了4個不同級別RING0-RING3,RING0級別最高,
這樣linux用戶代碼運行在RING3下,內核運行在RING0,這樣系統本身就得到了
充分的保護

用戶空間(用戶模式)轉到內核空間(系統模式)方法:
·系統調用
·硬件中斷

linux kernel 體系結構:

技術分享圖片

虛擬文件系統VFS:
VFS(虛擬文件系統)隱藏各種文件系統的具體細節,為文件操作提供統一的接口


二.Linux內核源代碼
linux內核下載www.kernel.org
目錄結構:
解壓linux kernel tar後目錄
·arch:根據cpu體系結構不同而分的代碼
·block:部分塊設備驅動程序
·crypto:加密,壓縮,CRC校驗算法
·documentation:內核文檔
·drivers:設備驅動程序
·fs(虛擬文件系統vfs):文件系統
·include:內核所需的頭文件,(與平臺無關的頭文件在include/linux中)
·lib:庫文件代碼(與平臺相關的)
·mm:實現內存管理,與硬件體系結構無關的(與硬件體系結構相關的在arch中)
·net:網絡協議的代碼
·samples:一些內核編程的範例
·scripts:配置內核的腳本
·security:SElinux的模塊
·sound:音頻設備的驅動程序
·usr:cpio命令實現,用於制作根文件系統的命令(文件系統與內核放到一塊的命令)
·virt:內核虛擬機

linux DOC 編譯生成:

linux源根目錄/Documentation/00-INDEX:目錄索引
linux源根目錄/Documentation/HOWTO:指南
·生成linux內核幫助文檔:在linux源根目錄(Documentation) 執行make htmldocs

ubuntu16下需要執行sudo apt-get install xmlto安裝插件才可生成doc文檔

後面開發中經常要改的是arch,drivers中的代碼


三.Linux內核配置與編譯
清理文件(在linux源碼根目錄):
·make clean:只清理所有產生的文件
·make mrproper:清理所有產生的文件與config配置文件
·make distclean:清理所有產生的文件與config配置文件,並且編輯過的與補丁文件

配置(收集硬件信息如cpu型號,網卡等...):


·make config:基於文本模式的交互配置
·make menuconfig:基於文本模式的菜單模式(推薦使用)
·make oldconfig:使用已有的.config,但會詢問新增的配置項
·make xconfig:圖形化的配置(需要安裝圖形化系統)
配置方法:
1)使用make menuconfig操作方法:
1>按y:編譯>連接>鏡像文件
2>按m:編譯
3>按n:什麽都不做
4>按"空格鍵":y,n輪換
配置完並保存後會在linux源碼根目錄下生成一個.config文件
註意:在ubuntu11上要執行apt-get install libncurses5-dev來安裝支持包
2)利用已有的配置文件模板(.config)
1>linux源碼根目錄/arch/<cpu架構>/configs/<具體某一的CPU文件>,把裏面對應的文件copy並改名為.config至linux源碼根目錄下
2>利用當前運行已有的文件(要用ls /boot/ -a查看)把/boot/config-2.6.18-53.e15拷貝並改名為.config至linux源碼根目錄下執行以上操作就可以用make menuconfig在拷貝
.config文件上面修改文件了

編譯內核:

1)make zImage
2)make bzImage
區別:在X86平臺上,zimage只能用於小於512k的內核
獲取詳細編譯信息:make zimage V=1 或 make bzimage V=1
編譯好的內核在:arch/<cpu>/boot/目錄下
註意:在把.config配置文件cp到根目錄編譯內核前,必須進入make menuconfig並保存退出(否則生不了效)

編譯並安裝模塊:
1)編譯內核模塊:make modules
2)安裝內核模塊:make modules_install INSTALL_MOD_PATH=/lib/modules
更換本機器內核:將編譯好的內核模塊從內核源碼目錄copy至/lib/modules下
制作init ramdisk():輸入執行命令mkinitrd initrd-2.6.39(任意) 2.6.39(可通過查詢/lib/modules下的目錄得到)
註意:
mkinitrd命令為redhat裏面的,ubuntu的命令為:mkinitramfs -k /lib/modules/模塊安裝位置 -o initrd-2.6.39(任意) 2.6.39(可通過查詢/lib/modules下的目錄得到)
如果ubuntu裏面沒有mkinitramfs命令可以用apt-get install initrd-tools進行安裝

安裝內核模塊:
1)手動
1>cp linux根目錄/arch/x86/boot/bzImage /boot/mylinux-2.6.39
2>cp linux根目錄/initrd-2.6.39 /boot/initrd-2.6.39
最後修改/etc/grub.conf或/etc/lilo.conf文件
2)自動
1>make install:這個命令會自動完成上面的操作(查看當前內核版本:uname -r)
-----------------------------------------------------------------------------
四.linux內核模塊開發
描述:
linux內核組件非常龐大,內核ximage並不包含某組件,而是在該組件需要被使用的時候,動態的添加到正在運行的內核中(也可以卸載),這種機制叫做“內核模塊”的機制。內核模塊通常通過使用makefile文件對模塊進行編譯

模塊安裝與卸載:
1)加載:insmod hello.ko
2)卸載:rmmod hello
3)查看:lsmod
4)加載(自動尋找模塊依賴):modprobe hello
modprobe會根據文件/lib/modules/version/modules.dep來查看要加載的模塊,看它是否還依賴於其他模塊,如果是,會先找到這些模塊,把它們先加載到內核

實例分析:
1)moduleDep/1(一個模塊的編譯)

技術分享圖片
 1 #include <linux/module.h>
 2 #include <linux/init.h>
 3 
 4 //模塊入口函數
 5 //__init:表示代碼段中的子段,裏面的內容只運行一次並且回收內存.
 6 static int __init hello_init(void)
 7 {
 8     printk(KERN_EMERG "hello world!\n");
 9     return 0;
10 }
11 //模塊卸載函數
12 //__exit:
13 static void __exit hello_exit(void)
14 {
15     printk(KERN_EMERG "hello exit!\n");
16 }
17 //內核符號導出 函數
18 int add_integar(int a,int b)
19 {
20     return a+b; 
21 }
22 int sub_integar(int a,int b)
23 {
24     return a-b; 
25 }
26 
27 module_init(hello_init);
28 module_exit(hello_exit);
29 //函數導出
30 EXPORT_SYMBOL(add_integar);
31 EXPORT_SYMBOL(sub_integar);
技術分享圖片

makefile:

技術分享圖片
#第一次執行KERNELRELEASE是空的,所以執行else裏面的
ifneq ($(KERNELRELEASE),)

obj-m :=hello.o

#else塊
else

KDIR:= /lib/modules/2.6.18-53.el5/build

all:
#KDIR    依賴內核模塊源代碼路徑(內核編譯安裝路徑)
#PWD     表示內核代碼在哪(當前目錄)
#modules 編譯的是模塊
    make -C $(KDIR) M=$(PWD) modules 

clean:
    rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.order

endif
技術分享圖片

2)moduleDep/2(兩個模塊的編譯)

技術分享圖片
 1 #include <linux/module.h>
 2 #include <linux/init.h>
 3 //模塊可選信息
 4 MODULE_LICENSE("GPL");//許可證聲明
 5 MODULE_AUTHOR("liyuan");//作者聲明
 6 MODULE_DESCRIPTION("This module is a param example.");//模塊描述
 7 MODULE_VERSION("V1.0");//模塊別名
 8 MODULE_ALIAS("a simple module");//模塊別名
 9 
10 //模塊參數
11 static char *name = "liyuan arg";
12 static int age = 30;
13 //S_IRUGO是參數權限,也可以用數字
14 module_param(age,int,S_IRUGO);
15 module_param(name,charp,S_IRUGO);
16 
17 
18 //使用外部文件函數
19 extern int add(int a,int b);
20 
21 
22 //聲明 外部內核符號 函數
23 extern int add_integar(int a,int b);
24 extern int sub_integar(int a,int b);
25 
26 static int __init mains_init(void)
27 {
28      //多文件編譯
29 
30     printk(KERN_EMERG"param hi");
31     int vle=add(1,2);
32     printk(KERN_EMERG"add value:%d\n",vle);
33     //模塊參數
34 
35      printk(KERN_EMERG" name : %s\n",name);
36      printk(KERN_EMERG" age : %d\n",age);
37 
38     //使用其他模塊的函數(內核符號導出)
39     int adds=add_integar(3,1);
40     int subs=sub_integar(3,1);
41     printk(KERN_EMERG" add_integar : %d\n",adds);
42     printk(KERN_EMERG" sub_integar : %d\n",subs);
43     return 0;
44 }
45 
46 static void __exit mains_exit(void)
47 {
48     printk("param exit!");
49 }
50 
51 module_init(mains_init);
52 module_exit(mains_exit);
技術分享圖片

add.c

int add(int a,int b)
{
    return a+b;
}

makefile

技術分享圖片
ifneq ($(KERNELRELEASE),)
#兩個以上內核源文件 生成單獨的內核模塊名ma

#內核ma
obj-m :=ma.o
#下面的ma-objs前面必須和上面一樣為ma
ma-objs := mains.o add.o

else

KDIR:= /lib/modules/2.6.18-53.el5/build

all:
        make -C $(KDIR) M=$(PWD) modules 
clean:
    rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.order

endif
技術分享圖片

運行帶參模塊:insmod hello.ko name=yuan age=12
內核符號導出(/proc/kallsyms記錄了內核中所有導出的符號的名字與地址):
一個內核模塊的運行依賴另一個內核模塊的函數實現,必須先運行第一個內核模塊,這樣就需要進行內核符號導出。

註意:
錯誤信息:disagrees about version of symbol struct_module insmod:error inserting ...
開發內核模塊時會出現,內核模塊不匹配的情況.是你當前運行的linux內核與編譯連接所依賴的
內核版本不匹配,解決方法:
·使用modprobe --force-modversion強行插入
·可使用uname -r進行查看當前運行的內核版本

printk內核打印:
在<linux/kernel.h>中printk有8個優先級,按優先級遞減的是:
·KERN_EMERG 0
用於緊急的消息,常常是那些崩潰的消息
·KERN_ALERT 1
需要立刻行動的消息
·KERN_CRIT 2
嚴重情況
·KERN_ERR 3
錯誤情況
·KERN_WARNING(printk默認級別) 4
有問題的警告
·KERN_NOTICE 5
正常情況,但是仍然值得註意
·KERN_INFO 6
信息消息
·KERN_DEBUG 7
用作調試消息

不管是哪個級別的都會在/var/log/messages裏面打印出來(messages可以刪除後,運行內核進行測試內核打印情況)控制臺打印(優先級配置/proc/sys/kernel/printk)

總結一下我們的安裝步驟:

       1、獲取內核源碼,解壓至/usr/src
           # tar xf linux-3.13.5.tar.xz -C /usr/src
           # ln -sv /usr/src/linux-3.13.5  /usr/src/linux
       2、配置內核特性(選擇一種方法就可以了)
           make config:遍歷選擇所要編譯的內核特性
           make allyesconfig:配置所有可編譯的內核特性
           make allnoconfig:並不是所有的都不編譯
           make menuconfig:這種就是打開一個文件窗口選擇菜單
           make kconfig(KDE桌面環境下,並且安裝了qt開發環境)
           make gconfig(Gnome桌面環境,並且安裝gtk開發環境)
       3、編譯內核
           # make [-j #] : #號最多為CPU物理核心總數的兩倍,這樣會快點哦            
       4、安裝內核模塊
           # make modules_install
       5、安裝內核
           # make install
       6、驗正並測試
           # cat /boot/grub/grub.conf
           查看新內核是否已經添加, 而後重啟系統並測試

Linux 內核編譯步驟及配置詳解