在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

zircon微內核啟動代碼分析

yzcdx ? 來源:OS與AUTOSAR研究 ? 2023-06-11 09:14 ? 次閱讀

1. C++入口函數lk_main

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

Zircon微內核的代碼是用C++寫的,C++和C的基礎語法差不多,C++新加入了一些面向對象的東西,在內核中沒有界面化那些C++的API的情況下,區別基本就是C語言結構體的回調函數是一個.C++則是::,不用另外學習C++語法就可以看懂,但是為什么zircon使用了面向對象語言C++?這里你必須對面向對象的思想有最基本的認識,首先就是抽象出來共同的屬性操作,這樣就不用寫代碼對不同的模塊寫重復的代碼,而且結構更加的清晰,其次對象的安全性也更好,可以參考zCore入門-面向對象的Rust微內核里面介紹的面向對象OS設計的好處,再次印證那句“編程語言和操作系統的設計是相輔相成的”。

進入正題系統啟動整體流程為:kernel-》userboot-》bootfs鏡像-》文件系統-》可執行文件-》組件管理器-》拉起進程。

先說kernel的啟動,lk_main()入口函數在代碼中的位置在kernel/top/main.cc中

//日志打印開關定義
dlog_bypass_init_early();
thread_init_early():
percpu::InitializeBoot();


//創建一個precpu的對象
thread_t*t=&percpu::Get(0).idle_thread;//找到cpu0對應的空閑線程
thread_construct_first(t,"bootstrap");

這t就是一個線程,線程在zircon中也是一個對象,相關的對象為Job->Process->Thread

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

Zircon 公開了三個運行代碼的主要內核對象:

線程:給定地址空間內的執行線程。

進程:在私有、隔離的地址空間中運行的一組可執行指令。

作業:一組相關的流程和作業。所有作業形成一個單根樹。

線程對象是表示分時CPU 執行上下文的構造。Thread 對象與特定的Process Object相關聯,它為 I/O 和計算所需的其他對象提供內存和句柄。

線程是調度的基本單位,并且調度有優先級,在thread_construct_first()函數中,會設置線程的信息,例如base_priority優先級為HIGHEST_PRIORITY,set_current_thread設置此線程為當前正在運行的thread

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

call_constructors();//調用全局構造函數

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

函數上打了attribute((constructor))則為全局構造函數,編譯器將其編譯到.init 段,而__init_array_start 和__init_array_end 是該段的開始和結尾。

2. 內核分層初始化lk_primary_cpu_init_level

在zircon中對于系統啟動進行分層設計,這樣結構很明朗好理解,而且新添加內容的時候方便找到對應的地方,在出錯調試的時候也更加的方便,真是簡單實用又高深的方法,大道至簡也就這樣吧,在我們的編程中可以借鑒。

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);

可見去__start_lk_init結構體數組中按照level找元素,找到就執行hook()回調函數。

在kernel/kernel.ld中定義

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

Ld文件規定了二進制文件的組織形式,在數據段中存在這個數組,這個數組里面的元素按照.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這個宏用于聲明這個數據段的結構體,我們添加初始化的模塊函數時就可以在模塊尾部聲明這個LK_INIT_HOOK。

LK_INIT_LEVEL_EARLIEST這個level的結構體不存在,只是空跑下算初始化

lk_primary_cpu_init_level(LK_INIT_LEVEL_ARCH_EARLY,LK_INIT_LEVEL_PLATFORM_EARLY - 1);

就會執行

kernel/lib/code_patching/code_patching.cc中apply_startup_code_patches函數

LK_INIT_HOOK(code_patching,apply_startup_code_patches,

LK_INIT_LEVEL_ARCH_EARLY)

這個函數好像沒做什么。

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

arch_early_init();

x86_mmu_early_init();

mmu就是內存管理單元,主要負責物理內存和虛擬內存的映射,這里是early_init,具體不詳細說明了,這里說一個技巧那就是chatGPT,直接把代碼復制到chatGPT提問讓解析一下

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

