牛津字典中對(duì)"kernel"一詞的定義是:"較軟的、通常是一個(gè)堅(jiān)果可食用的部分。"當(dāng)然還有第二種定義:"某個(gè)東西核心或者最重要的部分。"對(duì)Linux來(lái)說(shuō),它的Kernel無(wú)疑屬于第二種解釋。讓我們來(lái)看看這個(gè)重要的東西是如何工作的,先從一點(diǎn)理論說(shuō)起。
廣義地來(lái)說(shuō)kernel就是一個(gè)軟件,它在硬件和運(yùn)行在計(jì)算機(jī)上的應(yīng)用程序之間提供了一個(gè)層。嚴(yán)格點(diǎn)從計(jì)算機(jī)科學(xué)的角度來(lái)說(shuō),Linux中的Kernel指的是Linus Torvalds在90年代初期寫(xiě)的那點(diǎn)代碼。
??? 所有的你在Linux各版本中看到的其他東西--Bash shell、KDE窗口管理器、web瀏覽器、X服務(wù)器、Tux Racer以及所有的其他,都不過(guò)是運(yùn)行在Linux上的應(yīng)用而已,而不是操作系統(tǒng)自身的一部分。為了給大家一個(gè)更加直觀的感覺(jué),我來(lái)舉個(gè)例子,比如RHEL5的安裝大概要占據(jù)2.5GB的硬盤(pán)空間(具體多大當(dāng)然視你的選擇安裝來(lái)定),在這其中,kernel以及它的各個(gè)模塊組件,只有47MB,所占 比例約為2%。
在kernel內(nèi)部
那么kernel到底是如何工作的呢?如下面的圖表。Kernel通過(guò)許多的進(jìn)入端口也就是我們從技術(shù)角度所說(shuō)的系統(tǒng)調(diào)用,來(lái)使得運(yùn)行在它上面的應(yīng)用程序可用。Kernel使用的系統(tǒng)調(diào)用比如"讀"和"寫(xiě)"來(lái)提供你硬件的抽象(abstraction)。
?
從程序員的視角來(lái)看,這些看起來(lái)只是普通的功能調(diào)用,然而實(shí)際上系統(tǒng)調(diào)用在處理器的操作模式上,從用戶(hù)空間到Kernel空間有一個(gè)明顯的切換。同時(shí),系統(tǒng)調(diào)用提供了一個(gè)"Linux虛擬機(jī)",可以被認(rèn)為是對(duì)硬件的抽象。
Kernel提供的更明顯的抽象之一是文件系統(tǒng)。舉例來(lái)說(shuō),這里有一段短的程序是用C寫(xiě)的,它打開(kāi)了一個(gè)文件并將內(nèi)容拷貝到標(biāo)準(zhǔn)的輸出:
#include
int main()
{
??? int fd, count; char buf[1000];
??? fd=open("mydata", O_RDONLY);
??? count = read(fd, buf, 1000);
??? write(1, buf, count);
??? close(fd);
}
??? 在這里,你可以看到四個(gè)系統(tǒng)調(diào)用的例子:打開(kāi)、讀、寫(xiě)和關(guān)閉。不談這段程序語(yǔ)法的細(xì)節(jié),重點(diǎn)是:通過(guò)這些系統(tǒng)調(diào)用Linux Kernel提供了一個(gè)文件的"錯(cuò)覺(jué)",而實(shí)際上它不過(guò)是一堆數(shù)據(jù)有了個(gè)名字,這樣一來(lái)你就不必去與硬件底層的堆棧、分區(qū)、頭和指針、分區(qū)等交涉了,而是 直接以例子中的方式與硬件"交流",這也就是我們所說(shuō)的抽象(abstraction),將底層的東西以更易懂的方式表達(dá)出來(lái)。
臺(tái)前幕后
系統(tǒng)文件是Kernel提供的較為明顯的一種抽象。還有一些特性不是這么的明顯,比如進(jìn)程調(diào)度。任何一個(gè)時(shí)間,都可能有好幾個(gè)進(jìn)程或者程序等待著運(yùn)行。Kernel的時(shí)間調(diào)度給每個(gè)進(jìn)程分配CPU時(shí)間,所以就一段時(shí)間內(nèi)來(lái)說(shuō),我們會(huì)有種錯(cuò)覺(jué):電腦同一時(shí)間運(yùn)行好幾個(gè)程序。這是另外一個(gè)C程序:
#include
main()
{
? if (fork()) {
??? write(1, "Parent/n", 7);
??? wait(0);
??? exit(0);
? }
? else {
??? write(1, "Child/n", 6);
??? exit(0);
? }
}
????
在這個(gè)程序中創(chuàng)建了一個(gè)新進(jìn)程,而原來(lái)的進(jìn)程(父進(jìn)程)和新進(jìn)程(子進(jìn)程)都編寫(xiě)了標(biāo)準(zhǔn)輸出然后結(jié)束。注意系統(tǒng)調(diào)用fork(), exit() 以及 wait()執(zhí)行程序的創(chuàng)建、結(jié)束和各自同步。這是進(jìn)程管理和調(diào)度中最典型的簡(jiǎn)單調(diào)用。
Kernel還有一個(gè)更加不易見(jiàn)到的功能,連程序員都不易察覺(jué),那就是存儲(chǔ)管理。每個(gè)程序運(yùn)行得都好像它有個(gè)自己的地址空間來(lái)調(diào)用一樣,實(shí)際上它跟其他進(jìn) 程一樣共享計(jì)算機(jī)的物理存儲(chǔ),如果系統(tǒng)運(yùn)行的存儲(chǔ)過(guò)低,它的地址空間甚至?xí)淮疟P(pán)的交互區(qū)暫時(shí)寄用。存儲(chǔ)管理的另外一個(gè)方面是防止一個(gè)進(jìn)程訪(fǎng)問(wèn)其他進(jìn)程的 地址空間--對(duì)于多進(jìn)程操作系統(tǒng)來(lái)說(shuō)這是很必要的一個(gè)防范措施。
Kernel同樣還配置網(wǎng)絡(luò)鏈接協(xié)議比如IP、TCP和UDP等,它們?cè)诰W(wǎng)絡(luò)上提供機(jī)器對(duì)機(jī)器(machine-to-machine)和進(jìn)程對(duì)進(jìn)程 (process-to-process)的通信。這里又會(huì)造成一種假象,即TCP在兩個(gè)進(jìn)程之間提供了一個(gè)固定連接--就好像連接兩個(gè)電話(huà)的銅線(xiàn)一樣, 實(shí)際中卻并沒(méi)有固定的連接,特殊的引用協(xié)議比如FTP、DNS和HTTP是通過(guò)用戶(hù)級(jí)程序來(lái)實(shí)施的,而并非Kernel的一部分。
Linux(像之前的Unix)在安全方面口碑很好,這是因?yàn)镵ernel跟蹤記錄了每個(gè)運(yùn)行進(jìn)程的user ID和group ID,每次當(dāng)一個(gè)應(yīng)用企圖訪(fǎng)問(wèn)資源(比如打開(kāi)一個(gè)文件來(lái)寫(xiě)入)的時(shí)候,Kernel就會(huì)核對(duì)文件上的訪(fǎng)問(wèn)許可然后做出允許/禁止的命令。這種訪(fǎng)問(wèn)控制模式 最終對(duì)整個(gè)Linux系統(tǒng)的安全作用很大。
Kernel還提供了一大套模塊的集合,其功能包括如何處理與硬件設(shè)備交流的諸多細(xì)節(jié)、如何從磁盤(pán)讀取一個(gè)分區(qū)、如果從網(wǎng)絡(luò)接口卡獲取數(shù)據(jù)包等。有時(shí)我們稱(chēng)這些為設(shè)備驅(qū)動(dòng)。
模塊化的Kernel
現(xiàn)在我們隊(duì)Kernel是做什么的已經(jīng)有了一些了解,讓我們?cè)賮?lái)簡(jiǎn)單看下它的物理組成。早期版本的Linux Kernel是整體式的,也就是說(shuō)所有的部件都靜態(tài)地連接成一個(gè)(很大的)執(zhí)行文件。
相比較而言,現(xiàn)在的Linux Kernel是模塊化的:許多功能包含在模塊內(nèi),然后動(dòng)態(tài)地載入kernel中。這使得kernel的內(nèi)核很小,而且在運(yùn)行kernel時(shí)可以不必reboot就能載入和替代模塊。
Kernel的內(nèi)核在boot time時(shí)從位于/boot 目錄的一個(gè)文件加載進(jìn)存儲(chǔ)中,通常這個(gè)/boot目錄會(huì)被叫做KERNELVERSION,KERNELVERSION與kernel版本有關(guān)。(如果你想知道你的kernel版本是什么,運(yùn)行命令行顯 示系統(tǒng)信息-r。)kernel的模塊位于目錄/lib/modules/KERNELVERSION之下,所有的組件都會(huì)在kernel安裝時(shí)被拷貝。
管理模塊
大部分情況下,Linux管理它的模塊不需要你的幫忙,但是如果必要的時(shí)候有命令行可以來(lái)手動(dòng)檢查和管理模塊。比如,為了查清楚當(dāng)前到底哪個(gè)模塊在載入kernel。這里有一個(gè)輸出的例子:
# lsmod
pcspkr????????????? 4224? 0?
hci_usb??????????? 18204? 2?
psmouse??????????? 38920? 0?
bluetooth????????? 55908? 7 rfcomm,l2cap,hci_usb
yenta_socket?????? 27532? 5?
rsrc_nonstatic???? 14080? 1 yenta_socket
isofs????????????? 36284? 0?
輸出的內(nèi)容包括:模塊的名字、大小、使用次數(shù)和依賴(lài)于它的模塊列表。使用次數(shù)對(duì)防止卸載當(dāng)前活躍的模塊非??傄?。Linux只允許使用次數(shù)為零的模塊被移除。
你可以使用modprobe來(lái)手動(dòng)加載和卸載模塊,(還有兩個(gè)命令行叫做insmod和rmmod,但modprobe更易于使用因?yàn)樗詣?dòng)移除了模塊依 賴(lài))。比如lsmod的輸出在我們的電腦上顯示了一個(gè)名叫isofs的卸載模塊,它的使用次數(shù)是零而且沒(méi)有依賴(lài)模塊,(isofs是一個(gè)模塊,它支持CD上使用的ISO系統(tǒng)文件格式)這種情況下,kernel會(huì)允許我們卸載模塊:
# modprobe -r isofs
現(xiàn)在,isofs不再顯示在Ismod的輸出中,kernel由此節(jié)省了36,284字節(jié)的存儲(chǔ)。如果你放入CD并且讓它自動(dòng)安裝,kernel將自動(dòng)重 新載入isofs模塊,而且isofs的使用次數(shù)增加到1次。如果這時(shí)候你還試圖移除模塊,就不會(huì)成功了因?yàn)樗诒皇褂茫?br />
# modprobe -r isofs?
FATAL: Module isofs is in use.
????
Lsmod只是列出了當(dāng)前被載入的模塊,modprobe則將列出所有可用的模塊,它實(shí)際上輸出了/lib/modules/KERNELVERSION目錄下所有的模塊,名單會(huì)很長(zhǎng)!
實(shí)際上,使用modprobe來(lái)手動(dòng)加載一個(gè)模塊并不常見(jiàn),但確實(shí)可以通過(guò)modprobe命令行來(lái)對(duì)模塊設(shè)置參數(shù),例如:
# modprobe usbcore blinkenlights=1
我們并不是在創(chuàng)建blinkenlights,而是usbcore模塊的實(shí)參數(shù)。
那么如何知道一個(gè)模塊會(huì)接受什么參數(shù)呢?一個(gè)比較好的方法是使用modinfo命令,它列出了關(guān)于模塊的種種信息。這里有一個(gè)關(guān)于模塊snd-hda-intel的例子
# modinfo snd-hda-intel?
filename:?????? /lib/modules/2.6.20-16-generic/kernel/sound/pci/hda/snd-hda-intel.ko
description:??? Intel HDA driver
license:??????? GPL
srcversion:???? A3552B2DF3A932D88FFC00C
alias:????????? pci:v000010DEd0000055Dsv*sd*bc*sc*i*
alias:????????? pci:v000010DEd0000055Csv*sd*bc*sc*i*
depends:??????? snd-pcm,snd-page-alloc,snd-hda-codec,snd
vermagic:?????? 2.6.20-16-generic SMP mod_unload 586?
parm:?????????? index:Index value for Intel HD audio interface. (int)
parm:?????????? id:ID string for Intel HD audio interface. (charp)
parm:?????????? model:Use the given board model. (charp)
parm:?????????? position_fix:Fix DMA pointer (0 = auto, 1 = none, 2 = POSBUF, 3 = FIFO size). (int)
parm:?????????? probe_mask:Bitmask to probe codecs (default = -1). (int)
parm:?????????? single_cmd:Use single command to communicate with codecs (for debugging only). (bool)
parm:?????????? enable_msi:Enable Message Signaled Interrupt (MSI) (int)
parm:?????????? enable:bool
對(duì)我們來(lái)說(shuō)比較有興趣的以"parm"開(kāi)頭的那些部分:顯示了模塊所接受的參數(shù)。這些描述都比較簡(jiǎn)明,如果想要更多的信息,那就安裝kernel的源代碼,在類(lèi)似于/usr/src/KERNELVERSION/Documentation的目錄下你會(huì)找到。
里面會(huì)有一些有趣的東西,比如文件/usr/src/KERNELVERSION/Documentation/sound/alsa/ALSA-Configuration.txt描述的是被許多ALSA聲音模塊承認(rèn)的參數(shù);/usr/src/KERNELVERSION /Documentation/kernel-parameters.txt這個(gè)文件也很有用。
前幾天在Ubuntu論壇有一個(gè)例子,說(shuō)的是如何將參數(shù)傳遞到一個(gè)模塊(詳見(jiàn)https://help.ubuntu.com/community /HdaIntelSoundHowto)。實(shí)際上問(wèn)題的關(guān)鍵是snd-hda-intel參數(shù)在正確驅(qū)動(dòng)聲音硬件時(shí)需要一點(diǎn)操作,而且在boot time加載時(shí)會(huì)中止。解決方法的一部分是將probe_mask=1選項(xiàng)賦給模塊,如果你是手動(dòng)加載模塊,你需要輸入:
# modprobe snd-hda-intel probe_mask=1
更有可能,你在文件/etc/modprobe.conf中放置這樣類(lèi)似的一行:options snd-hda-intel probe_mask=1
這"告訴"modprobe每次在加載snd-hda-intel模塊時(shí)包含probe_mask=1選項(xiàng)?,F(xiàn)在的有些Linux版本將這一信息分離進(jìn)/etc/modprobe.d下的不同文件中了,而不是放入modprobe.conf中。
/proc系統(tǒng)文件
Linux kernel同樣通過(guò)/proc系統(tǒng)文件來(lái)展示了許多細(xì)節(jié)。為了說(shuō)明/proc,我們首先需要擴(kuò)展我們對(duì)于文件的理解。除了認(rèn)為文件就是存儲(chǔ)在硬盤(pán)或者CD或者存儲(chǔ)空間上的持久信息之外,我們還應(yīng)當(dāng)把它理解為任何可以通過(guò)傳統(tǒng)系統(tǒng)調(diào)用如:打開(kāi)、讀、寫(xiě)、關(guān)閉等訪(fǎng)問(wèn)的信息,當(dāng)然它也可以被常見(jiàn)的程序訪(fǎng)問(wèn)。
/proc之下的"文件"完全是kernel虛擬的一個(gè)部分,給我們一個(gè)視角可以看到kernel內(nèi)部的數(shù)據(jù)結(jié)構(gòu)。實(shí)際上,許多Linux的報(bào)告工具均能 夠很好地呈現(xiàn)在/proc下的文件中尋到的格式化版本的信息。比如,一列/proc/modules將展示一列當(dāng)前加載的模塊。
同樣的,/proc/meminfo提供了關(guān)于虛擬存儲(chǔ)系統(tǒng)當(dāng)前狀態(tài)的更多細(xì)節(jié)信息,而類(lèi)如vmstat的工具則是以一種更加可理解的方式提供了相同的一 些信息;/proc/net/arp顯示了系統(tǒng)ARP cache的當(dāng)前內(nèi)容,從命令行來(lái)說(shuō),arp -a顯示的也是相同的信息。
尤其有意思的是/proc/sys下的"文件"。/proc/sys/net/ipv4/ip_forward下的設(shè)置告訴我們kernel是否將轉(zhuǎn)發(fā)IP數(shù)據(jù)包,也就是說(shuō)是否扮演網(wǎng)關(guān)的作用?,F(xiàn)在,kernel告訴我們這是關(guān)閉的:
# cat /proc/sys/net/ipv4/ip_forward?
0
當(dāng)你發(fā)現(xiàn)你可以對(duì)這些文件寫(xiě)入的時(shí)候,你會(huì)覺(jué)得更加有意思。繼續(xù)舉例來(lái)說(shuō):
# echo 1 > /proc/sys/net/ipv4/ip_forward
將在運(yùn)行的kernel中打開(kāi)IP 轉(zhuǎn)發(fā)(IP forwarding)
除了使用cat和echo來(lái)檢查和更正/proc/sys下的設(shè)置以外,你也可以使用sysctl命令:
# sysctl net.ipv4.ip_forward?
net.ipv4.ip_forward = 0
這等同于:
# cat /proc/sys/net/ipv4/ip_forward?
0
也等同于:
# sysctl -w net.ipv4.ip_forward=1
net.ipv4.ip_forward = 1
還等同于:
# echo 1 > /proc/sys/net/ipv4/ip_forward
????
需要注意的是,以這種方式你所做的設(shè)置改變只能影響當(dāng)前運(yùn)行的kernel的,當(dāng)reboot的時(shí)候就不再有效。如果想讓設(shè)置永久有效,將它們放置在/etc/sysctl.conf文件中。在boot time時(shí),sysctl將自動(dòng)重新確定它在此文件下找到的任何設(shè)置。
/etc/sysctl.conf下的代碼行大概是這樣的:net.ipv4.ip_forward=1
性能調(diào)優(yōu)(performance tuning)
有這樣一個(gè)說(shuō)法:/proc/sys下可寫(xiě)入的參數(shù)孕育了整個(gè)Linux性能調(diào)優(yōu)的亞文化。我個(gè)人覺(jué)得這種說(shuō)法有點(diǎn)過(guò)夸,但這里會(huì)有幾個(gè)你確實(shí)很想一試的 例子:Oracle 10g的安裝說(shuō)明(www.oracle.com/technology/obe/obe10gdb/install/linuxpreinst /linuxpreinst.htm)要求你設(shè)置一組參數(shù),包括:kernel.shmmax=2147483648這將公用存儲(chǔ)器的大小設(shè)置為2GB。(公用存儲(chǔ)器是處理期內(nèi)的通信機(jī)制,允許存儲(chǔ)單元在多個(gè)進(jìn)程的地址空間內(nèi)同時(shí)可用)
IBM 'Redpaper'在Linux性能和調(diào)優(yōu)方面的說(shuō)明(www.redbooks.ibm.com/abstracts/redp4285.html) 在調(diào)教/proc/sys下的參數(shù)方面給出了不少建議,包括:vm.swappiness=100 這個(gè)參數(shù)控制著存儲(chǔ)頁(yè)如何被交換到磁盤(pán)。
一些參數(shù)可以被設(shè)置從而提高安全性,如net.ipv4.icmp_echo_ignore_broadcasts=1它"告訴"kernel不必響應(yīng)ICMP請(qǐng)求,從而使得你的網(wǎng)絡(luò)免受類(lèi)如Smurf攻擊之類(lèi)的拒絕服務(wù)器(denial-of-service)型攻擊。
net.ipv4.conf.all.rp_filter=1 則是"告訴"kernel加強(qiáng)入站過(guò)濾(ingress filtering)和出站過(guò)濾(egress filtering)
那么有沒(méi)有一個(gè)說(shuō)明能涵蓋這所有的參數(shù)?好吧,這有一行命令:# sysctl -a它將展示所有的參數(shù)名字和當(dāng)前值。列表很長(zhǎng),但是你無(wú)法知道這些參數(shù)是做什么的。
?
評(píng)論
查看更多