Linux內核/模塊開發常見問題集【轉】

http://www.dss.cn/Article/embed_system/200511/792.html

 

Linux內核/模塊開發常見問題集(FAQ)

 

 

  轉載自水木清華
  歡迎大家補充

 

1. 請推薦一些好的Linux內核參考書?

2. 源代碼問題
2.1 如何得到某一版本的Linux內核源代碼?
2.2 請問xx命令、xx庫的源碼是哪個文件?
2.3 linux-2.x.x.tar.gz.sign 文件有什麼用途?
2.4 請推薦一些源代碼查看工具?
2.5 內核patch如patch-2.6.3怎麼用?
2.6 如何統計linux內核有多少行代碼?
2.7 xx結構的定義在哪個內核源文件中?
2.8 volatile和__volatile__是什麼意思?
2.9 do{ ... } while(0)是什麼意思?
2.10 list_entry的定義是怎麼回事?
2.11 校內查看linux 內核源代碼地址

3. 模塊編程問題
3.1 模塊編程需要注意什麼?
3.2 爲什麼insmod一個模塊時顯示版本不匹配?
3.3 爲什麼出現Unresolved Symbol錯誤?
3.4 爲什麼出現no license錯誤?
3.5 爲什麼看不到用printk打印的信息?

4. 內核開發問題
4.1 怎麼製作、使用patch文件?
4.2 在內核中可以使用系統調用嗎?
4.3 在內核中怎麼打開並操作一個文件?
4.4 在內核中讀寫文件時爲什麼會出現EFAULT(-14)錯誤?
4.5 怎麼在系統中增加一個自己的系統調用?
4.6 怎麼在內核中加入我自己的驅動程序?
4.7 怎麼通過程序得到cpu和mem使用率?
4.8 如何獲得高精度的系統時間?
4.9 怎麼進行系統性能調諧?
4.10 內核中怎麼進行互斥?

5. 其它問題
5.1 如何學習Linux內核?
5.2 如何下載精華區?
5.3 init進程是核心進程嗎?init與初始進程是不是一回事?
5.4 initrd(.img)有什麼用?

6. 關於本FAQ
7. Changelog


1. 請推薦一些好的Linux內核參考書?
  a.《Linux Device Drivers, 2nd Edition》,有中文譯本
  b.《Understanding the Linux Kernel, 2nd Edition》
  c.《Linux內核源代碼情景分析》,分上下兩冊
  d.《邊幹邊學-Linux內核指導》
  e.《Linux內核2.4版源代碼分析大全》
  f.《Linux Kernel Development》
  g.《IA-64 Linux Kernel: Design and Implementation》
  注:a電子版可在http://www.oreilly.com/catalog/linuxdrive2/ 下載;
      f和g比較新,在國內比較難買到。
    也可以版面查詢文章標題 "Linux內核書籍推薦&介紹"

2. 源代碼問題
2.1 如何得到某一版本的Linux內核源代碼?
   a. http://www.kernel.org ftp://ftp.kernel.org ,這是Linux內核版本的發佈
      網站。
   b. 很多鏡像或本地網站也提供部分Linux內核版本的下載,多用ftp搜索引擎。
      linuxaid.com提供的mirror:
      ftp://ftp.linuxaid.lkams.kernel.org/pub/mirrors/kernel/linux/kernel
   c. 一般的Linux發行版如Redhat之類會隨盤提供相應的內核源代碼,不過這個源代
      碼往往是改動過的,與標準Linux內核有差異。

2.2 請問xx命令、xx庫的源碼是哪個文件?
   a. 一個系統除了內核以外,還需要有shell、gcc等一系列工具和命令以及C庫等一
      系列庫,這些作爲應用程序其源代碼都不在內核中,需要另外下載相應的源代碼。
   b. 對於Redhat系統,可以用rpm -qf命令來查找某一命令所在的軟件包,然後再找
      相應的源代碼包安裝。
   c. gnu.org有很多軟件源代碼如bash/glibc/binutils/make/gcc的源代碼。
   d. 可在http://www.rpmfind.net http://www.google.com 去搜一搜。

