Linux核心是如何工作的?

來源:互聯網
上載者:User
本文發表於Linux Format magazine雜誌,作者從技術深度上解釋了Linux Kernel是如何工作的。相信對Linux開發人員來說有不小的協助。
牛津字典中對"kernel"一詞的定義是:"較軟的、通常是一個堅果可食用的部分。"當然還有第二種定義:"某個東西核心或者最重要的部分。"對Linux來說,它的Kernel無疑屬於第二種解釋。讓我們來看看這個重要的東西是如何工作的,先從一點理論說起。
廣義地來說kernel就是一個軟體,它在硬體和運行在電腦上的應用程式之間提供了一個層。嚴格點從電腦科學的角度來說,Linux中的Kernel指的是Linus Torvalds在90年代初期寫的那點代碼。
所有的你在Linux各版本中看到的其他東西--Bash shell、KDE視窗管理器、web瀏覽器、X伺服器、Tux Racer以及所有的其他,都不過是運行在Linux上的應用而已,而不是作業系統自身的一部分。為了給大家一個更加直觀的感覺,我來舉個例子,比如RHEL5的安裝大概要佔據2.5GB的硬碟空間(具體多大當然視你的選擇安裝來定),在這其中,kernel以及它的各個模組組件,只有47MB,所佔比例約為2%。
在kernel內部

那麼kernel到底是如何工作的呢?如下面的圖表。Kernel通過許多的進入連接埠也就是我們從技術角度所說的系統調用,來使得運行在它上面的應用程式可用。Kernel使用的系統調用比如"讀"和"寫"來提供你硬體的抽象(abstraction)。

從程式員的視角來看,這些看起來只是普通的功能調用,然而實際上系統調用在處理器的操作模式上,從使用者空間到Kernel空間有一個明顯的切換。同時,系統調用提供了一個"Linux虛擬機器",可以被認為是對硬體的抽象。
Kernel提供的更明顯的抽象之一是檔案系統。舉例來說,這裡有一段短的程式是用C寫的,它開啟了一個檔案並將內容拷貝到標準的輸出:
#include <fcntl.h>
int main()
{
int fd, count; char buf[1000];
fd=open("mydata", O_RDONLY);
count = read(fd, buf, 1000);
write(1, buf, count);
close(fd);
}

在這裡,你可以看到四個系統調用的例子:開啟、讀、寫和關閉。不談這段程式文法的細節,重點是:通過這些系統調用Linux Kernel提供了一個檔案的"錯覺",而實際上它不過是一堆資料有了個名字,這樣一來你就不必去與硬體底層的堆棧、分區、頭和指標、分區等交涉了,而是直接以例子中的方式與硬體"交流",這也就是我們所說的抽象(abstraction),將底層的東西以更易懂的方式表達出來。

台前幕後
系統檔案是Kernel提供的較為明顯的一種抽象。還有一些特性不是這麼的明顯,比如進程調度。任何一個時間,都可能有好幾個進程或者程式等待著運行。
Kernel的時間調度給每個進程分配CPU時間,所以就一段時間內來說,我們會有種錯覺:電腦同一時間運行好幾個程式。這是另外一個C程式:
#include <stdlib.h>
main()
{
if (fork()) {
write(1, "Parent\n", 7);
wait(0);
exit(0);
}
else {
write(1, "Child\n", 6);
exit(0);
}
}

在這個程式中建立了一個新進程,而原來的進程(父進程)和新進程(子進程)都編寫了標準輸出然後結束。注意系統調用fork(), exit() 以及wait()執行程式的建立、結束和各自同步。這是進程管理和調度中最典型的簡單調用。
Kernel還有一個更加不易見到的功能,連程式員都不易察覺,那就是儲存管理。每個程式運行得都好像它有個自己的地址空間來調用一樣,實際上它跟其他進程一樣共用電腦的實體儲存體,如果系統啟動並執行儲存過低,它的地址空間甚至會被磁碟的互動區暫時寄用。儲存管理的另外一個方面是防止一個進程訪問其他進程的地址空間--對於多進程作業系統來說這是很必要的一個防範措施。
Kernel同樣還配置網路連結協議比如IP、TCP和UDP等,它們在網路上提供機器對機器(machine-to-machine)和進程對進程(process-to-process)的通訊。這裡又會造成一種假象,即TCP在兩個進程之間提供了一個固定串連--就好像串連兩個電話的銅線一樣,實際中卻並沒有固定的串連,特殊的引用協議比如FTP、DNS和HTTP是通過使用者級程式來實施的,而並非Kernel的一部分。
Linux(像之前的Unix)在安全方面口碑很好,這是因為Kernel追蹤記錄了每個運行進程的user ID和group ID,每次當一個應用企圖訪問資源(比如開啟一個檔案來寫入)的時候,Kernel就會核對檔案上的訪問許可然後做出允許/禁止的命令。這種存取控制模式最終對整個Linux系統的安全作用很大。
Kernel還提供了一大套模組的集合,其功能包括如何處理與硬體裝置交流的諸多細節、如何從磁碟讀取一個分區、如果從網路介面卡擷取資料包等。有時我們稱這些為裝置驅動。

