Linux 內核模塊編譯 Makefile


驅動編譯分為靜態編譯動態編譯;靜態編譯即為將驅動直接編譯進內核,動態編譯即為將驅動編譯成模塊。

而動態編譯又分為兩種:

a -- 內部編譯

       在內核源碼目錄內編譯

b -- 外部編譯

       在內核源碼的目錄外編譯

 

二、具體編譯過程分析   

        注:本次編譯是外部編譯,使用的內核源碼是Ubuntu 的源代碼,而非開發板所用linux 3.14內核源碼,運行平台為X86。

        對於一個普通的linux設備驅動模塊,以下是一個經典的makefile代碼,使用下面這個makefile可以完成大部分驅動的編譯,使用時只需要修改一下要編譯生成的驅動名稱即可。只需修改obj-m的值。

 

ifneq  ($(KERNELRELEASE),)
obj-m:=hello.o
else
KDIR := /lib/modules/$(shell uname -r)/build
PWD:=$(shell pwd)
all:
    make -C $(KDIR) M=$(PWD) modules
clean:
    rm -f *.ko *.o *.symvers *.cmd *.cmd.o
endif

 

 

1、makefile 中的變量

    先說明以下makefile中一些變量意義:

(1)KERNELRELEASE           在linux內核源代碼中的頂層makefile中有定義

(2)shell pwd                             取得當前工作路徑

(3)shell uname -r                    取得當前內核的版本號

(4)KDIR                                     當前內核的源代碼目錄。

關於linux源碼的目錄有兩個,分別為

 "/lib/modules/$(shell uname -r)/build"

"/usr/src/linux-header-$(shell uname -r)/"

       但如果編譯過內核就會知道,usr目錄下那個源代碼一般是我們自己下載后解壓的,而lib目錄下的則是在編譯時自動copy過去的,兩者的文件結構完全一樣,因此有時也將內核源碼目錄設置成/usr/src/linux-header-$(shell uname -r)/。關於內核源碼目錄可以根據自己的存放位置進行修改。

(5)make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules

這就是編譯模塊了:

a -- 首先改變目錄到-C選項指定的位置(即內核源代碼目錄),其中保存有內核的頂層makefile;

b -- M=選項讓該makefile在構造modules目標之前返回到模塊源代碼目錄;然后,modueles目標指向obj-m變量中設定的模塊;在上面的例子中,我們將該變量設置成了hello.o。

 

2、make 的的執行步驟

a -- 第一次進來的時候,宏“KERNELRELEASE”未定義,因此進入 else;

b -- 記錄內核路徑,記錄當前路徑;

       由於make 后面沒有目標,所以make會在Makefile中的第一個不是以.開頭的目標作為默認的目標執行。默認執行all這個規則

c -- make -C $(KDIR) M=$(PWD) modules

    -C 進入到內核的目錄執行Makefile ,在執行的時候KERNELRELEASE就會被賦值,M=$(PWD)表示返回當前目錄,再次執行makefile,modules 編譯成模塊的意思

     所以這里實際運行的是

     make -C /lib/modules/2.6.13-study/build M=/home/fs/code/1/module/hello/ modules

d -- 再次執行該makefile,KERNELRELEASE就有值了,就會執行obj-m:=hello.o

     obj-m:表示把hello.o 和其他的目標文件鏈接成hello.ko模塊文件,編譯的時候還要先把hello.c編譯成hello.o文件

 

可以看出make在這里一共調用了3次

   1)-- make
   2)-- linux內核源碼樹的頂層makedile調用,產生。o文件
   3)-- linux內核源碼樹makefile調用,把.o文件鏈接成ko文件

 

3、編譯多文件

若有多個源文件,則采用如下方法:

obj-m := hello.o

hello-objs := file1.o file2.o file3.o

 

三、內部編譯簡單說明

        如果把hello模塊移動到內核源代碼中。例如放到/usr/src/linux/driver/中, KERNELRELEASE就有定義了。

     在/usr/src/linux/Makefile中有KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)$(LOCALVERSION)。

這時候,hello模塊也不再是單獨用make編譯,而是在內核中用make modules進行編譯,此時驅動模塊便和內核編譯在一起。

 

頂層:

然后src中:是所有的源程序以及頭文件( 我這里是使用自己的IR樹的代碼作為實驗 )

 

而build文件夾是為了編譯使用的!下面有:

obj文件夾里面放的是編譯過程中的.o和.d文件,還有一個subdir.mk的子文件,

用於指示怎么生成.o

obj中:

 

下面我們從頂層開始慢慢分析:

 

*******溫馨提示:下面的注釋是為了方便處理,寫在每一條語句后面,其實這樣的風格是不好的,所以,如果

       你使用了這個makefile,請將注釋換行...或者去掉,否則可能編譯異常!謝謝記住!

*******

 

最外層的makefile:

 

