日韩久久久精品,亚洲精品久久久久久久久久久,亚洲欧美一区二区三区国产精品 ,一区二区福利

lguest 三步曲之三 (源碼分析)

系統(tǒng) 1862 0

lguest上的guest os啟動(dòng)的過(guò)程


根據(jù)linux啟動(dòng)流程的分析,在執(zhí)行到j(luò)mp *0xc0100000時(shí),系統(tǒng)將會(huì)根據(jù)是壓縮內(nèi)核還是未壓縮的內(nèi)核來(lái)決定跳轉(zhuǎn)的方向:
(1)如果是未壓縮的內(nèi)核,就直接跳到/kernel/head_32.S的入口開(kāi)始執(zhí)行
(2)如果是壓縮的內(nèi)核,就要先解壓,整個(gè)解壓的過(guò)程在/boot/compressed/head_32.S中,解壓完成后跳到解壓內(nèi)核的起始地址開(kāi)始執(zhí)行其實(shí)解壓后的起始地址,也是/kernel/head_32.S的入口。

因此不管是壓縮的內(nèi)核還是未壓縮的內(nèi)核,都會(huì)執(zhí)行/kernel/head_32.S中的代碼。這是可以確定0xc0100000處的第一條代碼就是startup_32.
現(xiàn)在我們就從/kernel/head_32.S開(kāi)始分析。
(1)重新加載boot_gdt_desc和段寄存器的值
(2)清空bss段
(3)把實(shí)模式下的boot_params,拷貝到保護(hù)模式下的boot_params結(jié)構(gòu)體中
(4)movl pa(boot_params) + NEW_CL_POINTER,%esi // %esi指向了 setup_header.cmd_line_ptr
判斷一下命令行參數(shù)指針是否為空,如果不為空,就要把命令行的參數(shù)拷貝過(guò)來(lái),拷貝到boot_command_line數(shù)組中。
下面就開(kāi)始判斷,如果定義了CONFIG_PARAVIRT的話,即支持虛擬化對(duì)的話,就要選擇到底執(zhí)行哪條內(nèi)核路徑,這里有三條路徑:
1 default_entry:這是默認(rèn)的系統(tǒng)啟動(dòng)路徑
2 bad_subarch: 當(dāng)前內(nèi)核不支持的啟動(dòng)路徑
3當(dāng)前內(nèi)核支持的兩種虛擬化啟動(dòng)路徑:lguest_entry 和 xen_entry

相當(dāng)于定義了一個(gè)內(nèi)核路徑數(shù)組,數(shù)組名是subarch_entries,元素個(gè)數(shù)是num_subarch_entries,然后在尋址的時(shí)候是這樣的:

eax = 0 + eax*4 + pa(subarch_entries)。因?yàn)槲覀兎治龅氖莑guest , 因此這里的跳到lguest_entry。

有個(gè)很重要的數(shù)據(jù)結(jié)構(gòu),在這里必須要介紹一下: boot_params 結(jié)構(gòu)體,它就是傳說(shuō)中的"zero page".


這個(gè)數(shù)據(jù)結(jié)構(gòu)幾乎保存了啟動(dòng)過(guò)程中所需要的所有的信息,比如屏幕顯示信息,hdr,e820返回的內(nèi)存信息等等,在后面的啟動(dòng)程序中,很多地方都用到了這個(gè)數(shù)據(jù)結(jié)構(gòu)中的參數(shù),

很明顯這是個(gè)循環(huán)復(fù)制的一組指令,目的地址就是boot_params, 那么源地址是哪里呢?其實(shí),當(dāng)跳到startup_32時(shí),%esi還是執(zhí)行實(shí)模式下的數(shù)據(jù)boot_params。應(yīng)該豁然開(kāi)朗了,

實(shí)模式下的數(shù)據(jù)在保護(hù)模式下是不可用的,因此要拷貝過(guò)來(lái)。為了驗(yàn)證esi到底是不是指向boot_params,看下面的代碼:

但是,boot_params到底是什么時(shí)候被初始化的呢?這也是我們比較關(guān)心的一個(gè)問(wèn)題。

大部分的初始化的代碼包含在arch/x86/boot/Main.c中
(1)copy_boot_params(); 初始化的boot_params.hdr
(2)detect_memory(void);
初始化了boot_params.e820_map 和boot_params.e820_entries
(3)query_apm_bios(); 初始化了apm_bios_info
(4)query_apm_bios(); 初始化了screen_info

到此,boot_params這個(gè)數(shù)據(jù)結(jié)構(gòu)介紹完了,后面很多代碼都會(huì)從這個(gè)結(jié)構(gòu)體里面取數(shù)據(jù)。繼續(xù)分析lguest執(zhí)行流程lguest_entry.

在arch/x86/lguest/i386_head.S中找到了ENTRY(lguest_entry)

我們把這段代碼總整體上來(lái)看,就是實(shí)現(xiàn)了這么一個(gè)操作,%eax = $LHCALL_LGUEST_INIT %ebx=lguest_data 數(shù)據(jù)結(jié)構(gòu)的物理地址
后面又創(chuàng)建了一個(gè)堆棧,然后又調(diào)用了一個(gè)c函數(shù)lguest_init .把以上的信息串聯(lián)起來(lái),豈不就相當(dāng)于在匯編里調(diào)用c函數(shù)的整個(gè)準(zhǔn)備過(guò)程,

先設(shè)置好參數(shù)(%eax,%ebx),然后設(shè)置好堆棧。

LHCALL_LGUEST_INIT是 lguest實(shí)現(xiàn)的hypercall 調(diào)用號(hào),就想我們熟悉的linux的其他的系統(tǒng)調(diào)用,只不過(guò)是lguest的系統(tǒng)調(diào)用而已。在調(diào)用