2.3 linux-2.x.x.tar.gz.sign 文件有什麼用途?
    這是一個數字簽名文件,用來校驗linux-2.x.x.tar.gz這個文件在簽名後是沒有
    被第三方修改過,更詳細的信息參考http://www.kernel.org/signature.html

2.4 請推薦一些源代碼查看工具?
   a. Windows系統可以用Source Insight,Linux系統可以用Source Navigator。
   b. vim或emacs編輯器,配合cscope、ctags、etags等交叉索引工具。
   c. vim或emacs編輯器,配合grep、egrep等文本搜索工具,不過最好要對源代碼目
      錄結構有所熟悉
   d. LXR,以網頁的形式通過瀏覽器瀏覽,安裝複雜(debian下安裝容易,請版面
搜尋lxr)
    校外:可以直接訪問http://lxr.linux.no/source/ 在線閱讀Linux內核源代碼。
    校內:可以訪問http://10.214.14.127/lxr/http/source/ ,如果需要特定版本
可以聯繫vatano,只有空間足夠就放上去。
   e. GNU global,可以在命令行用,也可以生成hypertext,類似lxr,但更省事。

2.5 內核patch如patch-2.6.3怎麼用?
   a. 內核patch一般是針對前一個版本,如patch-2.6.3是針對2.6.2的內核。
   b. 內核patch一般是和ChangeLog對應,如patch-2.6.3對應於ChangeLog-2.6.3。
   c. 在內核patch中查找Makefile關鍵字可得到相關信息,如在patch-2.6.0中有:
        diff -Nru a/Makefile b/Makefile
        --- a/Makefile  Wed Dec 17 19:00:07 2003
        +++ b/Makefile  Wed Dec 17 19:00:07 2003
        @@ -1,7 +1,7 @@
        VERSION = 2
        PATCHLEVEL = 6
        SUBLEVEL = 0
        -EXTRAVERSION = -test11
        +EXTRAVERSION =
   d. 找到了針對的內核就可以用patch來升級內核了。

2.6 如何統計linux內核有多少行代碼?
   嘗試以下shell命令:
   find /usr/src/linux-2.x.x  -name "*.[chS]" | xargs cat | wc -l

2.7 xx結構的定義在哪個內核源文件中?
   a. 請使用源碼查看工具,見問題2.4。
   b. 如果用grep等文本搜索工具,主要在include/linux和include/asm兩個目錄下
      搜索。

2.8 volatile和__volatile__是什麼意思?
   a. volatile是C語言定義的關鍵字,gcc爲了需要又定義了__volatile__,它和
      volatile表達的是同一意思。
   b. volatile的本意是"易變的",由於訪問寄存器的速度快於訪存,所以編譯器一般
      都會作優化以減少訪存。如果變量加上volatile修飾,則編譯器就不會對此變量
      的讀寫操作進行優化,即不通過寄存器緩衝而直接訪存。
   c. __asm__ __volatile__一起指示編譯器不要改動優化後面的彙編語句。

2.9 do{ ... } while(0)是什麼意思?
   a. 主要是爲了避免宏在不同情況展開可能會出現的一些錯誤。
   b. 在http://www.kernelnewbies.org/faq/ 上有詳細介紹。

2.10 list_entry的定義是怎麼回事?
   a. list_entry的定義在內核源文件include/linux/list.h中:
      #define list_entry(ptr, type, member) /
        ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
   b. 其功能是根據list_head型指針ptr換算成其宿主結構的起始地址,該宿主結構是
      type型的,而ptr在其宿主結構中定義爲member成員。如下圖:

      req-->|type型對象起始地址
            |
            |... ...
      ptr-->|ptr指針所指的member成員地址
            |
            |... ...

     ptr指向圖中所示的位置,通過(unsigned long)(&((type*)0)->member)得到ptr
     和req之間的差值,ptr減去這個差值就得到了type型宿主結構的指針req,返回
     類型爲(type*)。

2.11 校內查看Linux內核源代碼的地址
http://10.214.14.127/lxr/http/source/

