99精品伊人亚洲|最近国产中文炮友|九草在线视频支援|AV网站大全最新|美女黄片免费观看|国产精品资源视频|精彩无码视频一区|91大神在线后入|伊人终合在线播放|久草综合久久中文

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

zircon微內(nèi)核啟動(dòng)代碼分析

yzcdx ? 來(lái)源:OS與AUTOSAR研究 ? 2023-06-11 09:14 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

1. C++入口函數(shù)lk_main

e12dd6c4-07ef-11ee-962d-dac502259ad0.png

Zircon微內(nèi)核的代碼是用C++寫的,C++和C的基礎(chǔ)語(yǔ)法差不多,C++新加入了一些面向?qū)ο蟮臇|西,在內(nèi)核中沒(méi)有界面化那些C++的API的情況下,區(qū)別基本就是C語(yǔ)言結(jié)構(gòu)體的回調(diào)函數(shù)是一個(gè).C++則是::,不用另外學(xué)習(xí)C++語(yǔ)法就可以看懂,但是為什么zircon使用了面向?qū)ο笳Z(yǔ)言C++?這里你必須對(duì)面向?qū)ο蟮乃枷胗凶罨镜恼J(rèn)識(shí),首先就是抽象出來(lái)共同的屬性操作,這樣就不用寫代碼對(duì)不同的模塊寫重復(fù)的代碼,而且結(jié)構(gòu)更加的清晰,其次對(duì)象的安全性也更好,可以參考zCore入門-面向?qū)ο蟮腞ust微內(nèi)核里面介紹的面向?qū)ο驩S設(shè)計(jì)的好處,再次印證那句“編程語(yǔ)言和操作系統(tǒng)的設(shè)計(jì)是相輔相成的”。

進(jìn)入正題系統(tǒng)啟動(dòng)整體流程為:kernel-》userboot-》bootfs鏡像-》文件系統(tǒng)-》可執(zhí)行文件-》組件管理器-》拉起進(jìn)程。

先說(shuō)kernel的啟動(dòng),lk_main()入口函數(shù)在代碼中的位置在kernel/top/main.cc中

//日志打印開(kāi)關(guān)定義
dlog_bypass_init_early();
thread_init_early():
percpu::InitializeBoot();


//創(chuàng)建一個(gè)precpu的對(duì)象
thread_t*t=&percpu::Get(0).idle_thread;//找到cpu0對(duì)應(yīng)的空閑線程
thread_construct_first(t,"bootstrap");

這t就是一個(gè)線程,線程在zircon中也是一個(gè)對(duì)象,相關(guān)的對(duì)象為Job->Process->Thread

e15c47d4-07ef-11ee-962d-dac502259ad0.png

Zircon 公開(kāi)了三個(gè)運(yùn)行代碼的主要內(nèi)核對(duì)象:

線程:給定地址空間內(nèi)的執(zhí)行線程。

進(jìn)程:在私有、隔離的地址空間中運(yùn)行的一組可執(zhí)行指令。

作業(yè):一組相關(guān)的流程和作業(yè)。所有作業(yè)形成一個(gè)單根樹。

線程對(duì)象是表示分時(shí)CPU 執(zhí)行上下文的構(gòu)造。Thread 對(duì)象與特定的Process Object相關(guān)聯(lián),它為 I/O 和計(jì)算所需的其他對(duì)象提供內(nèi)存和句柄。

線程是調(diào)度的基本單位,并且調(diào)度有優(yōu)先級(jí),在thread_construct_first()函數(shù)中,會(huì)設(shè)置線程的信息,例如base_priority優(yōu)先級(jí)為HIGHEST_PRIORITY,set_current_thread設(shè)置此線程為當(dāng)前正在運(yùn)行的thread

list_add_head(&thread_list,&t->thread_list_node);//把線程加入線程表中,這樣調(diào)度的時(shí)候就可以找到這個(gè)線程。

call_constructors();//調(diào)用全局構(gòu)造函數(shù)

for (void (*const* a)() = __init_array_start; a != __init_array_end;a++)(*a)();

函數(shù)上打了attribute((constructor))則為全局構(gòu)造函數(shù),編譯器將其編譯到.init 段,而__init_array_start 和__init_array_end 是該段的開(kāi)始和結(jié)尾。

2. 內(nèi)核分層初始化lk_primary_cpu_init_level

在zircon中對(duì)于系統(tǒng)啟動(dòng)進(jìn)行分層設(shè)計(jì),這樣結(jié)構(gòu)很明朗好理解,而且新添加內(nèi)容的時(shí)候方便找到對(duì)應(yīng)的地方,在出錯(cuò)調(diào)試的時(shí)候也更加的方便,真是簡(jiǎn)單實(shí)用又高深的方法,大道至簡(jiǎn)也就這樣吧,在我們的編程中可以借鑒。

lk_primary_cpu_init_level(LK_INIT_LEVEL_EARLIEST,LK_INIT_LEVEL_ARCH_EARLY-1);
lk_init_level(LK_INIT_FLAG_PRIMARY_CPU,start_level,stop_level);


