轉載自水木清華 歡迎大家補充 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 -> 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檔案: #i nclude <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__ #i nclude <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等網友提出寶貴意見! |