系統(tǒng)調(diào)用前,要把系統(tǒng)調(diào)用號(hào)保存在eax中,把參數(shù)保存在ebx等其他的幾個(gè)寄存器中。然后.byte 0×0f ,0×01,0xc1就是執(zhí)行系統(tǒng)調(diào)用,相當(dāng)于

int $0×80,這么做的目的無(wú)非就是通知host os,當(dāng)前運(yùn)行的是一個(gè)guest os.

這里也涉及到一個(gè)非常重要的數(shù)據(jù)結(jié)構(gòu)lguest_data, 分析下這個(gè)結(jié)構(gòu)體。

這個(gè)數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)了Host和Guest之間進(jìn)行交流的一種方法
/*G:032 The second method of communicating with the Host is to via "struct
* lguest_data". Once the Guest's initialization hypercall tells the Host where
* this is, the Guest and Host both publish information in it. :*/
詳細(xì)分析一下每一個(gè)數(shù)據(jù)成員的含義:
(1)irq_enabled
/* 512 == enabled (same as eflags in normal hardware). The Guest
* changes interrupts so often that a hypercall is too slow. */
相當(dāng)于Host里面的eflags, 512=2^9,即第10位置1,就表示開(kāi)中斷。這么做的原因是:如果通過(guò)hypercall 來(lái)實(shí)現(xiàn)中斷的使能的話,太慢了!

(2)DECLARE_BITMAP(blocked_interrupts, LGUEST_IRQS);

定義了一個(gè)bitmap,用來(lái)做中斷屏蔽的???
(3)CR2 :Guest缺頁(yè)中斷時(shí)會(huì)在CR2中保存一個(gè)hypercall,Host在這里寫(xiě)上一次page fault的虛擬地址
(4)time : Host設(shè)置的時(shí)間
(5)hcall_status[LHCALL_RING_SIZE] LHCALL_RING_SIZE=64
/* Async hypercall ring. Instead of directly making hypercalls, we can
* place them in here for processing the next time the Host wants.
* This batching can be quite efficient. */
/* 0xFF == done (set by Host), 0 == pending (set by Guest). */
并不是每產(chǎn)生一個(gè)hypercall,Host就對(duì)它進(jìn)行處理,可以先讓這些hypercall 排隊(duì),主機(jī)在某一個(gè)時(shí)間來(lái)對(duì)他們進(jìn)行處理。0xFF表示Host處理完了所有的pending的hypercall, 0表示有Guest的hypercall在pending.
(6)reserve_mem: 指明給switcher保留的空間的大小(主機(jī)初始化)
(7)設(shè)置TSC的 頻率(Host初始化)
(以下變量guest 在初始化時(shí)設(shè)置)
(8)noirq_start,noirq_end: 不允許中斷的一段指令的范圍,即使此時(shí)是開(kāi)中斷的;
(9)kernel_address = PAGE_OFFSET
(10)syscall_vec: 系統(tǒng)調(diào)用號(hào)0×80

下面我們就跳到arch/x86/lguest/boot.c 中的lguest_init()函數(shù)來(lái)執(zhí)行。
這個(gè)函數(shù)主要是對(duì)內(nèi)核的一些敏感操作進(jìn)行的封裝或這說(shuō)是替換,主要有一下幾個(gè)方面的封裝:
(1)中斷相關(guān)的操作
(2)cpu指令的封裝
(3)頁(yè)表管理
(4)apic的讀寫(xiě)操作
(5)時(shí)間相關(guān)操作
對(duì)以上部分封裝之后,接著又執(zhí)行了下面一系列的操作:
(1)reserve_top_address(lguest_data.reserve_mem);
為Host<->Guest Switcher 保留一段內(nèi)存空間,這段內(nèi)存空間的大小由lguest_data.reserve_mem 指定。
(2)lockdep_init()
這個(gè)函數(shù)分別申請(qǐng)4096個(gè)classhash_table和8192個(gè)chainhash_table.這兩個(gè)hashtable到底是用來(lái)he干什么的,現(xiàn)在還不清楚?
(3)para_virt_disable_iospace()
禁止所有的非虛擬驅(qū)動(dòng)程序去掃描它所支持的硬件設(shè)備,減少啟動(dòng)時(shí)間
(4)cpu_detect(&new_cpu_data)
(5)add_preferred_console(“hvc”,0,NULL);
注冊(cè)hvc這個(gè)虛擬終端的驅(qū)動(dòng)
(6)virtio_cons_early_init(early_put_chars);
(7)pm_power_off = lguest_power_off
machine_ops.restart=lguest_restart
(9)i386_start_kernel

lguest 三步曲之三 (源碼分析)


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號(hào)聯(lián)系: 360901061

您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對(duì)您有幫助就好】

您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長(zhǎng)會(huì)非常 感謝您的哦!!!

發(fā)表我的評(píng)論
最新評(píng)論 總共0條評(píng)論
主站蜘蛛池模板: 永仁县| 涿州市| 西安市| 邻水| 米林县| 九江县| 福建省| 淮阳县| 新河县| 大庆市| 涿鹿县| 斗六市| 奉节县| 鹤山市| 托克托县| 永靖县| 苗栗市| 广东省| 沿河| 枣阳市| 奇台县| 南陵县| 雷山县| 改则县| 沙湾县| 兴和县| 江阴市| 确山县| 漳浦县| 高唐县| 聊城市| 监利县| 阳东县| 崇阳县| 穆棱市| 荃湾区| 习水县| 德安县| 太谷县| 鹤峰县| 新乐市|