for(conststructlk_init_struct*ptr=__start_lk_init;ptr!=__stop_lk_init;ptr++){
  /* keep the lowest one we haven't called yet */
if(ptr->level>=start_level&&ptr->level>last_called_level){
    found = ptr;
    continue;
  }
if(ptr->level==last_called_level&&ptr!=last&&seen_last){
    found = ptr;
    break;
}
found->hook(found->level);

可見(jiàn)去__start_lk_init結(jié)構(gòu)體數(shù)組中按照l(shuí)evel找元素,找到就執(zhí)行hook()回調(diào)函數(shù)。

在kernel/kernel.ld中定義

PROVIDE_HIDDEN(__start_lk_init = .);
KEEP(*(.data.rel.ro.lk_init))
PROVIDE_HIDDEN(__stop_lk_init = .);

Ld文件規(guī)定了二進(jìn)制文件的組織形式,在數(shù)據(jù)段中存在這個(gè)數(shù)組,這個(gè)數(shù)組里面的元素按照.data.rel.ro. lk_init字符串去組織

在kernel/include/lk/init.h中

#define LK_INIT_HOOK_FLAGS(_name, _hook,
_level, _flags) 
__ALIGNED(sizeof(void *)) __USED
__SECTION(".data.rel.ro.lk_init") 
static const struct lk_init_struct _init_struct_##_name = {         
.level = _level,                                               
.flags = _flags,                                               
.hook = _hook,                                                 
.name = #_name,                                                 
};
#define LK_INIT_HOOK(_name, _hook, _level)

LK_INIT_HOOK_FLAGS(_name, _hook, _level, LK_INIT_FLAG_PRIMARY_CPU)

LK_INIT_HOOK這個(gè)宏用于聲明這個(gè)數(shù)據(jù)段的結(jié)構(gòu)體,我們添加初始化的模塊函數(shù)時(shí)就可以在模塊尾部聲明這個(gè)LK_INIT_HOOK。

LK_INIT_LEVEL_EARLIEST這個(gè)level的結(jié)構(gòu)體不存在,只是空跑下算初始化

lk_primary_cpu_init_level(LK_INIT_LEVEL_ARCH_EARLY,LK_INIT_LEVEL_PLATFORM_EARLY - 1);

就會(huì)執(zhí)行

kernel/lib/code_patching/code_patching.cc中apply_startup_code_patches函數(shù)

LK_INIT_HOOK(code_patching,apply_startup_code_patches,

LK_INIT_LEVEL_ARCH_EARLY)

這個(gè)函數(shù)好像沒(méi)做什么。

3. 使用chatGPT解釋看不懂代碼

arch_early_init();

x86_mmu_early_init();

mmu就是內(nèi)存管理單元,主要負(fù)責(zé)物理內(nèi)存和虛擬內(nèi)存的映射,這里是early_init,具體不詳細(xì)說(shuō)明了,這里說(shuō)一個(gè)技巧那就是chatGPT,直接把代碼復(fù)制到chatGPT提問(wèn)讓解析一下

e1cd05f0-07ef-11ee-962d-dac502259ad0.png

最近我也經(jīng)常使用chatGPT,而且越難的東西它越擅長(zhǎng),這里說(shuō)的難就是用的人少,需要大量經(jīng)驗(yàn),難入門的東西,一個(gè)典型就是匯編語(yǔ)言,只是看匯編就交給chatGPT,自己完全沒(méi)必要學(xué)習(xí)匯編,學(xué)了又會(huì)忘記

chatGPT有一個(gè)缺點(diǎn)就是不保真,需要你是一個(gè)行內(nèi)的人,你提問(wèn)它回答給你啟發(fā),你可以自己大致的辨別有用的信息,并且求證,這不就是導(dǎo)師干的事么,最重要的還是方向啊,現(xiàn)在最牛逼的老師領(lǐng)進(jìn)門了,那修行還不事半功倍。

platform_early_init(void)

對(duì)x86來(lái)說(shuō)kernel/platform/pc/platform.cc中實(shí)現(xiàn)

初始化串口,之后串口就可以使用了

platform_save_bootloader_data
if (_zbi_base != NULL) {
zbi_header_t* bd = (zbi_header_t*)X86_PHYS_TO_VIRT(_zbi_base);
process_zbi(bd, (uintptr_t)_zbi_base);
}

_zbi_base在kernel/arch/x86/start.S中賦值,multiboot傳給start.S的是imag的基地址

// _zbi_base is in bss, so now it's safe to set it.

mov %ebx,PHYS(_zbi_base)

這個(gè)zbi類型的數(shù)據(jù),之前介紹過(guò)zbi,zbi有很多類型,這里為"CONTAINER"

boot_reserve_init();保留kernel所在的區(qū)域

PMM: boot reserve add [0x100000, 0x334fff]
platform_preserve_ramdisk();保留ramdis所在的區(qū)域
PMM: boot reserve add [0xb4e000, 0x1598fff]
pc_mem_init-》platform_mem_range_init-》mem_arena_init-》pmm_add_arena-》
PmmArena* arena = new
(boot_alloc_mem(sizeof(PmmArena))) PmmArena();

會(huì)分配boot內(nèi)存0x1599000,0x1599040 這個(gè)都是物理內(nèi)存如果是虛擬內(nèi)存轉(zhuǎn)換關(guān)系為:

paddr_to_physmap ()中:

"0xffffff8000000000UL" + 物理內(nèi)存= 虛擬內(nèi)存

lk_primary_cpu_init_level(LK_INIT_LEVEL_PLATFORM_EARLY,LK_INIT_LEVEL_TARGET_EARLY - 1);關(guān)于平臺(tái)早期初始化的鉤子函數(shù),主要是一些驅(qū)動(dòng)、中斷

LK_INIT_HOOK(platform_dev_init,platform_dev_init, LK_INIT_LEVEL_PLATFORM)

這個(gè)會(huì)初始化一堆的驅(qū)動(dòng),驅(qū)動(dòng)從zbi中找到ZBI_TYPE_KERNEL_DRIVER類型的,繼續(xù)執(zhí)行pdev_init_driver()會(huì)執(zhí)行.data.rel.ro.lk_pdev_init數(shù)據(jù)段中結(jié)構(gòu)體初始化,