模組化的Kernel
現在我們隊Kernel是做什麼的已經有了一些瞭解,讓我們再來簡單看下它的物理組成。早期版本的Linux Kernel是整體式的,也就是說所有的組件都靜態地串連成一個(很大的)執行檔案。
相比較而言,現在的Linux Kernel是模組化的:許多功能包含在模組內,然後動態地載入kernel中。這使得kernel的核心很小,而且在運行kernel時可以不必reboot就能載入和替代模組。
Kernel的核心在boot time時從位於/boot目錄的一個檔案載入進儲存中,通常這個/boot
目錄會被叫做KERNELVERSION,KERNELVERSION與kernel版本有關。(如果你想知道你的kernel版本是什麼,運行命令列顯示系統資訊-r。)kernel的模組位於目錄/lib/modules/KERNELVERSION之下,所有的組件都會在kernel安裝時被拷貝。
管理模組
大部分情況下,Linux管理它的模組不需要你的幫忙,但是如果必要的時候有命令列可以來手動檢查和管理模組。比如,為了查清楚當前到底哪個模組在載入kernel。這裡有一個輸出的例子:
# 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
輸出的內容包括:模組的名字、大小、使用次數和依賴於它的模組列表。使用次數對防止卸載當前活躍的模組非常總要。Linux只允許使用次數為零的模組被移除。
你可以使用modprobe來手動載入和卸載模組,(還有兩個命令列叫做insmod和rmmod,但modprobe更便於使用因為它自動移除了模組依賴)。比如lsmod的輸出在我們的電腦上顯示了一個名叫isofs的卸載模組,它的使用次數是零而且沒有相依模組,(isofs是一個模組,它支援CD上使用的ISO系統檔案格式)這種情況下,kernel會允許我們卸載模組:
# modprobe -r isofs
現在,isofs不再顯示在Ismod的輸出中,kernel由此節省了36,284位元組的儲存。如果你放入CD並且讓它自動安裝,kernel將自動重新載入isofs模組,而且isofs的使用次數增加到1次。如果這時候你還試圖移除模組,就不會成功了因為它正在被使用:
# modprobe -r isofs
FATAL: Module isofs is in use.