[plain]  view plain  copy
 
 print?
  1. SHELL = /bin/sh             # 這個地方是指示使用的shell是sh  
  2. EXEC = ir_tree              # 最終生成的binary的名稱  
  3. BUILD_DIR = build           # 這個子文件夾,此處也就是我們build文件夾  
  4.   
  5. all:                        # all在此處是終極目標,這個你應該知道的。一般我們make的時候,第一個目標作為終極目標  
  6.     @( cd ${BUILD_DIR}; make )  # 這句是進去build文件夾去執行那個makefile  
  7.                                 
  8. clean:                      # clean就不說了  
  9.     @echo 'start clean...'  
  10.     @($(RM) $(EXEC))  
  11.     @(cd ${BUILD_DIR}; make clean)  
  12.     @echo 'Finished!'  
  13.     @echo ''  

 

 

現在進入build文件夾,看這個文件夾下面的makefile

 

[plain]  view plain  copy
 
 print?
  1. SHELL = /bin/sh            # 同上  
  2.   
  3. INCLUDE_DIR :=             # include文件夾,一般我們在引用庫的時候,需要將其頭文件放在一個include中,然后自己的程序                           # 編譯的時候需要包含這個include,例如-I$(<span style="font-family: SimHei;">INCLUDE_DIR</span><span style="font-family: SimHei;">)</span>  
  4. LIB_DIR := -lm             # 引入的庫  
  5. EXEC = ../ir_tree          # 這是一個最終binary名稱,這里是將這個可執行放在了上層文件夾中  
  6.   
  7. -include obj/subdir.mk     # 這個地方是include了一個子文件  
  8.                            # 這里子文件作用是,為了生成所有的.o文件(當然附帶生成.d文件!),生成.o之后,才能回到這一                           # 層的makefile進行鏈接成最終的可執行的操作!具體操作我們稍后再看  
  9.   
  10. all:${EXEC}                # 好!這里是這個makefile的第一個目標。即終極目標,所有需要找<span style="font-family: SimHei;">${EXEC}的生成規則!</span>  
  11.   
  12. ${EXEC}: ${OBJS}           # <span style="font-family: SimHei;">${EXEC}的生成規則,注意這里我們沒有看到$(OBJS),那是因為在</span><span style="font-family: SimHei;">obj/subdir.mk中!</span><span style="font-family: SimHei;">  
  13. </span> @echo ' Building target: $@ '  
  14.     gcc -o $@ $(OBJS) $(LIB_DIR)   # 這一句就是為了將所有的.o文件 + 引用的庫 鏈接起來,生成最后的$@,也就是$(EX                                       # EC),也就是最后的binary!  
  15.     @echo 'Finished building target: $@'  
  16.     @echo ''  
  17.   
  18. clean:  
  19.     @echo 'start rm objs and deps ...'  
  20.     $(RM) $(OBJS) \  
  21.     $(C_DEPS)  
  22.     @echo 'Finish rm objs and deps ...'  
  23.   
  24. .PHONY: all clean                      # 偽目標  
  25. .SECONDARY:  


下面需要看看obj中的subdir.mk的內容了!這個是為了生成所有的.o文件。

 

同時!請注意:當我們的一個.c或者.h被修改之后,需要重新編譯!這一點非常重要!

特別是.h被修改的時候,不能忘記重新編譯( 當然,有些時候.h修改,我們不需要編譯,這個先暫時不說,后面在討論!其實,你使用一個make --touch就可以~ )

 

[plain]  view plain  copy
 
 print?
  1. C_SRCS += \            # 所有的.c文件,當然你喜歡使用wildcard也是可的!  
  2. ../src/card.c \        # $(<span style="font-family: SimHei;">wildcard ../src/*.c</span><span style="font-family: SimHei;">)</span>  
  3. ../src/index.c \  
  4. ../src/node.c \  
  5. ../src/rect.c \  
  6. ../src/split_l.c \  
  7. ../src/test.c  
  8.   
  9. OBJS += \             <span style="font-family: SimHei;"># 所有的.c文件,當然你喜歡使用wildcard也是可的!</span>  
  10. ./obj/card.o \        # OBJS = $(patsubst %.c,%.o,$(wildcard ../src/*.c))  
  11. ./obj/index.o \       # 但是你要將src文件目錄改成obj的 <span style="font-family: SimHei;">OBJS := $(addprefix "./obj/",$(notdir $(OBJS)))</span>  
  12. ./obj/node.o \  
  13. ./obj/rect.o \  
  14. ./obj/split_l.o \  
  15. ./obj/test.o  
  16.   
  17. C_DEPS += \          # deps  
  18. ./obj/card.d \  
  19. ./obj/index.d \  
  20. ./obj/node.d \  
  21. ./obj/rect.d \  
  22. ./obj/split_l.d \  
  23. ./obj/test.d  
  24.   
  25. all: $(OBJS)        # 注意在這個subdir中,這個是終極目標,也就是所有的objs  
  26.   
  27. obj/%.o: ../src/%.c ./obj/%.d    #這里是o文件的依賴規則:注意是.c和.d同時成為依賴,.d文件中是一個目標的所有的依賴文                                 # 件,包括.c和.h文件,所有一旦.h被修改,這個地方也是可以識別的!  
  28.     @echo 'start building $< ...'  
  29.     gcc -O3 -Wall -c -fmessage-length=0 -MMD -MP -MF"$(@:%.o=%.d)" \  
  30. -MT"$(@:%.o=%.d)" -o "$@" "$<"  
  31.     @echo 'Finished building: $< '  
  32.     @echo ''  
  33.   
  34. -include $(C_DEPS) # 注意:這里將所有的.d文件引入!注意,第一次引入時候,沒有.d文件,會尋找.d的生成規則,也就是下面                   # 的,這個.d又是依賴.c文件的,所有,一旦一個.c文件中多了一個頭文件之類,又可以跟新.d,從而,執行                   # 上面的.o生成時候,就能夠實時跟新  
  35.   
  36. ./obj/%.d: ../src/%.c   # 注意:這里為了生成.d  
  37.     @echo 'start building $(notdir $@)...'  
  38.     $(CC) $< $(INCLUDE) -MM -MD -o $@  