LK_PDEV_INIT驅(qū)動(dòng)通過(guò)這個(gè)宏就可以注冊(cè)到.data.rel.ro.lk_pdev_init

然后就可以看到我們的打印了

vm_init_preheap();創(chuàng)建供內(nèi)核使用的虛存空間VmAspace

上面提到boot的內(nèi)存起始為0x1599000,分配到了0x1599040,

MarkPagesInUsePhys在頁(yè)表中標(biāo)記內(nèi)存頁(yè)已經(jīng)使用

heap_init();

Zircon 的內(nèi)核堆由內(nèi)部的 cmpctmalloc 實(shí)現(xiàn)。

cmpct_init

vm_init();虛擬內(nèi)存初始化

找到內(nèi)核鏡像的各個(gè)段,及初始化讀寫策略,例如

regions[] = {
{
.name = "kernel_code",
.base = (vaddr_t)__code_start,
.size = ROUNDUP((uintptr_t)__code_end - (uintptr_t)__code_start,
PAGE_SIZE),
.arch_mmu_flags = ARCH_MMU_FLAG_PERM_READ | ARCH_MMU_FLAG_PERM_EXECUTE,
},

// 遍歷上面的幾個(gè)段,并設(shè)置策略

for (uint i = 0; i < fbl::count_of(regions); ++i) {
temp_region* region = ®ions[i];
ASSERT(IS_PAGE_ALIGNED(region->base));
dprintf(INFO, "VM: reserving kernel region [%#" PRIxPTR
", %#" PRIxPTR ") flags %#x name '%s'
",
region->base,
region->base + region->size, region->arch_mmu_flags, region->name);
// 在vmm中標(biāo)記一塊虛擬內(nèi)存,這塊虛擬內(nèi)存抽象為VmRegion類,擁有自己的底層mmu相關(guān)的配置
zx_status_t status = aspace->ReserveSpace(region->name,
region->size, region->base);
ASSERT(status == ZX_OK);
// 對(duì)某VmRegion對(duì)應(yīng)的虛擬內(nèi)存設(shè)置內(nèi)存保護(hù)的相關(guān)參數(shù)
status = ProtectRegion(aspace, region->base, region->arch_mmu_flags);
ASSERT(status == ZX_OK);
}
// 標(biāo)記映射表
// reserve the kernel aspace where the physmap is
aspace->ReserveSpace("physmap", PHYSMAP_SIZE,
PHYSMAP_BASE);

·ReserveSpace:在 vmm 中標(biāo)記一塊虛擬內(nèi)存,這塊虛擬內(nèi)存抽象為VmRegion類,擁有自己的底層mmu相關(guān)的配置

·ProtectRegion:對(duì)某VmRegion對(duì)應(yīng)的虛擬內(nèi)存設(shè)置內(nèi)存保護(hù)的相關(guān)參數(shù)

物理內(nèi)存一塊區(qū)域存儲(chǔ)的數(shù)據(jù),可以映射為內(nèi)核里面一個(gè)VmRegion類,這個(gè)類里面有其虛擬內(nèi)存信息,這一過(guò)程就是內(nèi)存映射。

kernel_init();

mp_init();多核初始化

// 多核初始化
void mp_init(void) {
// 核間中斷任務(wù)表初始化
mp.ipi_task_lock = SPIN_LOCK_INITIAL_VALUE;
for (uint i = 0; i < fbl::count_of(mp.ipi_task_list); ++i) {
list_initialize(&mp.ipi_task_list[i]);
}
}

thread_create("bootstrap2",&bootstrap2, NULL, DEFAULT_PRIORITY);

4.bootstrap2線程

創(chuàng)建 bootstrap2 線程加入thread_list列表,由 bootstrap2 線程完成剩下的初始化工作,等到下次調(diào)度的時(shí)候,這個(gè)線程會(huì)運(yùn)行。

thread_become_idle();變成idle進(jìn)程,并且開(kāi)啟中斷啟動(dòng)調(diào)度

sched_reschedule();啟動(dòng)線程調(diào)度

arch_enable_ints();開(kāi)啟中斷

進(jìn)入bootstrap2線程

arch_init(); CPU 架構(gòu)初始化,這里為x86 intel平臺(tái)cpu的一些初始化,包括mmu、gdt、idt、tarce

-》platform_init(); 平臺(tái)初始化

platform_init_smp多cpu管理

-》x86_bringup_aps-》

status =

x86_bootstrap16_acquire((uintptr_t)_x86_secondary_cpu_long_mode_entry,

&bootstrap_aspace, (void**)&bootstrap_data,

&bootstrap_instr_ptr);

_x86_secondary_cpu_long_mode_entry是第二個(gè)cpu啟動(dòng)執(zhí)行的地址

之后會(huì)啟動(dòng)其他cpu進(jìn)入_x86_secondary_cpu_long_mode_entry執(zhí)行

x86_secondary_entry-》

finish_secondary_entry-》

lk_secondary_cpu_entry-》

thread_secondary_cpu_entry-》

thread_exit開(kāi)啟本cpu調(diào)度

pc_init_smbios

SMBIOS(System Management BIOS)是主板或系統(tǒng)制造者以標(biāo)準(zhǔn)格式顯示產(chǎn)品管理信息所需遵循的統(tǒng)一規(guī)范。

打印出來(lái)為:smbios: manufacturer="QEMU"product="Standard PC (Q35 + ICH9, 2009)"

lk_primary_cpu_init_level(LK_INIT_LEVEL_TARGET, LK_INIT_LEVEL_LAST);

初始化下面幾個(gè)模塊:

* debuglog in $zx/kernel/lib/debuglog/debuglog.cc
* kcounters in $zx/kernel/lib/counters/counters.cc
* ktrace in $zx/kernel/lib/ktrace/ktrace.cc
* kernel_shell in $zx/kernel/lib/console/console.cc
* userboot in $zx/kernel/lib/userabi/userboot.cc

LK_INIT_HOOK(userboot, userboot_init,LK_INIT_LEVEL_USER)中userboot_init函數(shù)會(huì)啟動(dòng)

5. Userboot進(jìn)程

e20d2b76-07ef-11ee-962d-dac502259ad0.png

kernel-》userboot-》bootfs鏡像-》文件系統(tǒng)-》可執(zhí)行文件-》組件管理器-》拉起進(jìn)程

關(guān)于userboot初始化

zx_status_t status = MessagePacket::Create創(chuàng)建一個(gè)新的MessagePacket msg

Handle** const handles =msg->mutable_handles()創(chuàng)建一個(gè)可變引用handles指向其中的handle數(shù)組,這個(gè)數(shù)組里面存了很多信息

enum HandleIndex : uint32_t {
// These describe userboot itself.
kProcSelf, // 記錄新進(jìn)程中指向新進(jìn)程本身的handle號(hào)
kVmarRootSelf, // 記錄為新進(jìn)程創(chuàng)建的vmar的handle
kRootJob, //一個(gè)process屬于一個(gè)job
kRootResource,
// Essential VMO handles.
kZbi,
kFirstVdso,
kLastVdso = kFirstVdso + static_cast(VdsoVariant::COUNT)
- 1,
// These get passed along to userland to be recognized by ZX_PROP_NAME.
// The decompressor is also used by userboot itself.
// The remainder are VMO handles that userboot doesn't care about.
kUserbootDecompressor,
kFirstKernelFile = kUserbootDecompressor,
kCrashlog,
kCounterNames,
kCounters,
#if ENABLE_ENTROPY_COLLECTOR_TEST
kEntropyTestData,
#endif
kFirstInstrumentationData,
kHandleCount = kFirstInstrumentationData +
InstrumentationData::vmo_count()
};

創(chuàng)建process,其包含一個(gè)vmar,每個(gè)進(jìn)程都從一個(gè)單一的虛擬內(nèi)存地址區(qū)域 (VMAR) 開(kāi)始,進(jìn)程根VMAR 跨越整個(gè)用戶地址空間。VMAR 用于映射虛擬內(nèi)存對(duì)象(VMO),虛擬內(nèi)存對(duì)象將程序所需的代碼、數(shù)據(jù)、匿名和共享內(nèi)存頁(yè)面提供到進(jìn)程的地址空間中。

// It also gets many VMOs for VDSOs and other things.

const VDso* vdso = VDso::Create();

vdso->GetVariants(&handles[kFirstVdso]);

vDSO(virtualDynamic Shared Object),Zircon vDSO 是 Zircon 內(nèi)核訪問(wèn)系統(tǒng)調(diào)用的唯一方法(作為系統(tǒng)調(diào)用的跳板)。它之所以是虛擬的,是因?yàn)樗皇菑奈募到y(tǒng)中的ELF文件加載的,而是由內(nèi)核直接提供的vDSO鏡像

