Linux內(nèi)核入門,包教會(huì)
原創(chuàng)【51CTO精選譯文】這年頭,Linux成了一個(gè)時(shí)髦詞。自詡對(duì)電腦玩的精通的學(xué)生和IT人士們,沒有哪個(gè)不在自己的電腦上安裝一、兩個(gè)Linux,并自覺趕上了時(shí)髦。然而,在Ubuntu或SUSE的論壇中,經(jīng)常有這樣的對(duì)話:
“你學(xué)Linux學(xué)了這么久,都學(xué)到了什么?”
“哦,我現(xiàn)在Linux的安裝、升級(jí)、桌面美化都很熟練!你看我這是最新版的Ubuntu,桌面很漂亮吧!”
“……”
Linux社區(qū)中有一句名言:如果你進(jìn)入你的操作系統(tǒng)不知道該做什么,那最好還是關(guān)掉電腦,一定有更重要的事等著你去做。說真的,如果對(duì)Linux命令不熟練,真的不能算是學(xué)過Linux。然而另一方面,Linux內(nèi)核雖然是一般用戶可學(xué)可不學(xué)的內(nèi)容,但可以說卻是Linux操作系統(tǒng)中最好玩的部分。尤其對(duì)于開發(fā)者而言,Linux內(nèi)核開發(fā)絕對(duì)是最理想的磨練場所。51CTO編輯一直認(rèn)為,國外之所以IT技術(shù)大拿林立,和他們從小接觸類UNIX系統(tǒng)、把玩內(nèi)核開發(fā)是脫不了關(guān)系的。
下面是Linux內(nèi)核開發(fā)者Robert Love寫的一篇入門文章,號(hào)稱“包教會(huì)”,推薦對(duì)Linux內(nèi)核開發(fā)感興趣的學(xué)生、Linux愛好者、開發(fā)者以及系統(tǒng)管理員們一定不要錯(cuò)過。當(dāng)然,雖然標(biāo)題說是包教會(huì),你可能需要一定的Linux命令以及C語言的基礎(chǔ)。
以下是正文內(nèi)容:
Linux內(nèi)核一直都被視為學(xué)習(xí)Linux最難的一塊,相信大家也一定看過不少關(guān)于內(nèi)核的文章,但捫心自問,你現(xiàn)在究竟掌握了多少?本文將從零開始介紹被視為高深的Linux內(nèi)核,內(nèi)容涉及內(nèi)核源代碼的下載,編譯,安裝,以及內(nèi)核開發(fā)相關(guān)的內(nèi)容。
如何獲取Linux內(nèi)核源代碼
下載Linux內(nèi)核當(dāng)然要去官方網(wǎng)站了,網(wǎng)站提供了兩種文件下載,一種是完整的Linux內(nèi)核,另一種是內(nèi)核增量補(bǔ)丁,它們都是tar歸檔壓縮包。除非你有特別的原因需要使用舊版本的Linux內(nèi)核,否則你應(yīng)該總是升級(jí)到最新版本。
使用Git
由Linus領(lǐng)頭的內(nèi)核開發(fā)隊(duì)伍從幾年前就開始使用Git版本控制系統(tǒng)管理Linux內(nèi)核了(參考閱讀:什么是Git?),而Git項(xiàng)目本身也是由Linus創(chuàng)建的,它和傳統(tǒng)的CVS不一樣,Git是分布式的,因此它的用法和工作流程很多開發(fā)人員可能會(huì)感到很陌生,但我強(qiáng)烈建議使用Git下載和管理Linux內(nèi)核源代碼。
你可以使用下面的Git命令獲取Linus內(nèi)核代碼樹的最新“推送”版本:
$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
然后使用下面的命令將你的代碼樹與Linus的代碼樹最新狀態(tài)同步:
$ git pull
安裝內(nèi)核源代碼
內(nèi)核包有GNU zip(gzip)和bzip2格式。Bzip2是默認(rèn)和首選格式,因?yàn)樗膲嚎s比通常比gzip更好,bzip2格式的Linux內(nèi)核包一般采用linux-x.y.z.tar.bz2形式的文件名,這里的x.y.z是內(nèi)核源代碼的具體版本號(hào),下載到源代碼包后,解壓和抽取就很簡單了,如果你下載的是bzip2包,運(yùn)行:
$ tar xvjf linux-x.y.z.tar.bz2
如果你下載的是gzip包,則運(yùn)行:
$ tar xvzf linux-x.y.z.tar.gz
無論執(zhí)行上面哪一個(gè)命令,最后都會(huì)將源代碼解壓和抽取到linux-x.y.z目錄下,如果你使用Git下載和管理內(nèi)核源代碼,你不需要下載tar包,只需要運(yùn)行g(shù)it clone命令,它就會(huì)自動(dòng)下載和解壓。
內(nèi)核源代碼通常都會(huì)安裝到/usr/src/linux下,但在開發(fā)的時(shí)候最好不要使用這個(gè)源代碼樹,因?yàn)獒槍?duì)你的C庫編譯的內(nèi)核版本通常也鏈接到這里的。
應(yīng)用補(bǔ)丁
Linux內(nèi)核開發(fā)人員會(huì)將自己的修改做成補(bǔ)丁與其它人員分享,而且補(bǔ)丁是增量的,增量補(bǔ)丁是從一個(gè)內(nèi)核樹移動(dòng)到另一個(gè)內(nèi)核樹的有效方法,不用下載完整的內(nèi)核包就可以升級(jí)內(nèi)核,不僅可節(jié)省帶寬,也節(jié)省了內(nèi)核升級(jí)時(shí)間,應(yīng)用補(bǔ)丁之前先進(jìn)入內(nèi)核源代碼樹所在目錄,然后運(yùn)行:
$ patch –p1 < ../patch-x.y.z
注意,補(bǔ)丁包也有明確的版本號(hào),這里的版本號(hào)與Linux內(nèi)核源代碼的版本號(hào)要一致,內(nèi)核和補(bǔ)丁版本號(hào)不一致時(shí),強(qiáng)制應(yīng)用補(bǔ)丁會(huì)引起意想不到的后果。
內(nèi)核源代碼樹介紹
內(nèi)核源代碼樹分為許多目錄,它們下面又包含許多子目錄,源代碼樹的頂級(jí)目錄及其描述參見下表。
目錄 | 描述 |
arch | 特定架構(gòu)的源代碼 |
block | 塊I/O層 |
crypto | 加密API |
Documentation | 內(nèi)核源代碼文檔 |
drivers | 設(shè)備驅(qū)動(dòng) |
firmware | 使用某個(gè)驅(qū)動(dòng)需要的設(shè)備固件 |
fs | VFS和獨(dú)立文件系統(tǒng) |
include | 內(nèi)核頭 |
init | 內(nèi)核啟動(dòng)和初始化 |
ipc | 進(jìn)程間通信 |
kernel | 核心子系統(tǒng),如調(diào)度器 |
lib | 助手例行程序 |
mm | 內(nèi)存管理子系統(tǒng)和VM |
net | 網(wǎng)絡(luò)子系統(tǒng) |
samples | 示例,示范代碼 |
scripts | 用于生成內(nèi)核的腳本 |
security | Linux安全模塊 |
sound | 聲音子系統(tǒng) |
usr | 早期的用戶空間代碼(叫做initramfs) |
tools | 輔助Linux開發(fā)的工具 |
virt | 虛擬化基礎(chǔ)設(shè)施 |
在源代碼樹的根目錄下還有很多文件需要說明,COPYING是內(nèi)核許可描述文件(即GNU GPL v2),CREDITS是參與Linux內(nèi)核的開發(fā)人員名單,MAINTAINERS列出了維護(hù)各個(gè)子系統(tǒng)和驅(qū)動(dòng)的個(gè)人,Makefile是內(nèi)核Makefile的基礎(chǔ)。
#p#
生成內(nèi)核
生成內(nèi)核其實(shí)很簡單,甚至比編譯和安裝其它系統(tǒng)級(jí)組件,如glibc還要簡單,從2.6版本開始,Linux內(nèi)核引入了一個(gè)新的配置和生成系統(tǒng),它使生產(chǎn)內(nèi)核的操作變得更加簡單了。
配置內(nèi)核
既然已經(jīng)拿到內(nèi)核源代碼,那我們在開始編譯前就可以根據(jù)需要自行配置和定制,可以編譯你指定的功能和想要的驅(qū)動(dòng),配置內(nèi)核是生成內(nèi)核必須的一步,因?yàn)閮?nèi)核提供了大量的功能,支持各種不同的硬件,有很多都需要配置,內(nèi)核配置是由配置選項(xiàng)控制的,配置選項(xiàng)都有CONFIG前綴,例如,對(duì)稱多處理(SMP)是由CONFIG_SMP配置選項(xiàng)配置的,如果設(shè)置了這個(gè)選項(xiàng),SMP就被啟用了,反之則被禁用,配置選項(xiàng)可以確定會(huì)生成哪個(gè)文件,也可以通過預(yù)處理指令操控代碼。
配置選項(xiàng)可以控制生成過程要么是布爾型,要么是三態(tài)型,布爾型就是“是”或“否”,大部分內(nèi)核配置選項(xiàng)都屬于布爾型,如CONFIG_PREEMPT,而三態(tài)型則在“是”和“否”的基礎(chǔ)上,又增加一個(gè)“模塊”選項(xiàng),模塊選項(xiàng)表示配置選項(xiàng)被設(shè)置了,但最后會(huì)編譯成模塊,而不是直接編譯進(jìn)內(nèi)核,模塊可以理解為可獨(dú)立動(dòng)態(tài)載入的對(duì)象,一般來說,驅(qū)動(dòng)配置通常都是三態(tài)型。
配置選項(xiàng)也可以是字符串或整數(shù),這樣的選項(xiàng)不會(huì)控制生成過程,指定的值由內(nèi)核源代碼訪問預(yù)處理宏時(shí)使用,例如,可以為某個(gè)配置選項(xiàng)指定靜態(tài)分配數(shù)組的大小。
Linux廠商也會(huì)隨發(fā)行版提供預(yù)編譯的內(nèi)核,如Canonical為Ubuntu,或Red Hat為Fedora提供的內(nèi)核,這樣的內(nèi)核通常只啟用了需要的內(nèi)核功能,幾乎所有驅(qū)動(dòng)都被編譯成模塊了,這樣的內(nèi)核提供了一個(gè)良好的基礎(chǔ)內(nèi)核和廣泛的硬件模塊支持,無論如何,想要成為內(nèi)核高手,你應(yīng)該編譯自己的內(nèi)核。
值得慶幸的是,內(nèi)核提供了很多工具簡化配置 ,最簡單的工具是基于文本命令行的實(shí)用程序,如:
$ make config
這個(gè)工具會(huì)一個(gè)選項(xiàng)一個(gè)選項(xiàng)地配置,但用戶需要參與,如指定“是(y)”,“否(m)”還是“模塊(m)”,整個(gè)配置過程需要很長的時(shí)間,因此,除非是有人按小時(shí)計(jì)費(fèi)請(qǐng)你升級(jí)內(nèi)核,實(shí)在找不出別的理由用這種最原始的方法配置內(nèi)核了,相反,有現(xiàn)成的基于ncurses的圖形化工具可以代替。
$ make menuconfig
或是基于gtk+的圖形化工具
$ make gconfig
上述三個(gè)工具都將配置選項(xiàng)分成多個(gè)類別,如“處理器類型和特征”,你可以在這些類別上來回移動(dòng),查看內(nèi)核選項(xiàng),當(dāng)然也可以修改它們的設(shè)置了。
下面這個(gè)命令會(huì)根據(jù)你的架構(gòu)創(chuàng)建一個(gè)默認(rèn)的配置基礎(chǔ)。
$ make defconfig
雖然默認(rèn)配置有些武斷(在i386上,默認(rèn)配置是由Linus配置的),但如果你從未配置過內(nèi)核,它提供了一個(gè)良好的開端。
配置選項(xiàng)存儲(chǔ)在源代碼樹根目錄下一個(gè)名叫.config的文件中,你可以打開這個(gè)文件手工編輯其中的配置選項(xiàng),修改后或要在新的內(nèi)核源代碼樹上應(yīng)用現(xiàn)有配置文件,你可以使用下面的命令驗(yàn)證和更新配置:
$ make oldconfig
在生成內(nèi)核之前必須運(yùn)行這個(gè)命令。
配置選項(xiàng)CONFIG_IKCONFIG_PROC指定了完整的內(nèi)核配置文件壓縮包位置,默認(rèn)是/proc/config.gz,這樣在生成新內(nèi)核時(shí)要克隆現(xiàn)有的配置就變得非常簡單了。如果你當(dāng)前的內(nèi)核開啟了這個(gè)選項(xiàng),你可以從/proc拷貝該配置文件,然后在此基礎(chǔ)上生成新的內(nèi)核:
$ zcat /proc/config.gz > .config $ make oldconfig
內(nèi)核配置好后,使用下面的命令進(jìn)行生成:
$ make
和2.6以前的內(nèi)核不一樣,在生成內(nèi)核前不再需要執(zhí)行make dep命令了,依賴樹會(huì)自動(dòng)維護(hù),也不需要再指定特定的生成類型,如bzImage,或獨(dú)立生成模塊,默認(rèn)Makefile規(guī)則會(huì)自動(dòng)處理好一切。
將干擾信息最小化
在生成過程中會(huì)遭到警告和錯(cuò)誤的干擾。最小化干擾信息的一個(gè)訣竅是重定向make的輸出,但仍然會(huì)看到一些警告和錯(cuò)誤:
$ make > ../detritus
如果你想查看生成輸出,你可以事后閱讀這個(gè)文件,如果你完全不想看到任何輸出,那么就重定向到/dev/null:
$ make > /dev/null
同時(shí)執(zhí)行多個(gè)生成作業(yè)
Make命令提供了一個(gè)功能可以將生成過程拆分成多個(gè)平行的作業(yè),這些作業(yè)可以獨(dú)立運(yùn)行,也可以并行運(yùn)行,在多處理器系統(tǒng)上可以極大地提高生成速度,也提高了處理器利用率,因?yàn)樯纱笮驮创a樹會(huì)出現(xiàn)大量的I/O等待時(shí)間。
默認(rèn)情況下,make只能拆分成一個(gè)作業(yè),因?yàn)镸akefiles常常會(huì)包含不正確的依賴信息,如果真是這樣,多個(gè)并行執(zhí)行的作業(yè)將會(huì)引起混亂,最終會(huì)導(dǎo)致生成過程失敗,如果Makefiles中的依賴信息無誤,那么完全可以拆分成多個(gè)作業(yè)執(zhí)行,如:
$ make –jn
這里的n表示拆分的作業(yè)數(shù)量,通常按每個(gè)處理器拆分成1-2個(gè)作業(yè),例如,在一個(gè)16核心的機(jī)器上 ,你可以運(yùn)行:
$ make -j32 > /dev/null
使用distcc或ccache等優(yōu)秀的工具也可以大大提高生成速度。
#p#
安裝新內(nèi)核
內(nèi)核生成好之后,你需要安裝它,如何安裝于系統(tǒng)架構(gòu)和引導(dǎo)加載程序有關(guān),我們以x86架構(gòu),grub引導(dǎo)加載程序?yàn)槔M(jìn)行說明。
首先將arch/i386/boot/bzImage拷貝到/boot,重命名為vmlinuz- version,這里的version也是版本號(hào),然后編輯/boot/grub/grub.conf,為新內(nèi)核添加相應(yīng)的項(xiàng)目,如果是使用LILO引導(dǎo)裝載程序,則修改/etc/lilo.conf文件,然后運(yùn)行l(wèi)ilo。
模塊的安裝與系統(tǒng)架構(gòu)無關(guān),都是自動(dòng)完成的,以root用戶運(yùn)行:
% make modules_install
這個(gè)命令會(huì)將所有編譯好的模塊安裝到/lib/modules下對(duì)應(yīng)的子目錄中。
生成過程會(huì)在源代碼樹根目錄下創(chuàng)建一個(gè)System.map文件,它包含一個(gè)符號(hào)查找表,映射內(nèi)核符號(hào)到它們的起始地址,在調(diào)試期間可以用它將內(nèi)存地址轉(zhuǎn)換成函數(shù)和變量名。
可能會(huì)遇到的問題
與普通用戶空間的應(yīng)用程序相比,Linux內(nèi)核有多個(gè)特殊的屬性,下面是我認(rèn)為最重要的一些不同:
◆內(nèi)核既不訪問C庫也不訪問標(biāo)準(zhǔn)C頭;
◆內(nèi)核是用GNU C編碼的;
◆內(nèi)核缺少用戶空間提供的內(nèi)存保護(hù);
◆內(nèi)核不能容易地執(zhí)行浮點(diǎn)運(yùn)算;
◆內(nèi)核有一個(gè)小型的固定大小的進(jìn)程堆棧;
◆由于內(nèi)核支持異步中斷和SMP,因此同步和并發(fā)是內(nèi)核主要擔(dān)心的問題;
◆可移植性也很重要。
下面我們就逐個(gè)來了解一下這些問題,所有內(nèi)核開發(fā)人員都必須記住它們。
#p#
無libc或標(biāo)準(zhǔn)頭
和用戶空間應(yīng)用程序不一樣,內(nèi)核并沒有鏈接到標(biāo)準(zhǔn)的C庫,也沒有鏈接到任何其它的庫,這樣設(shè)計(jì)的原因有很多,包括如先有雞還是先有蛋的問題,但主要原因還是速度和內(nèi)核大小,不要說完整的C庫,就是它的一個(gè)子集也夠大,內(nèi)核太大只會(huì)導(dǎo)致效率低下。
不要擔(dān)心,許多常用的libc函數(shù)都在內(nèi)核中實(shí)現(xiàn)了,例如,常見的字符串操作函數(shù)就位于lib/string.c中,只需要包括它的頭文件<linux/string.h >就可以了。
這里的頭文件指的是內(nèi)核源代碼樹中的頭文件,內(nèi)核也只能使用樹內(nèi)的頭文件,基礎(chǔ)文件位于源代碼根目錄的include/目錄下,例如,<linux/inotify.h>頭文件就位于include/linux/inotify.h。
與架構(gòu)相關(guān)的頭文件則位于arch/<architecture>/include/asm,例如,如果在x86架構(gòu)下編譯,與你架構(gòu)相關(guān)的文件就是arch/x86/include/asm,只需要在引用這些頭的地方加上asm/前綴即可,如<asm/ioctl.h>。
漏掉的大部分都是類似printf()這樣的函數(shù),內(nèi)核不會(huì)使用printf(),但它提供了printk()函數(shù),其表現(xiàn)絕不比printf()差,printk()會(huì)拷貝格式化的字符串到內(nèi)核日志緩沖區(qū),syslog程序就是從這里讀取信息的,其用法也和printf()類似:
printk("Hello world! A string '%s' and an integer '%d'\n", str, i);
printf()和printk()之間最大的不同是,printk()允許你指定一個(gè)優(yōu)先級(jí)標(biāo)記,syslogd使用這個(gè)標(biāo)記確定在哪里顯示內(nèi)核消息,下面是一個(gè)使用優(yōu)先級(jí)標(biāo)記的示例:
printk(KERN_ERR "this is an error!\n");
注意在KERN_ERR和打印的消息之間沒有逗號(hào),這是故意這么設(shè)計(jì)的,優(yōu)先級(jí)使用一個(gè)預(yù)定義的字符定義,在編譯期間它與打印的信息是串聯(lián)的。
GNU C
和許多Unix內(nèi)核類似,Linux內(nèi)核也是用C編寫的,但也許會(huì)讓人很意外,內(nèi)核不是用嚴(yán)謹(jǐn)?shù)腁NSI C編寫的,內(nèi)核開發(fā)人員用的卻是gcc(GNU編譯器集,包含了編譯內(nèi)核和Linux C程序的C編譯器)中的各種語言擴(kuò)展。
內(nèi)核開發(fā)人員同時(shí)使用了C語言的ISO C99和GNU C擴(kuò)展,這些變化讓Linux內(nèi)核與gcc結(jié)合得更緊密,但最近又出現(xiàn)了一個(gè)編譯器 – 英特爾的C編譯器 – 也對(duì)gcc的功能支持得相當(dāng)好,因此也可以用它來編譯Linux內(nèi)核。最低支持的gcc版本是3.2,建議采用gcc 4.4或更高的版本編譯。使用ISO C99擴(kuò)展也是可以的,因?yàn)镃99是C語言的官方版本。
內(nèi)聯(lián)函數(shù)
C99和GNU C都支持內(nèi)聯(lián)函數(shù),內(nèi)聯(lián)函數(shù)是直接插入到每個(gè)函數(shù)調(diào)用的位置的,消除了函數(shù)調(diào)用和返回的開銷,允許進(jìn)一步優(yōu)化,因?yàn)榫幾g器可以同時(shí)優(yōu)化調(diào)用者和被調(diào)用函數(shù),但它也有缺點(diǎn),代碼大小會(huì)增加,因?yàn)楹瘮?shù)的內(nèi)容被直接復(fù)制到所調(diào)用者內(nèi)部了,因此也會(huì)增加內(nèi)存消耗和指令緩存空間。內(nèi)核開發(fā)人員一般在小型時(shí)間很關(guān)鍵的函數(shù)中才會(huì)使用內(nèi)聯(lián)函數(shù)。
定義函數(shù)時(shí),使用static和inline關(guān)鍵字聲明內(nèi)聯(lián)函數(shù),例如:
static inline void wolf(unsigned long tail_size)
函數(shù)必須先聲明后使用,否則編譯器就不能使函數(shù)內(nèi)聯(lián),一般做法是將內(nèi)聯(lián)函數(shù)放在頭文件中,因?yàn)樗鼈儽粯?biāo)記為static,不會(huì)創(chuàng)建輸出函數(shù),如果內(nèi)聯(lián)函數(shù)僅在一個(gè)文件中使用,可以放在該文件的頂部。
在內(nèi)核中,與復(fù)雜的宏相比,出于安全和可讀性方面考慮,內(nèi)聯(lián)函數(shù)是首選。
內(nèi)聯(lián)匯編
Gcc C編譯器允許在C函數(shù)中嵌入?yún)R編指令,asm()編譯器指令用于內(nèi)聯(lián)匯編代碼,例如,這個(gè)內(nèi)聯(lián)匯編指令執(zhí)行x86處理器的rdtsc指令,返回時(shí)間戳寄存器(tsc)的值:
unsigned int low, high;
asm volatile("rdtsc" : "=a" (low), "=d" (high));
/* low and high now contain the lower and upper 32-bits of the 64-bit tsc */
Linux內(nèi)核是用C和匯編語言混合編寫的,與底層硬件相關(guān)的代碼很多都是用匯編語言寫的,剩下的大部分內(nèi)核代碼都是直接用C編寫的。
分支注解
Gcc C編譯器內(nèi)置了一個(gè)指令優(yōu)化條件分支,內(nèi)核將這個(gè)打包成易于使用的宏 - likely()和unlikely()。
先看下面這樣的if語句:
if (error) { /* ... */ }
將這個(gè)分支標(biāo)記為非常不可能采用
/* we predict 'error' is nearly always zero ... */ if (unlikely(error)) { /* ... */ }
相反,將這個(gè)分支標(biāo)記為非??赡懿捎?/p>
/* we predict 'success' is nearly always nonzero ... */ if (likely(success)) { /* ... */ }
當(dāng)分支指令已經(jīng)知道一個(gè)優(yōu)先級(jí),或你想在一種情況下優(yōu)化另一種情況時(shí)應(yīng)該使用上述指令,最重要的是,當(dāng)分支正確標(biāo)記時(shí),這些指令會(huì)提升性能,但如果分支標(biāo)記錯(cuò)誤則會(huì)降低性能,在內(nèi)核代碼中,unlikely()要使用得更多,因?yàn)閕f語句傾向于表示一種特殊情況。
無內(nèi)存保護(hù)
當(dāng)用戶空間的應(yīng)用程序嘗試一個(gè)非法的內(nèi)存訪問時(shí),內(nèi)核可以捕捉到錯(cuò)誤,發(fā)送SIGSEGV信號(hào),殺掉進(jìn)程,如果內(nèi)核嘗試一個(gè)非法的內(nèi)存訪問時(shí),結(jié)果就不受控制了,因?yàn)檎l也無法去控制內(nèi)核,這也是內(nèi)核最主要的失誤。
此外,內(nèi)核內(nèi)存也是不可分頁的,因此你消耗的每個(gè)內(nèi)存字節(jié)都比物理內(nèi)存的一個(gè)字節(jié)要少。
不能(容易)使用浮點(diǎn)數(shù)
當(dāng)用戶空間進(jìn)程使用浮點(diǎn)指令時(shí),內(nèi)核要負(fù)責(zé)處理從整型到浮點(diǎn)模式的轉(zhuǎn)換。
與用戶空間不一樣,內(nèi)核不能無縫支持浮點(diǎn)數(shù),因?yàn)樗约翰荒茌p易地捕捉到自己,在內(nèi)核中使用浮點(diǎn)數(shù)需要手動(dòng)保存和恢復(fù)浮點(diǎn)數(shù)寄存器,因此除非卻有必要,否則盡量不要在內(nèi)核中做浮點(diǎn)運(yùn)算。
小型,固定大小的堆棧
用戶空間可以靜態(tài)分配許多不同的堆棧,包括巨型結(jié)構(gòu)和千元數(shù)組,這個(gè)行為是合法的,因?yàn)橛脩艨臻g有很大的堆棧,并可以動(dòng)態(tài)增長。
內(nèi)核堆棧不大也不是動(dòng)態(tài)的,相反,它很小且是固定的,內(nèi)核堆棧的精確大小根據(jù)架構(gòu)有所不同,在x86上,堆棧大小是在編譯時(shí)確定的,一般是4KB或8KB,歷史上,內(nèi)核堆棧有2頁,通常表示它處于32位架構(gòu)上,大小是8KB,如果是16KB就表示是64位架構(gòu),總之大小是固定的,每個(gè)進(jìn)程接收它自己的堆棧。
同步和并發(fā)
內(nèi)核最容易受競爭條件影響,和一個(gè)單線程的用戶空間應(yīng)用程序不一樣,有許多內(nèi)核特性允許同時(shí)訪問共享資源,因此需要同步以防止競爭,特別是:
◆Linux是一種搶占式多任務(wù)操作系統(tǒng),進(jìn)程是由內(nèi)核的進(jìn)程調(diào)度器隨意調(diào)度和再次調(diào)度的,內(nèi)核必須在這些任務(wù)之間同步;
◆Linux支持對(duì)稱多處理(SMP),因此,如果沒有適當(dāng)?shù)谋Wo(hù),在兩個(gè)或多個(gè)處理器上同時(shí)執(zhí)行的內(nèi)核代碼可能會(huì)同時(shí)訪問相同的資源;
◆中斷是異步發(fā)生的,因此,如果沒有適當(dāng)?shù)谋Wo(hù),在訪問資源期間也可能發(fā)生中斷,中斷處理程序可能就會(huì)訪問到相同的資源;
◆Linux是有優(yōu)先權(quán)的,因此,如果沒有適當(dāng)?shù)谋Wo(hù),內(nèi)核代碼可能會(huì)優(yōu)先執(zhí)行,訪問其它代碼正在使用的資源。
解決這些問題的一般方法是自旋鎖和信號(hào)量。
可移植性的重要性
雖然用戶空間應(yīng)用程序一般不會(huì)太重視可移植性,但Linux的確是一個(gè)可移植性操作系統(tǒng),應(yīng)該保持一致,這意味著與架構(gòu)無關(guān)的C代碼必須在大量的系統(tǒng)上正確地編譯和運(yùn)行,與架構(gòu)相關(guān)的代碼必須在內(nèi)核源代碼樹中使用特定的目錄分隔開。
總結(jié)
可以肯定,內(nèi)核有它獨(dú)特的性質(zhì),它有它自己的一些原則,不過,內(nèi)核的復(fù)雜性和障礙與其它大型軟件項(xiàng)目相比,并沒有什么大的不同,Linux開發(fā)道路上最重要的一步是認(rèn)識(shí)到內(nèi)核并不可怕,不熟悉?當(dāng)然!不可逾越?當(dāng)然不是!
原文地址:http://www.informit.com/articles/article.aspx?p=1610334
【編輯推薦】