❶ linux內核有沒有hook機制
有啊,一切順序邏輯,都有被hook的可能。 下面是一個linux上的hook的實例
截獲write系統調用:
#ifndefMODULE
#defineMODULE
#endif
#ifndef__KERNEL__
#define__KERNEL__
#endif
#include<linux/init.h>
#include<linux/mole.h>
#include<linux/version.h>
#include<linux/kernel.h>
#include<asm/unistd.h>
#include<linux/slab.h>
/*
#include<sys/types.h>
#include<asm/fcntl.h>
#include<linux/malloc.h>
#include<linux/types.h>
#include<linux/string.h>
#include<linux/fs.h>
#include<asm/errno.h>
#include<sys/syscall.h>
*/
MODULE_LICENSE("GPL");
structdescriptor_idt
{
unsignedshortoffset_low;
unsignedshortignore1;
unsignedshortignore2;
unsignedshortoffset_high;
};
staticstruct{
unsignedshortlimit;
unsignedlongbase;
}__attribute__((packed))idt48;
staticunsignedintSYS_CALL_TABLE_ADDR;
void**sys_call_table;
intbase_system_call;
int(*orig_write)(unsignedintfd,char*buf,unsignedintcount);
unsignedcharopcode_call[3]={0xff,0x14,0x85};
intmatch(unsignedchar*source)
{
inti;
for(i=0;i<3;i++){
if(source[i]!=opcode_call[i])
return0;
}
return1;
}
intget_sys_call_table(void)
{
inti,j;
unsignedchar*ins=(unsignedchar*)base_system_call;
unsignedintsct;
for(i=0;i<100;i++){
if(ins[i]==opcode_call[0]){
if(match(ins+i)){
sct=*((unsignedint*)(ins+3+i));
printk(KERN_ALERT"sys_call_tabl'saddressis
0x%X ",sct);
returnsct;
}
}
}
printk(KERN_ALERT"can'tfindtheaddressofsys_call_table ");
return-1;
}
inthacked_write(unsignedintfd,char*buf,unsignedintcount)
{
char*hide="hello";
if(strstr(buf,hide)!=NULL){
printk(KERN_ALERT"findname. ");
returncount;
}
else{
returnorig_write(fd,buf,count);
}
}
intinit_mole(void)
{
__asm__volatile("sidt%0":"=m"(idt48));
structdescriptor_idt*pIdt80=(structdescriptor_idt*)(idt48.base+8*0x80);
base_system_call=(pIdt80->offset_high<<16|pIdt80->offset_low);
printk(KERN_ALERT"system_calladdressat0x%x ",base_system_call);
SYS_CALL_TABLE_ADDR=get_sys_call_table();
sys_call_table=(void**)SYS_CALL_TABLE_ADDR;
orig_write=sys_call_table[__NR_write];
sys_call_table[__NR_write]=hacked_write;
return0;
}
voidcleanup_mole()
{
sys_call_table[__NR_write]=orig_write;
}
❷ linux中斷描述符表起始地址存放在cpu里嗎
linux中斷描述符表是保存於cpu內的,確切的說CPU有專門的寄存器IDTR來保存IDT在內存中的位置,即IDT的起始位置。
❸ linux支持中斷嵌套嗎
中斷嵌套, 對於不同的中斷是可以自由嵌套的,而對於同種類型的中斷,是不可以嵌套執行的。
以下簡單解釋一下如何利用狀態標志來防止同種類型中斷的重入:
當某種類型的中斷第一次發生時,首先其idt表項的狀態位上被賦予IRQ_PENDING標志,表示有待處理。 然後將中斷處理函數action置為null,然後由於其狀態沒有IRQ_INPROGRESS標志(第一次),故將其狀態置上IRQ_INPROGRESS並去處IRQ_PENDING標志,同時將action賦予相應的中斷處理函數指針(這里是一個重點,linux很巧妙的用法,隨後說明)。這樣,後面就可以順利執行handle_IRQ_event進行中斷處理,當在handle_IRQ_event中開中斷後(我認為應該是回復EOI給PCI),如果有同種類型的中斷發生,則再次進入do_IRQ函數,然後其狀態位上加上IRQ_PENDING標志,但是由於前一次中斷處理中加上的IRQ_INPROGRESS沒有被清除,因此這里無法清除IRQ_PENDING標志,因此action還是為null,這樣就無法再次執行handle_IRQ_event函數。從而退出本次中斷處理,返回上一次的中斷處理函數中,即繼續執行handle_IRQ_event函數。當handle_IRQ_event返回時檢查IRQ_PENDING標志,發現存在這個標志,說明handle_IRQ_event執行過程中被中斷過,存在未處理的同類中斷,因此再次循環執行handle_IRQ_event函數。直到不存在IRQ_PENDING標志為止。
❹ Linux 系統中的中斷是不是沒有中斷優先順序
關於中斷嵌套:在linux內核里,如果驅動在申請注冊中斷的時候沒有特別的指定,do_irq在做中斷響應的時候,是開啟中斷的,如果在驅動的中斷處理函數正在執行的過程中,出現同一設備的中斷或者不同設備的中斷,這時候新的中斷會被立即處理,還是被pending,等當前中斷處理完成後,再做處理。在2.4和2.6內核里,關於這一塊是否有什麼不同。 一般申請中斷的時候都允許開中斷,即不使用SA_INTERRUPT標志。如果允許共享則加上 SA_SHIRQ,如果可以為內核熵池提供熵值(譬如你寫的驅動是ide之類的驅動),則再加上 SA_SAMPLE_RANDOM標志。這是普通的中斷請求過程。對於這種一般情況,只要發生中斷,就可以搶占內核,即使內核正在執行其他中斷函數。這里有兩點說明:一是因為linux不支持 中斷優先順序,因此任何中斷都可以搶占其他中斷,但是同種類型的中斷(即定義使用同一個 中斷線的中斷)不會發生搶占,他們會在執行本類型中斷的時候依次被調用執行。二是所謂 只要發生中斷,就可以搶占內核這句是有一定限制的,因為當中斷發生的時候系統由中斷門 進入時自動關中斷(對於x86平台就是將eflags寄存器的if位置為0),只有當中斷函數被執行 (handle_IRQ_event)的過程中開中斷之後才能有搶占。 對於同種類型的中斷,由於其使用同樣的idt表項,通過其狀態標志(IRQ_PENDING和 IRQ_INPROGRESS)可以防止同種類型的中斷函數執行(注意:是防止handle_IRQ_event被重入, 而不是防止do_IRQ函數被重入),對於不同的中斷,則可以自由的嵌套。因此,所謂中斷嵌套, 對於不同的中斷是可以自由嵌套的,而對於同種類型的中斷,是不可以嵌套執行的。以下簡單解釋一下如何利用狀態標志來防止同種類型中斷的重入:當某種類型的中斷第一次發生時,首先其idt表項的狀態位上被賦予IRQ_PENDING標志,表示有待處理。 然後將中斷處理函數action置為null,然後由於其狀態沒有IRQ_INPROGRESS標志(第一次),故將其狀態置上IRQ_INPROGRESS並去處IRQ_PENDING標志,同時將action賦予相應的中斷處理函數指針(這里是一個重點,linux很巧妙的用法,隨後說明)。這樣,後面就可以順利執行handle_IRQ_event進行中斷處理,當在handle_IRQ_event中開中斷後,如果有同種類型的中斷發生,則再次進入do_IRQ函數,然後其狀態位上加上IRQ_PENDING標志,但是由於前一次中斷處理中加上的IRQ_INPROGRESS沒有被清除,因此這里無法清除IRQ_PENDING標志,因此action還是為null,這樣就無法再次執行handle_IRQ_event函數。從而退出本次中斷處理,返回上一次的中斷處理函數中,即繼續執行handle_IRQ_event函數。當handle_IRQ_event返回時檢查IRQ_PENDING標志,發現存在這個標志,說明handle_IRQ_event執行過程中被中斷過,存在未處理的同類中斷,因此再次循環執行handle_IRQ_event函數。直到不存在IRQ_PENDING標志為止。2.4和2.6的差別,就我來看,主要是在2.6中一進入do_IRQ,多了一個關閉內核搶占的動作,同時在處理中多了一種對IRQ_PER_CPU類型的中斷的處理,其他沒有什麼太大的改變。這類IRQ_PER_CPU的中斷主要用在smp環境下將中斷綁定在某一個指定的cpu上。例如arch/ppc/syslib/open_pic.c中的openpic_init中初始化ipi中斷的時候。 其實簡單的說,中斷可以嵌套,但是同種類型的中斷是不可以嵌套的,因為在IRQ上發生中斷,在中斷響應的過程中,這個IRQ是屏蔽的,也就是這個IRQ的中斷是不能被發現的。 同時在內核的臨界區內,中斷是被禁止的 關於do_IRQ可能會丟失中斷請求:do_IRQ函數是通過在執行完handle_IRQ_event函數之後判斷status是否被設置了IRQ_PENDING標志來判斷是否還有沒有被處理的同一通道的中斷請求。 但是這種方法只能判斷是否有,而不能知道有多少個未處理的統一通道中斷請求。也就是說,假如在第一個中斷請求執行handle_IRQ_event函數的過程中來了同一通道的兩個或更多中斷請求,而這些中斷不會再來,那麼僅僅通過判斷status是否設置了IRQ_PENDING標志不知道到底有多少個未處理的中斷,handle_IRQ_event只會被再執行一次。這算不算是個bug呢? 不算,只要知道有中斷沒有處理就OK了,知道1個和知道N個,本質上都是一樣的。作為外設,應當能夠處理自己中斷未被處理的情況。不可能丟失的,在每一個中斷描述符的結構體內,都有一個鏈表,鏈表中存放著服務常式序關於中斷中使用的幾個重要概念和關系: 一、基本概念 1. 產生的位置 發生的時刻 時序 中斷 CPU外部 隨機 非同步 異常 CPU正在執行的程序 一條指令終止執行後 同步 2.由中斷或異常執行的代碼不是一個進程,而是一個內核控制路徑,代表中斷發生時正在運行的進程的執行 中斷處理程序與正在運行的程序無關 引起異常處理程序的進程正是異常處理程序運行時的當前進程 二、特點 (2)能以嵌套的方式執行,但是同種類型的中斷不可以嵌套 (3)盡可能地限制臨界區,因為在臨界區中,中斷被禁止 2.大部分異常發生在用戶態,缺頁異常是唯一發生於內核態能觸發的異常 缺頁異常意味著進程切換,因此中斷處理程序從不執行可以導致缺頁的操作 3.中斷處理程序運行於內核態 中斷發生於用戶態時,要把進程的用戶空間堆棧切換到進程的系統空間堆棧,剛切換時,內核堆棧是空的 中斷發生於內核態時, 不需要堆棧空間的切換 三、分類 1.中斷的分類:可屏蔽中斷、不可屏蔽中斷 2.異常的分類: 分類 解決異常的方法 舉例 故障 那條指令會被重新執行 缺頁異常處理程序 陷阱 會從下一條指令開始執行 調試程序
❺ linux top 負載值是如何計算出來的
在Linux 系統中,uptime、w 、top 等命令都會有系統平均負載load average的輸出,那麼什麼是系統平均負載呢?
系統平均負載被定義為在特定時間間隔內運行隊列中的平均進程樹。如果一個進程滿足以下條件則其就會位於運行隊列中:- 它沒有在等待I/O 操作的結果- 它沒有主動進入等待狀態(也就是沒有調用'wait')
- 沒有被停止(例如:等待終止)
❻ linux 中cpu idel 是怎麼被統計出來的
Linux在具有高穩定性、可靠性的同時,具有很好的可伸縮性和擴展性,能夠針對不同的應用和硬體環境調整,優化出滿足當前應用需要的最佳性能。因此企業在維護Linux系統、進行系統調優時,了解系統性能分析工具是至關重要的。
在Linux下有很多系統性能分析工具,比較常見的有top、free、ps、time、timex、uptime等。下文將介紹幾個較為重要的性能分析工具vmstat、iostat和sar及其使用。
用vmstat監視內存使用情況
vmstat是Virtual Meomory Statistics(虛擬內存統計)的縮寫,可對操作系統的虛擬內存、進程、CPU活動進行監視。它是對系統的整體情況進行統計,不足之處是無法對某個進程進行深入分析。
vmstat的語法如下:
vmstat [-V] [-n] [delay [count]]
其中,-V表示列印出版本信息;-n表示在周期性循環輸出時,輸出的頭部信息僅顯示一次;delay是兩次輸出之間的延遲時間;count是指按照這個時間間隔統計的次數。對於vmstat輸出各欄位的含義,可運行man vmstat查看。
用iostat監視I/O子系統情況
iostat是I/O statistics(輸入/輸出統計)的縮寫,iostat工具將對系統的磁碟操作活動進行監視。它的特點是匯報磁碟活動統計情況,同時也會匯報出CPU使用情況。同vmstat一樣,iostat也有一個弱點,就是它不能對某個進程進行深入分析,僅對系統的整體情況進行分析。
iostat的語法如下:
iostat [ -c | -d ] [ -k ] [ -t ] [ -V ] [ -x [ device ] ] [ interval [ count ] ]
其中,-c為匯報CPU的使用情況;-d為匯報磁碟的使用情況;-k表示每秒按kilobytes位元組顯示數據;-t為列印匯報的時間;-v表示列印出版本信息和用法;-x device指定要統計的設備名稱,默認為所有的設備;interval指每次統計間隔的時間;count指按照這個時間間隔統計的次數。
iostat一般的輸出格式如下:
Linux 2.4.18-18smp (builder.linux.com) 2003年03月07日
avg-cpu: %user %nice %sys %idle
4.81 0.01 1.03 94.15
Device: tps Blk_read/s Blk_wrtn/s Blk_read Blk_wrtn
dev3-0 30.31 1117.68 846.52 16104536 12197374
dev3-1 7.06 229.61 40.40 3308486 582080
對於輸出中各欄位的含義,iostat的幫助中有詳細的說明。
使用sar進行綜合分析
表1 sar參數說明
選項 功能
-A 匯總所有的報告
-a 報告文件讀寫使用情況
-B 報告附加的緩存的使用情況
-b 報告緩存的使用情況
-c 報告系統調用的使用情況
-d 報告磁碟的使用情況
-g 報告串口的使用情況
-h 報告關於buffer使用的統計數據
-m 報告IPC消息隊列和信號量的使用情況
-n 報告命名cache的使用情況
-p 報告調頁活動的使用情況
-q 報告運行隊列和交換隊列的平均長度
-R 報告進程的活動情況
-r 報告沒有使用的內存頁面和硬碟塊
-u 報告CPU的利用率
-v 報告進程、i節點、文件和鎖表狀態
-w 報告系統交換活動狀況
-y 報告TTY設備活動狀況
sar是System Activity Reporter(系統活動情況報告)的縮寫。顧名思義,sar工具將對系統當前的狀態進行取樣,然後通過計算數據和比例來表達系統的當前運行狀態。它的特點是可以連續對系統取樣,獲得大量的取樣數據;取樣數據和分析的結果都可以存入文件,所需的負載很小。sar是目前Linux上最為全面的系統性能分析工具之一,可以從14個大方面對系統的活動進行報告,包括文件的讀寫情況、系統調用的使用情況、串口、CPU效率、內存使用狀況、進程活動及IPC有關的活動等,使用也是較為復雜。
sar的語法如下:
sar [-option] [-o file] t [n]
它的含義是每隔t秒取樣一次,共取樣n次。其中-o file表示取樣結果將以二進制形式存入文件file中。
另一種語法如下:
sar [-option] [-s time] [-e time] [-i sec] [-f file]
含義是表示從file文件中取出數據,如果沒有指定-f file,則從標准數據文件/var/adm/sa/sadd取數據,其中dd表示當前天。另外,-s time表示起始時間;-e time表示停止時間;-i sec表示取樣的時間間隔,如果不指定則表示取文件中所有的數據。對於具體的選項參見表1。
一般它與-q和-u聯合使用,以便對每個CPU的使用情況進行分析,比如運行如下命令:
sar -q -u 5 1
將輸出如下:
Linux 2.4.18-18smp (builder.linux.com) 2003年03月07日
09時46分16? CPU %user %nice %system %idle
09時46分21? all 0.20 0.00 0.00 99.80
09時46分16? runq-sz plist-sz ldavg-1 ldavg-5
09時46分21? 0 91 0.00 0.00
Average: CPU %user %nice %system %idle
Average: all 0.20 0.00 0.00 99.80
Average: runq-sz plist-sz ldavg-1 ldavg-5
Average: 0 91 0.00 0.00
由於sar命令太復雜,只有通過熟練使用才能了解每個選項的含義,對於sar輸出中每個欄位的含義運行man sar命令可以得到詳細的解釋。
❼ 誰知道Linux啟動時的idt、gdt、ldt是起什麼作用的
idt中斷向量表,切換進程,虛擬內存,硬體驅動,系統調用等都要用到它
❽ linux的內核運行原理是怎麼樣的呢如何從開機,到載入內核鏡像到內存
當PC啟動時,Intel系列的CPU首先進入的是實模式,並開始執行位於地址0xFFFF0處
的代碼,也就是ROM-BIOS起始位置的代碼。BIOS先進行一系列的系統自檢,然後初始化位
於地址0的中斷向量表。最後BIOS將啟動盤的第一個扇區裝入到0x7C00,並開始執行此處
的代碼。這就是對內核初始化過程的一個最簡單的描述。
最初,linux核心的最開始部分是用8086匯編語言編寫的。當開始運行時,核心將自
己裝入到絕對地址0x90000,再將其後的2k位元組裝入到地址0x90200處,最後將核心的其餘
部分裝入到0x10000。
當系統裝入時,會顯示Loading...信息。裝入完成後,控制轉向另一個實模式下的匯
編語言代碼boot/Setup.S。Setup部分首先設置一些系統的硬體設備,然後將核心從
0x10000處移至0x1000處。這時系統轉入保護模式,開始執行位於0x1000處的代碼。
接下來是內核的解壓縮。0x1000處的代碼來自於文件Boot/head.S,它用來初始化寄
存器和調用decompress_kernel( )程序。decompress_kernel( )程序由Boot/inflate.c,
Boot/unzip.c和Boot../misc.c組成。解壓縮後的數據被裝入到了0x100000處,這也是
linux不能在內存小於2M的環境下運行的主要原因。
解壓後的代碼在0x1010000處開始執行,緊接著所有的32位的設置都將完成: IDT、
GDT和LDT將被裝入,處理器初始化完畢,設置好內存頁面,最終調用start_kernel過程。
這大概是整個內核中最為復雜的部分。
[系統開始運行]
linux kernel 最早的C代碼從匯編標記startup_32開始執行
startup_32:
start_kernel
lock_kernel
trap_init
init_IRQ
sched_init
softirq_init
time_init
console_init
#ifdef CONFIG_MODULES
init_moles
#endif
kmem_cache_init
sti
calibrate_delay
mem_init
kmem_cache_sizes_init
pgtable_cache_init
fork_init
proc_caches_init
vfs_caches_init
buffer_init
page_cache_init
signals_init
#ifdef CONFIG_PROC_FS
proc_root_init
#endif
#if defined(CONFIG_SYSVIPC)
ipc_init
#endif
check_bugs
smp_init
rest_init
kernel_thread
unlock_kernel
cpu_idle
・startup_32 [arch/i386/kernel/head.S]
・start_kernel [init/main.c]
・lock_kernel [include/asm/smplock.h]
・trap_init [arch/i386/kernel/traps.c]
・init_IRQ [arch/i386/kernel/i8259.c]
・sched_init [kernel/sched.c]
・softirq_init [kernel/softirq.c]
・time_init [arch/i386/kernel/time.c]
・console_init [drivers/char/tty_io.c]
・init_moles [kernel/mole.c]
・kmem_cache_init [mm/slab.c]
・sti [include/asm/system.h]
・calibrate_delay [init/main.c]
・mem_init [arch/i386/mm/init.c]
・kmem_cache_sizes_init [mm/slab.c]
・pgtable_cache_init [arch/i386/mm/init.c]
・fork_init [kernel/fork.c]
・proc_caches_init
・vfs_caches_init [fs/dcache.c]
・buffer_init [fs/buffer.c]
・page_cache_init [mm/filemap.c]
・signals_init [kernel/signal.c]
・proc_root_init [fs/proc/root.c]
・ipc_init [ipc/util.c]
・check_bugs [include/asm/bugs.h]
・smp_init [init/main.c]
・rest_init
・kernel_thread [arch/i386/kernel/process.c]
・unlock_kernel [include/asm/smplock.h]
・cpu_idle [arch/i386/kernel/process.c]
start_kernel( )程序用於初始化系統內核的各個部分,包括:
*設置內存邊界,調用paging_init( )初始化內存頁面。
*初始化陷阱,中斷通道和調度。
*對命令行進行語法分析。
*初始化設備驅動程序和磁碟緩沖區。
*校對延遲循環。
最後的function'rest_init' 作了以下工作:
・開辟內核線程'init'
・調用unlock_kernel
・建立內核運行的cpu_idle環, 如果沒有調度,就一直死循環
實際上start_kernel永遠不能終止.它會無窮地循環執行cpu_idle.
最後,系統核心轉向move_to_user_mode( ),以便創建初始化進程(init)。此後,進程0開始進入無限循環。
初始化進程開始執行/etc/init、/bin/init 或/sbin /init中的一個之後,系統內核就不再對程序進行直接控制了。之後系統內核的作用主要是給進程提供系統調用,以及提供非同步中斷事件的處理。多任務機制已經建立起來,並開始處理多個用戶的登錄和fork( )創建的進程。
[init]
init是第一個進程,或者說內核線程
init
lock_kernel
do_basic_setup
mtrr_init
sysctl_init
pci_init
sock_init
start_context_thread
do_init_calls
(*call())-> kswapd_init
prepare_namespace
free_initmem
unlock_kernel
execve
[目錄]
--------------------------------------------------------------------------------
啟動步驟
系統引導:
涉及的文件
./arch/$ARCH/boot/bootsect.s
./arch/$ARCH/boot/setup.s
bootsect.S
這個程序是linux kernel的第一個程序,包括了linux自己的bootstrap程序,
但是在說明這個程序前,必須先說明一般IBM PC開機時的動作(此處的開機是指
"打開PC的電源"):
一般PC在電源一開時,是由內存中地址FFFF:0000開始執行(這個地址一定
在ROM BIOS中,ROM BIOS一般是在FEOOOh到FFFFFh中),而此處的內容則是一個
jump指令,jump到另一個位於ROM BIOS中的位置,開始執行一系列的動作,包
括了檢查RAM,keyboard,顯示器,軟硬磁碟等等,這些動作是由系統測試代碼
(system test code)來執行的,隨著製作BIOS廠商的不同而會有些許差異,但都
是大同小異,讀者可自行觀察自家機器開機時,螢幕上所顯示的檢查訊息。
緊接著系統測試碼之後,控制權會轉移給ROM中的啟動程序
(ROM bootstrap routine),這個程序會將磁碟上的第零軌第零扇區讀入
內存中(這就是一般所謂的boot sector,如果你曾接觸過電腦病
毒,就大概聽過它的大名),至於被讀到內存的哪裡呢? --絕對
位置07C0:0000(即07C00h處),這是IBM系列PC的特性。而位在linux開機
磁碟的boot sector上的正是linux的bootsect程序,也就是說,bootsect是
第一個被讀入內存中並執行的程序。現在,我們可以開始來
看看到底bootsect做了什麼。
第一步
首先,bootsect將它"自己"從被ROM BIOS載入的絕對地址0x7C00處搬到
0x90000處,然後利用一個jmpi(jump indirectly)的指令,跳到新位置的
jmpi的下一行去執行,
第二步
接著,將其他segment registers包括DS,ES,SS都指向0x9000這個位置,
與CS看齊。另外將SP及DX指向一任意位移地址( offset ),這個地址等一下
會用來存放磁碟參數表(disk para- meter table )
第三步
接著利用BIOS中斷服務int 13h的第0號功能,重置磁碟控制器,使得剛才
的設定發揮功能。
第四步
完成重置磁碟控制器之後,bootsect就從磁碟上讀入緊鄰著bootsect的setup
程序,也就是setup.S,此讀入動作是利用BIOS中斷服務int 13h的第2號功能。
setup的image將會讀入至程序所指定的內存絕對地址0x90200處,也就是在內存
中緊鄰著bootsect 所在的位置。待setup的image讀入內存後,利用BIOS中斷服
務int 13h的第8號功能讀取目前磁碟的參數。
第五步
再來,就要讀入真正linux的kernel了,也就是你可以在linux的根目錄下看
到的"vmlinuz" 。在讀入前,將會先呼叫BIOS中斷服務int 10h 的第3號功能,
讀取游標位置,之後再呼叫BIOS 中斷服務int 10h的第13h號功能,在螢幕上輸
出字串"Loading",這個字串在boot linux時都會首先被看到,相信大家應該覺
得很眼熟吧。
第六步
接下來做的事是檢查root device,之後就仿照一開始的方法,利用indirect
jump 跳至剛剛已讀入的setup部份
第七步
setup.S完成在實模式下版本檢查,並將硬碟,滑鼠,內存參數寫入到 INITSEG
中,並負責進入保護模式。
第八步
操作系統的初始化。
❾ Linux系統開機時啟動內核步驟是什麼
實模式,並開始執行位於地址0xFFFF0處
的代碼,也就是ROM-BIOS起始位置的代碼。BIOS先進行一系列的系統自檢,然後初始化位
於地址0的中斷向量表。最後BIOS將啟動盤的第一個扇區裝入到0x7C00,並開始執行此處
的代碼。這就是對內核初始化過程的一個最簡單的描述。
最初,linux核心的最開始部分是用8086匯編語言編寫的。當開始運行時,核心將自
己裝入到絕對地址0x90000,再將其後的2k位元組裝入到地址0x90200處,最後將核心的其餘
部分裝入到0x10000。
當系統裝入時,會顯示Loading...信息。裝入完成後,控制轉向另一個實模式下的匯
編語言代碼boot/Setup.S。Setup部分首先設置一些系統的硬體設備,然後將核心從
0x10000處移至0x1000處。這時系統轉入保護模式,開始執行位於0x1000處的代碼。
接下來是內核的解壓縮。0x1000處的代碼來自於文件Boot/head.S,它用來初始化寄
存器和調用decompress_kernel( )程序。decompress_kernel( )程序由Boot/inflate.c,
Boot/unzip.c和Boot../misc.c組成。解壓縮後的數據被裝入到了0x100000處,這也是
linux不能在內存小於2M的環境下運行的主要原因。
解壓後的代碼在0x1010000處開始執行,緊接著所有的32位的設置都將完成: IDT、
GDT和LDT將被裝入,處理器初始化完畢,設置好內存頁面,最終調用start_kernel過程。
這大概是整個內核中最為復雜的部分。
[系統開始運行]
linux kernel 最早的C代碼從匯編標記startup_32開始執行
startup_32:
start_kernel
lock_kernel
trap_init
init_IRQ
sched_init
softirq_init
time_init
console_init
#ifdef CONFIG_MODULES
init_moles
#endif
kmem_cache_init
sti
calibrate_delay
mem_init
kmem_cache_sizes_init
pgtable_cache_init
fork_init
proc_caches_init
vfs_caches_init
buffer_init
page_cache_init
signals_init
#ifdef CONFIG_PROC_FS
proc_root_init
#endif
#if defined(CONFIG_SYSVIPC)
ipc_init
#endif
check_bugs
smp_init
rest_init
kernel_thread
unlock_kernel
cpu_idle
・startup_32 [arch/i386/kernel/head.S]
・start_kernel [init/main.c]
・lock_kernel [include/asm/smplock.h]
・trap_init [arch/i386/kernel/traps.c]
・init_IRQ [arch/i386/kernel/i8259.c]
・sched_init [kernel/sched.c]
・softirq_init [kernel/softirq.c]
・time_init [arch/i386/kernel/time.c]
・console_init [drivers/char/tty_io.c]
・init_moles [kernel/mole.c]
・kmem_cache_init [mm/slab.c]
・sti [include/asm/system.h]
・calibrate_delay [init/main.c]
・mem_init [arch/i386/mm/init.c]
・kmem_cache_sizes_init [mm/slab.c]
・pgtable_cache_init [arch/i386/mm/init.c]
・fork_init [kernel/fork.c]
・proc_caches_init
・vfs_caches_init [fs/dcache.c]
・buffer_init [fs/buffer.c]
・page_cache_init [mm/filemap.c]
・signals_init [kernel/signal.c]
・proc_root_init [fs/proc/root.c]
・ipc_init [ipc/util.c]
・check_bugs [include/asm/bugs.h]
・smp_init [init/main.c]
・rest_init
・kernel_thread [arch/i386/kernel/process.c]
・unlock_kernel [include/asm/smplock.h]
・cpu_idle [arch/i386/kernel/process.c]
start_kernel( )程序用於初始化系統內核的各個部分,包括:
*設置內存邊界,調用paging_init( )初始化內存頁面。
*初始化陷阱,中斷通道和調度。
*對命令行進行語法分析。
*初始化設備驅動程序和磁碟緩沖區。
*校對延遲循環。
最後的function'rest_init' 作了以下工作:
・開辟內核線程'init'
・調用unlock_kernel
・建立內核運行的cpu_idle環, 如果沒有調度,就一直死循環
實際上start_kernel永遠不能終止.它會無窮地循環執行cpu_idle.
最後,系統核心轉向move_to_user_mode( ),以便創建初始化進程(init)。此後,進程0開始進入無限循環。
初始化進程開始執行/etc/init、/bin/init 或/sbin /init中的一個之後,系統內核就不再對程序進行直接控制了。之後系統內核的作用主要是給進程提供系統調用,以及提供非同步中斷事件的處理。多任務機制已經建立起來,並開始處理多個用戶的登錄和fork( )創建的進程。
[init]
init是第一個進程,或者說內核線程
init
lock_kernel
do_basic_setup
mtrr_init
sysctl_init
pci_init
sock_init
start_context_thread
do_init_calls
(*call())-> kswapd_init
prepare_namespace
free_initmem
unlock_kernel
execve
[目錄]
--------------------------------------------------------------------------------
啟動步驟
系統引導:
涉及的文件
./arch/$ARCH/boot/bootsect.s
./arch/$ARCH/boot/setup.s
bootsect.S
這個程序是linux kernel的第一個程序,包括了linux自己的bootstrap程序,
但是在說明這個程序前,必須先說明一般IBM PC開機時的動作(此處的開機是指
"打開PC的電源"):
一般PC在電源一開時,是由內存中地址FFFF:0000開始執行(這個地址一定
在ROM BIOS中,ROM BIOS一般是在FEOOOh到FFFFFh中),而此處的內容則是一個
jump指令,jump到另一個位於ROM BIOS中的位置,開始執行一系列的動作,包
括了檢查RAM,keyboard,顯示器,軟硬磁碟等等,這些動作是由系統測試代碼
(system test code)來執行的,隨著製作BIOS廠商的不同而會有些許差異,但都
是大同小異,讀者可自行觀察自家機器開機時,螢幕上所顯示的檢查訊息。
緊接著系統測試碼之後,控制權會轉移給ROM中的啟動程序
(ROM bootstrap routine),這個程序會將磁碟上的第零軌第零扇區讀入
內存中(這就是一般所謂的boot sector,如果你曾接觸過電腦病
毒,就大概聽過它的大名),至於被讀到內存的哪裡呢? --絕對
位置07C0:0000(即07C00h處),這是IBM系列PC的特性。而位在linux開機
磁碟的boot sector上的正是linux的bootsect程序,也就是說,bootsect是
第一個被讀入內存中並執行的程序。現在,我們可以開始來
看看到底bootsect做了什麼。
第一步
首先,bootsect將它"自己"從被ROM BIOS載入的絕對地址0x7C00處搬到
0x90000處,然後利用一個jmpi(jump indirectly)的指令,跳到新位置的
jmpi的下一行去執行,
第二步
接著,將其他segment registers包括DS,ES,SS都指向0x9000這個位置,
與CS看齊。另外將SP及DX指向一任意位移地址( offset ),這個地址等一下
會用來存放磁碟參數表(disk para- meter table )
第三步
接著利用BIOS中斷服務int 13h的第0號功能,重置磁碟控制器,使得剛才
的設定發揮功能。
第四步
完成重置磁碟控制器之後,bootsect就從磁碟上讀入緊鄰著bootsect的setup
程序,也就是setup.S,此讀入動作是利用BIOS中斷服務int 13h的第2號功能。
setup的image將會讀入至程序所指定的內存絕對地址0x90200處,也就是在內存
中緊鄰著bootsect 所在的位置。待setup的image讀入內存後,利用BIOS中斷服
務int 13h的第8號功能讀取目前磁碟的參數。
第五步
再來,就要讀入真正linux的kernel了,也就是你可以在linux的根目錄下看
到的"vmlinuz" 。在讀入前,將會先呼叫BIOS中斷服務int 10h 的第3號功能,
讀取游標位置,之後再呼叫BIOS 中斷服務int 10h的第13h號功能,在螢幕上輸
出字串"Loading",這個字串在boot linux時都會首先被看到,相信大家應該覺
得很眼熟吧。
第六步
接下來做的事是檢查root device,之後就仿照一開始的方法,利用indirect
jump 跳至剛剛已讀入的setup部份
第七步
setup.S完成在實模式下版本檢查,並將硬碟,滑鼠,內存參數寫入到 INITSEG
中,並負責進入保護模式。
第八步
操作系統的初始化。