bootstrap_vmos(handles);
EmbeddedVmo decompress_zbi("lib/hermetic/decompress-zbi.so",
decompress_zbi_image,
DECOMPRESS_ZBI_DATA_END);
handles[kUserbootDecompressor] =
decompress_zbi.vmo_handle().release();

這里開(kāi)始解壓縮ZBI格式的ramdisk文件,之前我們知道鏡像分為了kernel+ranmdisk,現(xiàn)在kernel初始化差不多了,要開(kāi)始讀這個(gè)ramdis里面的內(nèi)容了,原則上這里面的內(nèi)容都是用戶進(jìn)程相關(guān)的東西。

e25b2164-07ef-11ee-962d-dac502259ad0.png

platform_get_ramdisk(&rsize);

ramdisk_base和ramdisk_size

zx_status_t status = VmObjectPaged::CreateFromWiredPages(

rbase, rsize, true, &rootfs_vmo);

創(chuàng)建一個(gè)vmo對(duì)象,就是虛擬內(nèi)存對(duì)象,指向這個(gè)ramdisk

get_vmo_handle(rootfs_vmo, false, nullptr,&handles[kZbi]);

handles的kZbi里面存的這個(gè)vmo

e29f5834-07ef-11ee-962d-dac502259ad0.png

status = get_vmo_handle(ktl::move(kcounters_vmo), true, nullptr,

&handles[kCounters]);

內(nèi)核計(jì)數(shù)器放入handles

// Make the channel that will hold the message.
KernelHandle user_handle, kernel_handle;
status = ChannelDispatcher::Create(&user_handle, &kernel_handle,
&rights);
ASSERT(status ==
ZX_OK);

創(chuàng)建一個(gè)channel,有兩頭一個(gè)頭是user一頭是kernel,用于通信

status = kernel_handle.dispatcher()->Write(ZX_KOID_INVALID,

ktl::move(msg));

內(nèi)核側(cè)先寫入點(diǎn)數(shù)據(jù)

process->AddHandle(ktl::move(user_handle_owner));

用戶進(jìn)程把這個(gè)channel通過(guò)handle綁定到自己身上

Job->Process->handle->channel

status = userboot.Map(vmar, &vdso_base,&entry);

映射就是把vmo映射到vmar上面,vmar是process里面的數(shù)據(jù)。Entry就是userboot的入口地址,vdso_base就是進(jìn)行系統(tǒng)調(diào)用的基地址