最近我也經常使用chatGPT,而且越難的東西它越擅長,這里說的難就是用的人少,需要大量經驗,難入門的東西,一個典型就是匯編語言,只是看匯編就交給chatGPT,自己完全沒必要學習匯編,學了又會忘記

chatGPT有一個缺點就是不保真,需要你是一個行內的人,你提問它回答給你啟發,你可以自己大致的辨別有用的信息,并且求證,這不就是導師干的事么,最重要的還是方向啊,現在最牛逼的老師領進門了,那修行還不事半功倍。

platform_early_init(void)

對x86來說kernel/platform/pc/platform.cc中實現

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

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)

這個zbi類型的數據,之前介紹過zbi,zbi有很多類型,這里為"CONTAINER"

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

PMM: boot reserve add [0x100000, 0x334fff]
platform_preserve_ramdisk();保留ramdis所在的區域
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();

會分配boot內存0x1599000,0x1599040 這個都是物理內存如果是虛擬內存轉換關系為:

paddr_to_physmap ()中:

"0xffffff8000000000UL" + 物理內存= 虛擬內存

lk_primary_cpu_init_level(LK_INIT_LEVEL_PLATFORM_EARLY,LK_INIT_LEVEL_TARGET_EARLY - 1);關于平臺早期初始化的鉤子函數,主要是一些驅動、中斷

LK_INIT_HOOK(platform_dev_init,platform_dev_init, LK_INIT_LEVEL_PLATFORM)

這個會初始化一堆的驅動,驅動從zbi中找到ZBI_TYPE_KERNEL_DRIVER類型的,繼續執行pdev_init_driver()會執行.data.rel.ro.lk_pdev_init數據段中結構體初始化,

LK_PDEV_INIT驅動通過這個宏就可以注冊到.data.rel.ro.lk_pdev_init

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

vm_init_preheap();創建供內核使用的虛存空間VmAspace

上面提到boot的內存起始為0x1599000,分配到了0x1599040,

MarkPagesInUsePhys在頁表中標記內存頁已經使用

heap_init();

Zircon 的內核堆由內部的 cmpctmalloc 實現。

cmpct_init

vm_init();虛擬內存初始化

找到內核鏡像的各個段,及初始化讀寫策略,例如

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,
},

// 遍歷上面的幾個段,并設置策略

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中標記一塊虛擬內存,這塊虛擬內存抽象為VmRegion類,擁有自己的底層mmu相關的配置
zx_status_t status = aspace->ReserveSpace(region->name,
region->size, region->base);
ASSERT(status == ZX_OK);
// 對某VmRegion對應的虛擬內存設置內存保護的相關參數
status = ProtectRegion(aspace, region->base, region->arch_mmu_flags);
ASSERT(status == ZX_OK);
}
// 標記映射表
// reserve the kernel aspace where the physmap is
aspace->ReserveSpace("physmap", PHYSMAP_SIZE,
PHYSMAP_BASE);

·ReserveSpace:在 vmm 中標記一塊虛擬內存,這塊虛擬內存抽象為VmRegion類,擁有自己的底層mmu相關的配置

·ProtectRegion:對某VmRegion對應的虛擬內存設置內存保護的相關參數

物理內存一塊區域存儲的數據,可以映射為內核里面一個VmRegion類,這個類里面有其虛擬內存信息,這一過程就是內存映射。

kernel_init();

mp_init();多核初始化