3. 模塊編程問題
3.1 模塊編程需要注意什麼?
   a. 在gcc編譯選項中增加-c
   b. 在gcc編譯選項中定義兩個宏:-DMODULE -D__KERENL__
      或直接在源文件中定義這兩個宏:
      #define MODULE
      #define __KERNEL__
   c. 在源文件中包括module.h文件:
      #include <linux/module.h>
   d. 假定你現在運行的內核的源碼目錄絕對路徑是MyKernelSrcPath,在gcc編譯時
      增加選項:
         -I $MyKernelSrcPath/include (如-I /usr/src/linux/include)
      注意MyKernelSrcPath必須是指向與當前運行的系統內核匹配的(版本一致)、
      能編譯成功的(保證源代碼目錄的完整性)、已經config過的(保證有.config和
      include/linux/autoconf.h文件)內核源碼目錄
      注意:通常不要 -I /usr/include/linux
   e. 如果要用inline功能,需要在gcc編譯選項中增加-O2
   f. init_module()函數必須return 0,否則會出現Device or resource busy錯誤。

3.2 爲什麼insmod一個模塊時顯示版本不匹配?
   a. 見上面3.1->d
   b. 某些時候用insmod -f能夠成功加載,但需謹慎使用。

3.3 爲什麼出現Unresolved Symbol錯誤?
   a. 首先查看文件/proc/ksyms,看內核有沒有輸出這個符號,不同的內核版本如
      2.2和2.4輸出的符號會有些變化。
   b. 如果內核輸出的符號帶有版本控制信息如符號printk_R12345678,則性質同
      問題3.2。
   c. 注意:現在有很多版本都不輸出sys_call_table了,另想辦法吧!

3.4 爲什麼出現no license錯誤?
   在源文件加入下面一行(加在文件頭部,尾部均可):
   MODULE_LICENSE("GPL");

3.5 爲什麼看不到用printk打印的信息?
   a. 打印消息受級別的限制,消息級別可以通過printk設置,如:
        printk("<n>something");  /* 其中0<=n<=7 */
      假設控制檯的消息級別爲m, 當n<m時消息打印到控制檯,否則不打印。
      這樣一方面可以提高要打印消息本身的級別(數字越小級別越高),
      另一方面可以改變控制檯的消息級別(可從1到8),如改爲8可用以下命令:
       # echo "8" > /proc/sys/kernel/printk
   b. 用dmesg命令看。
   c. 當系統運行klogd和syslogd時,內核消息就會由klogd分發到syslogd,
      syslogd會根據配置文件/etc/syslog.conf作相應處理,具體可以查看syslogd
      和syslog.conf的man頁。


4. 內核開發問題
4.1 怎麼製作、使用patch文件?
   a. patch文件是由diff命令生成的,使用patch文件用patch命令,具體可查看diff
      和patch的man頁和info。
   b. diff命令的常用選項組合是urN,如:
        diff -urN linux/ my_linux/ >mypatch.diff

4.2 在內核中可以使用系統調用嗎?
   a. 可以。內核源代碼中就有使用系統調用的例子,如open()、execve()等。
   b. 在內核中使用系統調用必須要在源文件中包括以下兩行:
      #define __KERNEL_SYSCALLS__
      #include <linux/unistd.h>
   c. 內核中使用系統調用的相關定義可查看文件include/asm/unistd.h。
      如果要用的系統調用該文件中沒有定義,可以按照其格式自行添加。
   d. 如果要在模塊中使用系統調用,必須要自己定義errno如:
      int errno;
      內核在lib/errno.c中定義了errno,但該符號不導出,所以模塊編程時需要自己
      定義errno,用以存放系統調用出錯號。

4.3 在內核中怎麼打開並操作一個文件?
   a. 直接用open()、read()等系統調用,見問題4.2。
   b. 用filp_open()函數打開文件,得到struct file *的指針fp。
      使用指針fp進行相應操作,如讀文件可以用fp->f_ops->read。
      最後用filp_close()函數關閉文件。
      filp_open()、filp_close()函數在fs/open.c定義,在include/linux/fs.h中
      聲明。
   c. 自己寫包裝函數,可參照文件fs/exec.c中的open_exec()和kernel_read()函數。
      在http://www.linuxforum.net/forum/showflat.php?Cat=&Board=linuxK
       &Number=363455&page=&view=&sb=&o=&vc=1上有些代碼可以參照。