// Map userboot proper.
status = RoDso::Map(vmar_handle.dispatcher(), 0);
if (status == ZX_OK) {
*entry =
vmar_handle.dispatcher()->vmar()->base() + USERBOOT_ENTRY;
// Map the vDSO right after it.
*vdso_base =
vmar_handle.dispatcher()->vmar()->base() + RoDso::size();
// Releasing |vmar_handle| is safe because it has a no-op
// on_zero_handles(), otherwise the mapping routines would have
// to take ownership of the handle and manage its lifecycle.
status = vdso_->Map(vmar_handle.release(), RoDso::size());
}

6. userboot如何在vDSO中取得系統(tǒng)調(diào)用

當(dāng)內(nèi)核將userboot映射到第一個(gè)用戶進(jìn)程時(shí),會(huì)像正常程序那樣,在內(nèi)存中選擇一個(gè)隨機(jī)地址進(jìn)行加載。而在映射userboot的vDSO時(shí),并不采用上述隨機(jī)的方式,而是將vDSO映像直接放在內(nèi)存中userboot的映像之后。這樣一來(lái),vDSO代碼與userboot的偏移量總是固定的。

在編譯階段中,系統(tǒng)調(diào)用的入口點(diǎn)符號(hào)表會(huì)從vDSO ELF映像中提取出來(lái),隨后寫入到鏈接腳本的符號(hào)定義中。利用每個(gè)符號(hào)在vDSO映像中相對(duì)固定的偏移地址,可在鏈接腳本提供的_end符號(hào)的固定偏移量處,定義該符號(hào)。通過(guò)這種方式,userboot代碼可以直接調(diào)用到放在內(nèi)存中,其映像本身之后的,每個(gè)確切位置上的vDSO入口點(diǎn)。

vdso會(huì)映射到userboot的vmar中

status = VmObjectPaged::Create(PMM_ALLOC_FLAG_ANY, 0u,
stack_size, &stack_vmo);
status = vmar->Map(0,
    ktl::move(stack_vmo), 0, stack_size,
ZX_VM_PERM_READ |
ZX_VM_PERM_WRITE,
&stack_mapping);

新建一個(gè)堆棧的vmo,然后映射到vmar上面

uintptr_t sp =

compute_initial_stack_pointer(stack_base, stack_size);

計(jì)算線程的棧地址

status =

ThreadDispatcher::move(process), 0, "userboot",

&(), &rights);

創(chuàng)建userboot線程,線程是調(diào)度的基本單位。

auto arg1 = static_cast(hv);     // 傳給userboot線程的第一個(gè)參數(shù)為一個(gè)Handle的指針的編號(hào)(實(shí)例在process結(jié)構(gòu)中)   
// 第二個(gè)參數(shù)為vdso的基地址
status = thread->Start(
ThreadDispatcher::EntryState{entry, sp, arg1, vdso_base},
參數(shù)為:入口地址、堆棧地址、handels、vdso地址

kernel如何啟用userboot?

與任何其他進(jìn)程一樣,userboot必須從已經(jīng)映射到其地址空間的vDSO開(kāi)始,這樣它才能進(jìn)行系統(tǒng)調(diào)用。內(nèi)核將userboot和vDSO映射到第一個(gè)用戶進(jìn)程,然后在userboot的入口處啟動(dòng)它。

userboot的入口處在哪里?

UserbootImage userboot(vdso);

userboot.Map(vmar, &vdso_base, &entry);

從vdso中找到userboot

const VDso* vdso = VDso::Create();

vdso->GetVariants(&handles[kFirstVdso]);

bootstrap_vmos(handles);

Vdso獲取

kernel/lib/userabi/userboot/BUILD.gn中編譯

loadable_module("userboot") {
sources = [
"bootdata.cc",
"bootfs.cc",
   "loader-service.cc",
"option.cc",
"start.cc",
"userboot-elf.cc",
"util.cc",
]
configs += [ "$zx/public/gn/config:rodso" ]
ldflags = [ "-Wl,-e,_start" ]
libs = [ vdso_syms_ld ]

_start是入口函數(shù),在

kernel/lib/userabi/userboot/start.cc中定義

extern "C" [[noreturn]] void
_start(zx_handle_t arg) {
bootstrap(zx::channel{arg});
}

這里我們開(kāi)始進(jìn)入userboot的代碼了。

7. Userboot進(jìn)程代碼

e2e27d26-07ef-11ee-962d-dac502259ad0.png

zx_status_t status = channel.read(0, child_message.cmdline,handles.data(),

sizeof(child_message.cmdline),

handles.size(), &cmdline_len, &nhandles);

讀出來(lái)init的時(shí)候發(fā)的消息,里面主要是一些參數(shù),解析出來(lái)為

e3315f18-07ef-11ee-962d-dac502259ad0.png

options o;

child_message.pargs.environ_num =

parse_options(log.get(), child_message.cmdline, cmdline_len, &o);

把這option都存入o里面

zx::vmo bootfs_vmo{
bootdata_get_bootfs(log.get(), vmar_self.get(),
handles[kRootJob],
handles[kUserbootDecompressor],
handles[kFirstVdso],
handles[kZbi])};

定位bootfs里面第一個(gè)程序

bootdata_get_bootfs
bootdata_t bootdata;
zx_status_t status = zx_vmo_read(bootdata_vmo, &bootdata,
off,
sizeof(bootdata));

讀出bootdate的頭,這里bootdata.type是BOOTDATA_BOOTFS_BOOT

zx::create(bootdata.extra, 0,
&bootfs_vmo);創(chuàng)建一個(gè)vmo
status = Decompressor(job,
engine_vmo, vdso_vmo)
(*zx::unowned_vmo{bootdata_vmo},
off + sizeof(bootdata),
bootdata.length,
bootfs_vmo, 0,
bootdata.extra);

拿到vdso

handles[kBootfsVmo] =

bootfs_vmo.release(); // bootfs_vmo實(shí)際在用戶態(tài)也只是維護(hù)一個(gè)zx_handle_t

const char* root_option =

o.value[OPTION_ROOT];拿到root進(jìn)程為pkg/bootsvc

zx::process proc;
zx::vmar vmar;
zx::unowned_job root_job{handles[kRootJob]};    // 本進(jìn)程的job也就是子進(jìn)程的job,傳承下去
const char* filename = o.value[OPTION_FILENAME];
filename 是bin/bootsvc
//創(chuàng)建子進(jìn)程
status = zx::create(
*root_job, filename,
static_cast(strlen(filename)), 0,
&proc, &vmar);
check(log.get(), status, "zx_process_create");
load_child_process(log.get(), &o, &bootfs, root_prefix,
handles[kFirstVdso],
proc.get(), vmar.get(),
thread.get(), to_child.get(),
&entry,
&vdso_base, &stack_size,
loader_service_channel.reset_and_get_address());
加載bin/bootsvc,elf程序
elf_load_bootfs-》bootfs_open-》load
bootfs_open -》bootfs_search
zx_status_t status = zx_vmo_create_child(fs->vmo,
ZX_VMO_CHILD_COPY_ON_WRITE,
e->data_off, e->data_len, &vmo);
status =
zx_vmo_replace_as_executable(vmo, ZX_HANDLE_INVALID, &vmo);
elf_load_vmo 這個(gè)的vdso從handles[kFirstVdso]傳進(jìn)來(lái)的
load(log, "vDSO", vmar, vmo,
NULL, NULL, NULL, NULL, false, false)

會(huì)把vdso這個(gè)elf格式文件讀入進(jìn)來(lái)

sp =

compute_initial_stack_pointer(stack_base, stack_size);

新建堆??臻g

// 同時(shí)給子進(jìn)程發(fā)送消息和handle數(shù)組。注意,這時(shí)子進(jìn)程還沒(méi)啟動(dòng)

status = to_child.write(0, &child_message, sizeof(child_message),

handles.data(),handles.size());

status = proc.start(thread, entry, sp,

std::move(child_start_handle), vdso_base);

啟動(dòng)bootsvc

8. bootsvc

在system/core/bootsvc/main.cc中

zx_handle_close(dl_set_loader_service(ZX_HANDLE_INVALID));關(guān)閉跟userboot的通道,這時(shí)候

ldsvc.Serve(std::move(loader_service_channel));會(huì)接收到,然后繼續(xù)往下執(zhí)行

zx::vmo bootfs_vmo(zx_take_startup_handle(PA_HND(PA_VMO_BOOTFS, 0)));

從handle里面獲取bootfs_vmo

status =

bootsvc::Create(loop.dispatcher(),&bootfs_svc);

status =

bootfs_svc->AddBootfs(std::move(bootfs_vmo));

創(chuàng)建一個(gè)bootfs的服務(wù),關(guān)聯(lián)bootfs_vmo

status = bootsvc::RetrieveBootImage(&image_vmo, &item_map,&factory_item_map);

找回boot信息

LoadBootArgs(bootfs_svc, &args_vmo,&args_size);把參數(shù)信息讀入到vmo

const char* config_path = "/config/devmgr";
fbl::Vector buf;
zx::vmo config_vmo;
uint64_t file_size;
zx_status_t status = bootfs->Open(config_path, &config_vmo,
&file_size);
zx::resource root_resource_handle(zx_take_startup_handle(PA_HND(PA_RESOURCE,
0)));

找到系統(tǒng)資源

fbl::RefPtr svcfs_svc =

bootsvc::Create(loop.dispatcher());

創(chuàng)建svcfs服務(wù)

status =

bootsvc::Create(bootfs_svc,loop.dispatcher(), &loader_svc);

創(chuàng)建loader服務(wù)

std::thread(LaunchNextProcess, bootfs_svc, svcfs_svc, loader_svc,std::cref(log)).detach();

啟動(dòng)LaunchNextProcess

std::thread(LaunchNextProcess, bootfs_svc, svcfs_svc, loader_svc,
std::cref(log)).detach();
void LaunchNextProcess(fbl::RefPtr
bootfs,
fbl::RefPtr svcfs,
fbl::RefPtr loader_svc,
const zx::debuglog&
log) {
const char* bootsvc_next = getenv("bootsvc.next");
if
(bootsvc_next == nullptr) {
bootsvc_next = "bin/devcoordinator";
}

9. devcoordinator

system/core/devmgr/devcoordinator/main.cc中有main函數(shù)

status =

StartSvchost(root_job, require_system, &coordinator, std::move(fshost_client));

/boot/bin/svchost啟動(dòng)

devmgr_vfs_init(&coordinator, devmgr_args, needs_svc_mount,std::move(fshost_server));

devmgr_vfs_init -》

fshost_start-》

devmgr_launch -》

devmgr_launch_with_loader

fshost啟動(dòng)

intret =

thrd_create_with_name(&t, pwrbtn_monitor_starter, nullptr,"pwrbtn-monitor-starter");

pwrbtn-monitor啟動(dòng)

ret=

thrd_create_with_name(&t, service_starter, &coordinator,"service-starter");

const char* args[] = {"/boot/bin/miscsvc", nullptr};

devmgr::devmgr_launch(g_handles.svc_job, "miscsvc", args,nullptr, -1, handles, types,

countof(handles),nullptr, FS_BOOT | FS_DEV | FS_SVC | FS_VOLUME);

miscsvc啟動(dòng)

zx_status_t status =

devmgr::devmgr_launch(g_handles.svc_job,"netsvc", args, nullptr, -1,nullptr, nullptr, 0,&proc, FS_ALL);

netsvc啟動(dòng)

devmgr::devmgr_launch(g_handles.svc_job, "virtual-console",args, env.get(), -1, handles, types,

handle_count,nullptr, FS_ALL);

virtual-console啟動(dòng)

intret =

thrd_create_with_name(&t, fuchsia_starter, coordinator,"fuchsia-starter");

coordinator.PrepareProxy(coordinator.sys_device(), nullptr);

'devhost:sys'啟動(dòng)

coordinator.PrepareProxy(coordinator.test_device(), nullptr);

'devhost:test'啟動(dòng)

coordinator.BindDrivers();

for
(Driver& drv : drivers_) {
zx_status_t status = BindDriver(&drv);
if (status != ZX_OK && status != ZX_ERR_UNAVAILABLE) {
log(ERROR, "devcoordinator: failed to bind driver '%s': %s
",
drv.name.data(),
zx_status_get_string(status));
}
}

綁定了devhost:root和devhost:misc

coordinator.set_running(true);

status = loop.Run();

設(shè)置狀態(tài)為運(yùn)行,進(jìn)入循環(huán),啟動(dòng)四個(gè)devhost

10. Devhost

Zircon內(nèi)核中,設(shè)備驅(qū)動(dòng)程序以ELF格式的共享庫(kù)形式存在,由devhost進(jìn)程按需動(dòng)態(tài)加載(實(shí)現(xiàn)代碼參見(jiàn)

zircon/system/core/devmgr/devhost/目錄)

核心設(shè)備管理進(jìn)程(devmgr),包含具有跟蹤設(shè)備與驅(qū)動(dòng)關(guān)聯(lián)的devcoordinator進(jìn)程,同時(shí)管理著驅(qū)動(dòng)程序發(fā)現(xiàn),devhost進(jìn)程創(chuàng)建和控制,還要維護(hù)設(shè)備文件系統(tǒng)(devfs),通過(guò)devfs機(jī)制,用戶層的服務(wù)和應(yīng)用實(shí)現(xiàn)對(duì)設(shè)備的操作。

e3754318-07ef-11ee-962d-dac502259ad0.png

進(jìn)程devcoordinator將設(shè)備看做是一個(gè)統(tǒng)一樹狀結(jié)構(gòu)。樹的分支(和子分支)由一定數(shù)量的隸屬于devhost進(jìn)程的設(shè)備組成。關(guān)于如何將整棵設(shè)備樹劃分以分配到多個(gè)devhost進(jìn)程中,取決于系統(tǒng)的策略:基于安全或者穩(wěn)定性原因的驅(qū)動(dòng)隔離;以及為了性能原因?qū)Ⅱ?qū)動(dòng)進(jìn)行并置。

參考:

https://blog.csdn.net/sinat_20184565/article/details/92002908

dm dump可以查看進(jìn)程樹

devhost[proxy]
devhost[sys/platform]
devhost[sys/platform/001b]
devhost[sys/platform/acpi/acpi-pwrbtn]
devhost[sys/platform/acpi/i8042]
devhost[00:01.0]
devhost[00:1f.2]

當(dāng)program loader設(shè)置了一個(gè)新進(jìn)程后,使該進(jìn)程能夠進(jìn)行系統(tǒng)調(diào)用的唯一方法是:program loader在新進(jìn)程的第一個(gè)線程開(kāi)始運(yùn)行之前,將vDSO映射到新進(jìn)程的虛擬地址空間(地址隨機(jī))。因此,在啟動(dòng)其他能夠進(jìn)行系統(tǒng)調(diào)用的進(jìn)程的每個(gè)進(jìn)程自己本身都必須能夠訪問(wèn)vDSO的VMO。

vDSO映像在編譯時(shí)嵌入到內(nèi)核中。內(nèi)核將它作為只讀VMO公開(kāi)給用戶空間。內(nèi)核啟動(dòng)時(shí),會(huì)通過(guò)計(jì)算得到它所在的物理頁(yè)。






審核編輯:劉清

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 計(jì)數(shù)器
    +關(guān)注

    關(guān)注

    32

    文章

    2291

    瀏覽量

    96387
  • 虛擬機(jī)
    +關(guān)注

    關(guān)注

    1

    文章

    966

    瀏覽量

    29337
  • C++語(yǔ)言
    +關(guān)注

    關(guān)注

    0

    文章

    147

    瀏覽量

    7295
  • ChatGPT
    +關(guān)注

    關(guān)注

    29

    文章

    1589

    瀏覽量

    9078

原文標(biāo)題:Fuchsia入門-zircon微內(nèi)核啟動(dòng)代碼分析

文章出處:【微信號(hào):OS與AUTOSAR研究,微信公眾號(hào):OS與AUTOSAR研究】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    BootLoader啟動(dòng)代碼分析

    BootLoader啟動(dòng)代碼分析
    發(fā)表于 08-04 10:09

    如何使用dtb方式啟動(dòng)內(nèi)核

    份linux 內(nèi)核代碼可以在多個(gè)板卡上運(yùn)行,每個(gè)板卡可以使用自己的dtb文件?! ±鲜降膗-boot使用ATAGS的方式啟動(dòng)linux內(nèi)核,本文使用新式的dtb方式
    發(fā)表于 04-22 14:06

    Linux內(nèi)核代碼情景分析(上冊(cè))浙江大學(xué)

    本書著重于對(duì)LINUX系統(tǒng)最新版本(2.4.0)內(nèi)核代碼進(jìn)行情景描述和情景分析. 上冊(cè)共6章.
    發(fā)表于 06-10 14:40 ?0次下載
    Linux<b class='flag-5'>內(nèi)核</b>源<b class='flag-5'>代碼</b>情景<b class='flag-5'>分析</b>(上冊(cè))浙江大學(xué)

    Linux內(nèi)核代碼情景分析(下冊(cè))浙江大學(xué)

    本書著重于對(duì)LINUX系統(tǒng)最新版本(2.4.0)內(nèi)核代碼進(jìn)行情景描述和情景分析. 上冊(cè)共3章.
    發(fā)表于 06-10 14:43 ?0次下載

    Android內(nèi)核分析

    介紹Android 移動(dòng)平臺(tái)系統(tǒng)架構(gòu),通過(guò)對(duì)Android 源代碼分析,將其與標(biāo)準(zhǔn)Linux 內(nèi)核(2.6.27)源代碼相比較,詳細(xì)解析Android
    發(fā)表于 10-29 16:17 ?116次下載

    嵌入式uCLinux內(nèi)核啟動(dòng)過(guò)程分析

    分析uCLinux的啟動(dòng)過(guò)程,可以加快系統(tǒng)啟動(dòng)速度、正確建立應(yīng)用環(huán)境。本文要研究的就是uCLinux操作系統(tǒng)內(nèi)核啟動(dòng)過(guò)程。
    發(fā)表于 08-15 16:51 ?855次閱讀

    linux內(nèi)核啟動(dòng)內(nèi)核解壓過(guò)程分析

    linux啟動(dòng)時(shí)內(nèi)核解壓過(guò)程分析,一份不錯(cuò)的文檔,深入了解內(nèi)核必備
    發(fā)表于 03-09 13:39 ?1次下載

    ARM處理器的啟動(dòng)代碼分析與設(shè)計(jì)

    ARM處理器的啟動(dòng)代碼分析與設(shè)計(jì)
    發(fā)表于 09-25 08:27 ?12次下載
    ARM處理器的<b class='flag-5'>啟動(dòng)</b><b class='flag-5'>代碼</b>的<b class='flag-5'>分析</b>與設(shè)計(jì)

    Linux內(nèi)核移植相關(guān)代碼解析

    本文通過(guò)整理之前研發(fā)的一個(gè)項(xiàng)目(ARM7TDMI +uCLinux),分析內(nèi)核啟動(dòng)過(guò)程及需要修改的文件,以供內(nèi)核移植者參考。整理過(guò)程中也同時(shí)參考了眾多網(wǎng)友的帖子,在此謝過(guò)。由于整理過(guò)程
    發(fā)表于 11-07 11:29 ?0次下載

    linux內(nèi)核啟動(dòng)流程

    Linux的啟動(dòng)代碼真的挺大,從匯編到C,從Makefile到LDS文件,需要理解的東西很多。畢竟Linux內(nèi)核是由很多人,花費(fèi)了巨大的時(shí)間和精力寫出來(lái)的。而且直到現(xiàn)在,這個(gè)世界上仍然有成千上萬(wàn)的程序員在不斷完善Linux
    發(fā)表于 11-14 16:19 ?4480次閱讀
    linux<b class='flag-5'>內(nèi)核</b><b class='flag-5'>啟動(dòng)</b>流程

    Linux內(nèi)核代碼情景分析(全冊(cè)高清帶書簽)pdf下載

    Linux內(nèi)核代碼情景分析需要的拿走吧
    發(fā)表于 01-04 16:57 ?9次下載

    嵌入式Linux內(nèi)核移植相關(guān)代碼分析

    本文通過(guò)整理之前研發(fā)的一個(gè)項(xiàng)目(ARM7TDMI + uCLinux),分析內(nèi)核啟動(dòng)過(guò)程及需要修改的文件,以供內(nèi)核移植者參考。整理過(guò)程中也同時(shí)參考了眾多網(wǎng)友的帖子,在此
    發(fā)表于 04-02 14:37 ?338次閱讀

    內(nèi)核與宏內(nèi)核的比較與分析

    混合內(nèi)核實(shí)質(zhì)上也是內(nèi)核,而外內(nèi)核是一種比較極端的設(shè)計(jì)方法,目前還處于研究階段,所以我們就著重討論宏內(nèi)核
    發(fā)表于 03-17 16:05 ?11次下載
    <b class='flag-5'>微</b><b class='flag-5'>內(nèi)核</b>與宏<b class='flag-5'>內(nèi)核</b>的比較與<b class='flag-5'>分析</b>

    一種實(shí)時(shí)嵌入式多任務(wù)內(nèi)核分析與改進(jìn)

    一種實(shí)時(shí)嵌入式多任務(wù)內(nèi)核分析與改進(jìn)(嵌入式開(kāi)發(fā)系統(tǒng))-一種實(shí)時(shí)嵌入式多任務(wù)內(nèi)核分析與改進(jìn)
    發(fā)表于 07-30 13:49 ?11次下載
    一種實(shí)時(shí)嵌入式多任務(wù)<b class='flag-5'>微</b><b class='flag-5'>內(nèi)核</b>的<b class='flag-5'>分析</b>與改進(jìn)

    分析ARM Cortex-M內(nèi)核復(fù)位啟動(dòng)過(guò)程

    ARM Cortex-M內(nèi)核的復(fù)位啟動(dòng)過(guò)程也被稱為復(fù)位序列(Reset sequence),下面就來(lái)簡(jiǎn)要總結(jié)分析下這一過(guò)程。
    的頭像 發(fā)表于 03-20 09:58 ?3141次閱讀