// 多核初始化
void mp_init(void) {
// 核間中斷任務表初始化
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線程

創建 bootstrap2 線程加入thread_list列表,由 bootstrap2 線程完成剩下的初始化工作,等到下次調度的時候,這個線程會運行。

thread_become_idle();變成idle進程,并且開啟中斷啟動調度

sched_reschedule();啟動線程調度

arch_enable_ints();開啟中斷

進入bootstrap2線程

arch_init(); CPU 架構初始化,這里為x86 intel平臺cpu的一些初始化,包括mmu、gdtidt、tarce

-》platform_init(); 平臺初始化

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是第二個cpu啟動執行的地址

之后會啟動其他cpu進入_x86_secondary_cpu_long_mode_entry執行

x86_secondary_entry-》

finish_secondary_entry-》

lk_secondary_cpu_entry-》

thread_secondary_cpu_entry-》

thread_exit開啟本cpu調度

pc_init_smbios

SMBIOS(System Management BIOS)是主板或系統制造者以標準格式顯示產品管理信息所需遵循的統一規范。

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

lk_primary_cpu_init_level(LK_INIT_LEVEL_TARGET, LK_INIT_LEVEL_LAST);

初始化下面幾個模塊:

* 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函數會啟動

5. Userboot進程

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

kernel-》userboot-》bootfs鏡像-》文件系統-》可執行文件-》組件管理器-》拉起進程

關于userboot初始化

zx_status_t status = MessagePacket::Create創建一個新的MessagePacket msg

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

enum HandleIndex : uint32_t {
// These describe userboot itself.
kProcSelf, // 記錄新進程中指向新進程本身的handle號
kVmarRootSelf, // 記錄為新進程創建的vmar的handle
kRootJob, //一個process屬于一個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()
};

創建process,其包含一個vmar,每個進程都從一個單一的虛擬內存地址區域 (VMAR) 開始,進程根VMAR 跨越整個用戶地址空間。VMAR 用于映射虛擬內存對象(VMO),虛擬內存對象將程序所需的代碼、數據、匿名和共享內存頁面提供到進程的地址空間中。

// 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 內核訪問系統調用的唯一方法(作為系統調用的跳板)。它之所以是虛擬的,是因為它不是從文件系統中的ELF文件加載的,而是由內核直接提供的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();

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

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);

創建一個vmo對象,就是虛擬內存對象,指向這個ramdisk

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

handles的kZbi里面存的這個vmo

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

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

&handles[kCounters]);

內核計數器放入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);

創建一個channel,有兩頭一個頭是user一頭是kernel,用于通信

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

ktl::move(msg));

內核側先寫入點數據

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

用戶進程把這個channel通過handle綁定到自己身上

Job->Process->handle->channel

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

映射就是把vmo映射到vmar上面,vmar是process里面的數據。Entry就是userboot的入口地址,vdso_base就是進行系統調用的基地址

// 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中取得系統調用

當內核將userboot映射到第一個用戶進程時,會像正常程序那樣,在內存中選擇一個隨機地址進行加載。而在映射userboot的vDSO時,并不采用上述隨機的方式,而是將vDSO映像直接放在內存中userboot的映像之后。這樣一來,vDSO代碼與userboot的偏移量總是固定的。

在編譯階段中,系統調用的入口點符號表會從vDSO ELF映像中提取出來,隨后寫入到鏈接腳本的符號定義中。利用每個符號在vDSO映像中相對固定的偏移地址,可在鏈接腳本提供的_end符號的固定偏移量處,定義該符號。通過這種方式,userboot代碼可以直接調用到放在內存中,其映像本身之后的,每個確切位置上的vDSO入口點。

vdso會映射到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);

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

uintptr_t sp =

compute_initial_stack_pointer(stack_base, stack_size);

計算線程的棧地址

status =

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

&(), &rights);

創建userboot線程,線程是調度的基本單位。

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

kernel如何啟用userboot?

與任何其他進程一樣,userboot必須從已經映射到其地址空間的vDSO開始,這樣它才能進行系統調用。內核將userboot和vDSO映射到第一個用戶進程,然后在userboot的入口處啟動它。

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是入口函數,在

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

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

這里我們開始進入userboot的代碼了。

7. Userboot進程代碼

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);

讀出來init的時候發的消息,里面主要是一些參數,解析出來為

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里面第一個程序

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);創建一個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實際在用戶態也只是維護一個zx_handle_t

const char* root_option =

o.value[OPTION_ROOT];拿到root進程為pkg/bootsvc

zx::process proc;
zx::vmar vmar;
zx::unowned_job root_job{handles[kRootJob]};    // 本進程的job也就是子進程的job,傳承下去
const char* filename = o.value[OPTION_FILENAME];
filename 是bin/bootsvc
//創建子進程
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 這個的vdso從handles[kFirstVdso]傳進來的
load(log, "vDSO", vmar, vmo,
NULL, NULL, NULL, NULL, false, false)

會把vdso這個elf格式文件讀入進來

sp =

compute_initial_stack_pointer(stack_base, stack_size);

新建堆??臻g

// 同時給子進程發送消息和handle數組。注意,這時子進程還沒啟動

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);

啟動bootsvc

8. bootsvc

在system/core/bootsvc/main.cc中

zx_handle_close(dl_set_loader_service(ZX_HANDLE_INVALID));關閉跟userboot的通道,這時候

ldsvc.Serve(std::move(loader_service_channel));會接收到,然后繼續往下執行

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));

創建一個bootfs的服務,關聯bootfs_vmo

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

找回boot信息

LoadBootArgs(bootfs_svc, &args_vmo,&args_size);把參數信息讀入到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)));

找到系統資源

fbl::RefPtr svcfs_svc =

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

創建svcfs服務

status =

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

創建loader服務

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

啟動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函數

status =

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

/boot/bin/svchost啟動

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啟動

intret =

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

pwrbtn-monitor啟動

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啟動

zx_status_t status =

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

netsvc啟動

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

handle_count,nullptr, FS_ALL);

virtual-console啟動

intret =

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

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

'devhost:sys'啟動

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

'devhost:test'啟動

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();

設置狀態為運行,進入循環,啟動四個devhost

10. Devhost

Zircon內核中,設備驅動程序以ELF格式的共享庫形式存在,由devhost進程按需動態加載(實現代碼參見

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

核心設備管理進程(devmgr),包含具有跟蹤設備與驅動關聯的devcoordinator進程,同時管理著驅動程序發現,devhost進程創建和控制,還要維護設備文件系統(devfs),通過devfs機制,用戶層的服務和應用實現對設備的操作。

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

進程devcoordinator將設備看做是一個統一樹狀結構。樹的分支(和子分支)由一定數量的隸屬于devhost進程的設備組成。關于如何將整棵設備樹劃分以分配到多個devhost進程中,取決于系統的策略:基于安全或者穩定性原因的驅動隔離;以及為了性能原因將驅動進行并置。

參考:

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

dm dump可以查看進程樹

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]

當program loader設置了一個新進程后,使該進程能夠進行系統調用的唯一方法是:program loader在新進程的第一個線程開始運行之前,將vDSO映射到新進程的虛擬地址空間(地址隨機)。因此,在啟動其他能夠進行系統調用的進程的每個進程自己本身都必須能夠訪問vDSO的VMO。

vDSO映像在編譯時嵌入到內核中。內核將它作為只讀VMO公開給用戶空間。內核啟動時,會通過計算得到它所在的物理頁。






審核編輯:劉清

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 計數器
    +關注

    關注

    32

    文章

    2281

    瀏覽量

    95791
  • 虛擬機
    +關注

    關注

    1

    文章

    955

    瀏覽量

    28849
  • C++語言
    +關注

    關注

    0

    文章

    147

    瀏覽量

    7168
  • ChatGPT
    +關注

    關注

    29

    文章

    1584

    瀏覽量

    8621

原文標題:Fuchsia入門-zircon微內核啟動代碼分析