4.4 在內核中讀寫文件時爲什麼會出現EFAULT(-14)錯誤?
   a. 內核文件系統提供的read()和write()之類的函數,期望是對用戶態程序服務的,
      所以它會驗證讀寫緩衝區不超過用戶空間的上限即0xC000 0000。但現在內核中
      要讀寫文件,緩衝區在內核中即地址會超過0xC000 0000。
   b. 在讀寫文件前先得到當前fs:mm_segment_t old_fs=get_fs();
      並設置當前fs爲內核fs:set_fs(KERNEL_DS);
      在讀寫文件後再恢復原先fs: set_fs(old_fs);
      set_fs()、get_fs()等相關宏在文件include/asm/uaccess.h中定義。

4.5 怎麼在系統中增加一個自己的系統調用?
    去http://www.linuxaid.com.cn/engineer/ideal/kernel/new_syscall.htm
    和http://www.xenotime.net/linux/syscall_ex/ 看看。

4.6 怎麼在內核中加入自己的驅動程序?
   a. 去http://www-900.ibm.com/developerWorks/cn/linux/kernel/l-kerconf/
      index.shtml看看,瞭解一下整個內核的配置編譯系統。
   b. 在相應位置建立自己的源碼目錄、文件、Makefile等。
   c. 修改上層Makefile,把自己的程序加入到內核編譯系統中。
   d. 修改上層Config.in,把自己的程序加入到內核配置系統中。
   e. 確保自己的初始化函數被調用。有兩種方法,一是顯式調用,即在原來的系統
     初始化函數中直接加入對自己的調用,如字符設備就在drivers/char/mem.c中的
     chr_dev_init()函數中加入,塊設備就在drivers/block/ll_rw_blk.c中的
     blk_dev_init()函數中加入。另一種方法是用initcall,用宏module_init來申
     明你的初始化函數,操作系統在初始化到一定階段後會自動通過init/main.c中
     的do_initcalls()函數來統一調用這些初始化函數。module_init宏在文件
     include/linux/init.h中定義。

4.7 怎麼通過程序得到cpu和mem使用率?
   a. 這些信息的最終來源都是/proc目錄下的文件,如/proc/stat等。
   b. procps包下的命令如top、vmstat等實現了這些功能,可以參照其源代碼。
   c. procps包可從Redhat發行版中得到,也可從http://www.surriel.com/procps/
     處獲得。

4.8 如何獲得高精度的系統時間?
   a. Linux中jiffy是時鐘的基本單位,對於一般的系統來說配置成10ms。大多數時
     鐘相關的系統調用都是基於jiffy,所以精度不會太高。
   b. 可以考慮使用TSC(time stamp counter)、rtc(real time clock)等寄存器來獲得
     高精度時鐘,具體可查看相關的硬件手冊。

4.9 怎麼進行系統性能調諧?
   a. IBM developworks:
        http://www-900.ibm.com/developerWorks/cn/linux/l-kperf/index.shtml
        http://www-900.ibm.com/developerWorks/cn/linux/management/tune/index.sht
ml
   b. Linux Performance Tuning項目:http://linuxperf.nl.linux.org/
   c. http://www.fixdown.com/article/article/724.htm

4.10 內核中怎麼進行互斥?
    a. Linux內核中有兩種機制實現互斥:semaphore和spinlock。semaphore是讓進
      程睡眠等待資源,這一般假設無法預測資源什麼時候可以獲得;spin_lock一般
      用在SMP中,它假設所等待的資源馬上就會被釋放,所以循環等待資源。
      semaphore只能用於非中斷環境(典型的中斷環境過程包括象timer之類的中斷
      服務程序,softirq等)的進程間互斥,spinlock可以用於所有的進程間包括不同
      cpu的進程間的互斥,spinlock主要用於保護短小的臨界區,使用時必須要特別注
      意死鎖問題。
    b. semaphore是通過進程調度來實現互斥的。進程請求獲取semaphore時,如果
      semaphore空閒則該進程獲得semaphore,設置標誌並返回;如果semaphore忙
      (其它用戶已經獲得semaphore)則系統構建等待隊列並通過進程調度機制讓本進
      程睡眠。進程釋放semaphore時,系統按一定規則通過等待隊列喚醒一個睡眠進
      程。對semaphore可執行up()和down()操作,詳見include/asm/semaphore.h文件。
    c. spinlock主要是爲SMP互斥而引入的。在請求獲取spinlock時,如果空閒則獲得
      spinlock,設置標誌並返回。如果spinlock已經被其它用戶獲得而處於忙狀態,
      系統就會一直佔用CPU資源,不停查詢spinlock的狀態直到獲得spinlock。