好了,上面所有的都分析完了,然后可以make一下,.、ir_tree 看看效果吧~

默認的圖形界面是很簡陋的界面,可以根據需要再安裝GNOME或KDE桌面環境

安裝X圖形界面
#可查詢哪些組件是否已經安裝(可用來對照組件名稱)

yum grouplist

#安裝X圖形界面系統

yum list 列出所有可安裝的軟件包 可以通過 yum grouplist 來查看可能批量安裝哪些列表 比如 #yum groupinstall "DNS Name Server" //安裝 bind 及 bind-chroot 套件

yum groupinstall "X Window System" -y
#安裝GNOME桌面環境
yum groupinstall  "GNOME Desktop Environment" -y
#安裝KDE桌面環境
yum groupinstall "KDE (K Desktop Environment)"


卸載
卸載GNOME桌面環境
yum groupremove GNOME Desktop Environment'
卸載KDE桌面環境
yum groupremove 'K Desktop Environment'

啟動X圖形界面的方法
1、startx
2、設置開機自動啟動,修改/etc/inittab
            id:3:initdefault:      ------>      id:5:initdefault:
3、init 5

默認桌面環境選擇 
  一、設置GNOME或者KDE為默認的啟動桌面環境
  方法1:修改/etc/sysconfig/desktop,根據需要將“DESKTOP”后面的參數設置為KDE或GNOME。
  方法2:在當前用戶目錄下建立“.xinitrc”這個文件,文件的內容就一行startkde或gnome-session。
  二、GNOME和KDE的切換
  1、如果需要切換到GNOME:
  #switchdesk gnome
  2、如果需要切換到KDE:
  #switchdesk kde
  2、如果需要切換到KDE:
  #startkde

圖形界面與字符界面的切換
在LINUX中是有多控制台的,其中前6個是字符界面,第七個是圖形界面。   
如果你需要切換到字符界面,可以使用CTRL+ALT+Fn來實現,其中Fn是F1-F6中的任何一個,
當然如果你是在字符界面之間互相切換就沒必要CTRL了,直接ALT Fn

5)如果在OS啟動時需要啟動圖形化界面,則需要編輯 /etc/inittab 文件的 level 3 換成 level 5

      # vi /etc/inittab

      id:3:initdefault:

 

6)可以在 level 3 和 level 5 之間切換

     # init 3

     # init 5

 

  1. 初次接觸Linux的用戶 

    /swap

    缺點:一旦有磁盤有任何問題,根目錄將整體毀滅。

  2. 初級分配方式 
    /boot = 1G 

    /usr 
    /home 
    /var 
    /tmp 
    /swap = 2倍內存大小(如果內存較大,swap可適當減小)

    • 提示:/boot 要放在整塊硬盤的最前面!(一般分區時,安裝系統會主動將其置於最前方)

    • /boot 存放系統引導文件;(由於BIOS原因,boot只能在1024柱面前,否則會找不到。一般只需要100M,但是很多時候系統升級的時候,該目錄下會保存舊引導文件)

    • / 根目錄;
    • /usr 最龐大的目錄,幾乎所有應用程序、文件都在這里;
    • /home 用戶主目錄;
    • /var 某些大文件溢出區、cache存放、email存放;
    • /tmp 公共的臨時文件;
    • /swap 虛擬內存交換區。

幾種文件類型

    1. ext2/ext3:Linux 適用的文件系統類型。由於ext3文件系統多了日志的記錄, 對於系統的復原比較快速,因此建議務必要選擇新的ext3不要用 ext2 了。
    2. LVM:用來彈性調整文件系統容量的一種機制, 可以讓你的文件系統容量變大或變小而不改變原有的檔案數據內容!
    3. RAID:利用Linux操作系統的特性,用軟件仿真出磁盤陣列的功能!這東西很棒!不過目前暫時還用不到!
    4. swap:只用於操作系統虛擬內存置換,無法用於掛載。
    5. vfat:如果同時存在Windows/Linux操作系統,則可以選擇vfat為虛擬內存置換區。

      • 提示:一般只需要選擇ext3或者swap,ext3一般也叫標准模式。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM