/* This is for creating the first (init) user process. */ struct cap_group *create_root_cap_group(char *name, size_t name_len) { structcap_group *cap_group; structvmspace *vmspace; cap_t slot_id;
/* * The root_thread is actually a first user thread * which has no difference with other user threads */ voidcreate_root_thread(void) { structcap_group *root_cap_group; cap_t thread_cap; structthread *root_thread; char data[8]; int ret; cap_t stack_pmo_cap; structthread *thread; structpmobject *stack_pmo; structvmspace *init_vmspace; vaddr_tstack; vaddr_t kva; structprocess_metadatameta;
/* * Read from binary. * The msg and the binary of of the init process(procmgr) are linked * behind the kernel image via the incbin instruction. * The binary_procmgr_bin_start points to the first piece of info: * the entry point of the init process, followed by eight bytes of data * that stores the mem_size of the binary. */
/* Allocate and setup a user stack for the init thread */ stack_pmo_cap = create_pmo(ROOT_THREAD_STACK_SIZE, PMO_ANONYM, root_cap_group, 0, &stack_pmo, PMO_ALL_RIGHTS); BUG_ON(stack_pmo_cap < 0);
/* According to the linker.ld in procmgr, no exact bss exists * in elf.*/ BUG_ON(filesz != memsz); // No additional memory for .bss, so we can directly reuse // content in kernel image as their physical pages ret = create_pmo(PAGE_SIZE, PMO_DATA, root_cap_group, 0, &segment_pmo, PMO_ALL_RIGHTS); BUG_ON(ret < 0); kfree((void *)phys_to_virt(segment_pmo->start)); #if CHCORE_ARCH_X86_64 // See comments of embedded_bin_virt_to_phys segment_pmo->start = embedded_bin_virt_to_phys(segment_content_kvaddr); segment_pmo->size = pmo_size; #else segment_pmo->start = virt_to_phys(segment_content_kvaddr); segment_pmo->size = pmo_size; #endif unsigned vmr_flags = 0; if (flags & PHDR_FLAGS_R) vmr_flags |= VMR_READ; if (flags & PHDR_FLAGS_W) vmr_flags |= VMR_WRITE; if (flags & PHDR_FLAGS_X) vmr_flags |= VMR_EXEC;
/* Allocate a physical page for the main stack for prepare_env */ kva = (vaddr_t)get_pages(0); BUG_ON(kva == 0); commit_page_to_pmo(stack_pmo, ROOT_THREAD_STACK_SIZE / PAGE_SIZE - 1, virt_to_phys((void *)kva));
/* Add the thread into the thread_list of the cap_group */ lock(&root_cap_group->threads_lock); list_add(&thread->node, &root_cap_group->thread_list); root_cap_group->thread_cnt += 1; unlock(&root_cap_group->threads_lock);
/* Allocate the cap for the init thread */ thread_cap = cap_alloc(root_cap_group, thread); BUG_ON(thread_cap < 0); thread->cap = thread_cap;
/* L1 icache & dcache have no coherence on aarch64 */ flush_idcache();
root_thread = obj_get(root_cap_group, thread_cap, TYPE_THREAD); /* Enqueue: put init thread into the ready queue */ BUG_ON(sched_enqueue(root_thread)); obj_put(root_thread); }
graph LR
subgraph "Program Headers"
A[段 1] --> B[段 2]
B --> C[段 3]
C --> D[...]
end
subgraph "Memory Mapping"
E[PMO1] --> F[PMO2]
F --> G[PMO3]
G --> H[...]
end
A --> E
B --> F
C --> G
线程初始化
同理分为如下小的步骤:
准备环境:
为根线程准备环境,包括栈和程序入口点
初始化根线程:
使用根能力组、栈地址、入口点和优先级初始化根线程
其中即包括初始化线程上下文的操作
将根线程添加到能力组的线程列表:
将根线程添加到根能力组的线程列表中,并增加线程计数
为根线程分配能力:
为根线程分配一个能力(thread_cap)
刷新缓存:
刷新指令缓存和数据缓存,以确保新线程的指令和数据是最新的
将根线程放入就绪队列:
将根线程放入调度器的就绪队列,准备执行
graph LR
A[创建线程对象] --> B[初始化线程上下文]
B --> C[添加到能力组]
C --> D[分配线程能力]
D --> E[加入调度队列]
voidinit_thread_ctx(struct thread *thread, vaddr_tstack, vaddr_t func, u32 prio, u32 type, s32 aff) { /* Fill the context of the thread */ thread->thread_ctx->ec.reg[SP_EL0] = stack; thread->thread_ctx->ec.reg[ELR_EL1] = func; thread->thread_ctx->ec.reg[SPSR_EL1] = SPSR_EL1_EL0t;
/* Set the state of the thread */ thread->thread_ctx->state = TS_INIT;
/* Set thread type */ thread->thread_ctx->type = type;
/* Set the cpuid and affinity */ thread->thread_ctx->affinity = aff;
/* Set the budget and priority of the thread */ if (thread->thread_ctx->sc != NULL) { thread->thread_ctx->sc->prio = prio; thread->thread_ctx->sc->budget = DEFAULT_BUDGET; }
thread->thread_ctx->kernel_stack_state = KS_FREE; /* Set exiting state */ thread->thread_ctx->thread_exit_state = TE_RUNNING; thread->thread_ctx->is_suspended = false; }