5. 其它問題
5.1 如何學習Linux內核?
    請先閱讀本版精華區內核學習目錄的相關文章。

5.2 如何下載精華區?
   a. 除了88提供的下載,還可以通過腳本下載-_-

5.3 init進程是核心進程嗎?init與初始進程是不是一回事?
    Linux操作系統在系統初始化之初就捏造了一個原始進程(原始進程在系統初始化
   完畢後就演化成idle進程),當系統初始化進行到一定階段,原始進程會創建(通
   過kernel_thread()函數)出來init進程,init進程繼續進行系統初始化工作並在最
   後執行execve("/sbin/init",...),這樣init就從原來的核心進程搖身一變成用戶
   進程(用戶程序/sbin/init)了。init進程的pid爲1,原始進程(idle進程)的
   pid爲0。所有其它的進程都由init進程派生,用ps或pstree命令可以看到這一點。

5.4 initrd(.img)有什麼用?
   a. initrd(.img)是一個文件系統映像,裏面一般包含一些特殊的硬件模塊尤其是存
     儲設備如scsi/raid/ext3模塊,以便在保持內核足夠小的同時又支持儘可能多的硬
     件設備,常被安裝程序使用。
     initrd(.img)也不是必需的,只要必要的模塊編譯進內核就可以不用initrd(.img)。
   b. 在使用了initrd(.img)時,系統引導的大致過程如下:
    1)Loader程序(如lilo和grub)加載內核和initrd(.img)
    2)內核解壓縮initrd(.img)爲正常的RAM盤文件系統並掛接爲根分區
    3)執行linuxrc,在此過程中會加載硬件模塊
    4)在linuxrc終止後,真正的根文件系統被掛接
    5)在根文件系統上完成正常的引導過程。對於正常的系統而言,執行/sbin/init,
      這時控制就會轉到正常的大家所熟知的啓動過程。而對於安裝程序,只需將控制
      轉到安裝過程的第一階段,由它完成後續的安裝環境的加載,設備的進一步初始
      化等操作。
   c. 要使用initrd(.img)首先內核必須配置成支持initrd:
        CONFIG_BLK_DEV_RAM=y
        CONFIG_BLK_DEV_INITRD=y
      其次要在Loader腳本中增加相應指示。如在grub.conf中增加一行:
        initrd /boot/initrd-2.4.20.img
   d. 可用mkinitrd命令創建initrd(.img)文件:
        mkinitrd  imagefilename  kernelversion
      如對於2.4.20的內核可以:
        mkinitrd /boot/initrd-2.4.20.img 2.4.20
   e. 具體可查看Documentation/initrd.txt和man mkiinitrd。mkinitrd命令執行的詳
      細過程可以直接查看/sbin/mkinitrd(shell腳本)文件。


6. 關於本FAQ
   本FAQ主要根據本版以前的文章整理而成。
   特別感謝mada、pepp等網友提出寶貴意見!


7. Changelog
   2004/05/10 junky
   --發佈FAQ 1.0
   --調整了目錄結構
   --增加了Q2.5,Q2.6,Q5.1,Q5.2,Q5.4
   --Q1增加了2本新書
   --Q2.2增加了gnu.org的鏈接
   --Q3.1增加了init_module()返回0
   --Q3.2增加了insmod -f
   --Q4.2增加了errno的說明
   --Q4.9改名並增加了IBM developworks的鏈接

   2004/07/07 junky
   --刪除Q2.11

   --增加Q2.11
   --修改Q1, Q2.4, Q5.2
   2004/12/15 vatano

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章