文章出處:【微信號:OS與AUTOSAR研究,微信公眾號:OS與AUTOSAR研究】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    BootLoader啟動代碼分析

    BootLoader啟動代碼分析
    發表于 08-04 10:09

    如何使用dtb方式啟動內核

    份linux 內核代碼可以在多個板卡上運行,每個板卡可以使用自己的dtb文件?! ±鲜降膗-boot使用ATAGS的方式啟動linux內核,本文使用新式的dtb方式
    發表于 04-22 14:06

    Linux內核代碼情景分析(上冊)浙江大學

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

    Linux內核代碼情景分析(下冊)浙江大學

    本書著重于對LINUX系統最新版本(2.4.0)內核代碼進行情景描述和情景分析. 上冊共3章.
    發表于 06-10 14:43 ?0次下載

    Android內核分析

    介紹Android 移動平臺系統架構,通過對Android 源代碼分析,將其與標準Linux 內核(2.6.27)源代碼相比較,詳細解析Android
    發表于 10-29 16:17 ?116次下載

    嵌入式uCLinux內核啟動過程分析

    分析uCLinux的啟動過程,可以加快系統啟動速度、正確建立應用環境。本文要研究的就是uCLinux操作系統內核啟動過程。
    發表于 08-15 16:51 ?822次閱讀

    linux內核啟動內核解壓過程分析

    linux啟動內核解壓過程分析,一份不錯的文檔,深入了解內核必備
    發表于 03-09 13:39 ?1次下載

    ARM處理器的啟動代碼分析與設計

    ARM處理器的啟動代碼分析與設計
    發表于 09-25 08:27 ?12次下載
    ARM處理器的<b class='flag-5'>啟動</b><b class='flag-5'>代碼</b>的<b class='flag-5'>分析</b>與設計

    Linux內核移植相關代碼解析

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

    linux內核啟動流程

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

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

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

    嵌入式Linux內核移植相關代碼分析

    本文通過整理之前研發的一個項目(ARM7TDMI + uCLinux),分析內核啟動過程及需要修改的文件,以供內核移植者參考。整理過程中也同時參考了眾多網友的帖子,在此
    發表于 04-02 14:37 ?307次閱讀

    內核與宏內核的比較與分析

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

    一種實時嵌入式多任務內核分析與改進

    一種實時嵌入式多任務內核分析與改進(嵌入式開發系統)-一種實時嵌入式多任務內核分析與改進
    發表于 07-30 13:49 ?11次下載
    一種實時嵌入式多任務<b class='flag-5'>微</b><b class='flag-5'>內核</b>的<b class='flag-5'>分析</b>與改進

    分析ARM Cortex-M內核復位啟動過程

    ARM Cortex-M內核的復位啟動過程也被稱為復位序列(Reset sequence),下面就來簡要總結分析下這一過程。
    的頭像 發表于 03-20 09:58 ?2813次閱讀
    主站蜘蛛池模板: 99久久99久久 | 国产99久久九九精品免费 | 欧美日韩在线成人看片a | www.四虎影院在线观看 | 亚洲免费成人在线 | 午夜黄 | 亚洲午夜在线观看 | cao榴| 亚洲国产成人在线 | 激情在线网站 | 99久久精品费精品国产一区二 | sihu影院永久在线影院 | 91美女啪啪 | 一本到卡二卡三卡视频 | 人人爽人人爱 | 中文字幕一区精品欧美 | 超级极品白嫩美女在线 | 四虎影院成人 | 国内精品久久久久久久久蜜桃 | 色吧首页dvd| 四虎a级欧美在线观看 | 午夜激情网站 | 五月天婷婷亚洲 | 色婷婷精品综合久久狠狠 | 色婷婷亚洲精品综合影院 | 性做久久久久久久免费观看 | 美女又黄又www | 一区二区三区精品国产欧美 | 日本不卡在线一区二区三区视频 | 午夜寂寞影视 | 亚洲黄色小说网站 | 日本精品高清一区二区2021 | 国产精品国产三级国快看 | 奇米影视7777 | 日本69xxx | 天天干天天色天天干 | 亚洲精品国产自在久久出水 | 国产日韩精品一区二区在线观看 | 亚综合| 免费人成动漫在线播放r18 | 调教r18车肉高h男男 |