Lsmod只是列出了當前被載入的模組,modprobe則將列出所有可用的模組,它實際上輸出了/lib/modules/KERNELVERSION目錄下所有的模組,名單會很長!
實際上,使用modprobe來手動載入一個模組並不常見,但確實可以通過modprobe命令列來對模組設定參數,例如:
# modprobe usbcore blinkenlights=1
我們並不是在建立blinkenlights,而是usbcore模組的實參數。
那麼如何知道一個模組會接受什麼參數呢?一個比較好的方法是使用modinfo命令,它列出了關於模組的種種資訊。這裡有一個關於模組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
對我們來說比較有興趣的以"parm"開頭的那些部分:顯示了模組所接受的參數。這些描述都比較簡明,如果想要更多的資訊,那就安裝kernel的原始碼,在類似於/usr/src/KERNELVERSION/Documentation的目錄下你會找到。
裡面會有一些有趣的東西,比如檔案/usr/src/KERNELVERSION/Documentation/sound/alsa/ALSA-
Configuration.txt描述的是被許多ALSA聲音模組承認的參數;/usr/src/KERNELVERSION
/Documentation/kernel-parameters.txt這個檔案也很有用。
前幾天在Ubuntu論壇有一個例子,說的是如何將參數傳遞到一個模組(詳見https://help.ubuntu.com/community
/HdaIntelSoundHowto)。實際上問題的關鍵是snd-hda-intel參數在正確驅動聲音硬體時需要一點操作,而且在boot time載入時會中止。解決方案的一部分是將probe_mask=1選項賦給模組,如果你是手動載入模組,你需要輸入:
# modprobe snd-hda-intel probe_mask=1
更有可能,你在檔案/etc/modprobe.conf中放置這樣類似的一行:options
snd-hda-intel probe_mask=1
這"告訴"modprobe每次在載入snd-hda-intel模組時包含probe_mask=1選項。現在的有些Linux版本將這一資訊分離進/etc/modprobe.d下的不同檔案中了,而不是放入modprobe.conf中。
/proc系統檔案
Linux kernel同樣通過/proc系統檔案來展示了許多細節。為了說明/proc,我們首先需要擴充我們對於檔案的理解。除了認為檔案就是儲存在硬碟或者
CD或者儲存空間上的持久資訊之外,我們還應當把它理解為任何可以通過傳統系統調用如:開啟、讀、寫、關閉等訪問的資訊,當然它也可以被常見的程式訪問。
/proc之下的"檔案"完全是kernel虛擬一個部分,給我們一個視角可以看到kernel內部的資料結構。實際上,許多Linux的報告工具均能夠很好地呈現在/proc下的檔案中尋到的格式化版本的資訊。比如,一列/proc/modules將展示一列當前載入的模組。
同樣的,/proc/meminfo提供了關於虛擬儲存系統目前狀態的更多細節資訊,而類如vmstat的工具則是以一種更加可理解的方式提供了相同的一些資訊;/proc/net/arp顯示了系統ARP
cache的當前內容,從命令列來說,arp -a顯示的也是相同的資訊。
尤其有意思的是/proc/sys下的"檔案"。/proc/sys/net/ipv4/ip_forward下的設定告訴我們kernel是否將轉寄IP資料包,也就是說是否扮演網關的作用。現在,kernel告訴我們這是關閉的:
# cat /proc/sys/net/ipv4/ip_forward
0
當你發現你可以對這些檔案寫入的時候,你會覺得更加有意思。繼續舉例來說:
# echo 1 > /proc/sys/net/ipv4/ip_forward
將在啟動並執行kernel中開啟IP 轉寄(IP forwarding)
除了使用cat和echo來檢查和更正/proc/sys下的設定以外,你也可以使用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

需要注意的是,以這種方式你所做的設定改變只能影響當前啟動並執行kernel的,當reboot的時候就不再有效。如果想讓設定永久有效,將它們放置在 /etc/sysctl.conf檔案中。在boot time時,sysctl將自動重新確定它在此檔案下找到的任何設定。
/etc/sysctl.conf下的程式碼大概是這樣的:net.ipv4.ip_forward=1
效能調優(performance tuning)
有這樣一個說法:/proc/sys下可寫入的參數孕育了整個Linux效能調優的亞文化。我個人覺得這種說法有點過誇,但這裡會有幾個你確實很想一試的例子:Oracle
10g的安裝說明(www.oracle.com/technology/obe/obe10gdb/install/linuxpreinst
/linuxpreinst.htm)要求你設定一組參數,包括:kernel.shmmax=2147483648
這將公用儲存空間的大小設定為2GB。(公用儲存空間是處理期內的通訊機制,允許儲存單元在多個進程的地址空間內同時可用)
IBM 'Redpaper'在Linux效能和調優方面的說明(www.redbooks.ibm.com/abstracts/redp4285.html)在調教/proc/sys下的參數方面給出了不少建議,包括:vm.swappiness=100
這個參數控制著儲存頁如何被交換到磁碟。
一些參數可以被設定從而提高安全性,如net.ipv4.icmp_echo_ignore_broadcasts=1
它"告訴"kernel不必響應ICMP請求,從而使得你的網路免受類如Smurf攻擊之類的拒絕伺服器(denial-of-service)型攻擊。
net.ipv4.conf.all.rp_filter=1 則是"告訴"kernel加強入站過濾(ingress
filtering)和出站過濾(egress filtering)
那麼有沒有一個說明能涵蓋這所有的參數?好吧,這有一行命令:# sysctl -a
它將展示所有的參數名字和當前值。列表很長,但是你無法知道這些參數是做什麼的。另外比較有用的參考是Red Hat Enterprise Linux Reference Guide,對此有整章節的描述,你可以從www.redhat.com/docs/manuals/enterprise上下載。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.