一般来说,进程创建过程为:
在进程列表中增加一项,从PCB池中申请一个空间PCB,为新进程分配唯一标识符;
为新进程的进程映像分配地址空间,以便容纳进程实体,由进程管理程序确定加载至进程地址空间中的程序;
为新进程分配各种资源;
初始化PCB,如进程标识符、处理器初始状态、进程优先级等;
把新进程的状态设置为就绪态,并将其移入就绪队列;
通知操作系统某些模块,如记账程序、性能监控程序等。
以由ELF文件建立一个可运行的进程单位为例,GeekOS中内核线程的建立流程如下:
1、 Spawn_Init_Process函数的功能是调用Start_Kernel_Thread(),它以Spawner函数为进程体建立一个核心级进程,并使之准备就绪。
Spawner函数运行过程为:通过Read_Fully函数将ELF可执行文件读入内存缓冲区;通过Parse_ELF_Executable函数解析ELF文件, 并通过Spawn_Program函数执行ELF文件。最后通过Free函数释放内存缓冲区。
geekos/main.c
void Spawner(unsigned long arg);
static void Spawn_Init_Process(void )
{
Print("Starting the Spawner thread...
" );
Start_Kernel_Thread( Spawner, 0 , PRIORITY_NORMAL, true );
}
2、Start_Kernel_Thread函数通过调用Create_Thread函数、Setup_Kernel_Thread、Make_Runnable_Atomic来创建内核进程。
geekos/kthread.c
/*
* Start a kernel-mode-only thread, using given function as its body
* and passing given argument as its parameter. Returns pointer
* to the new thread if successful, null otherwise.
*
* startFunc - is the function to be called by the new thread
* arg - is a paramter to pass to the new function
* priority - the priority of this thread (use PRIORITY_NORMAL) for
* most things
* detached - use false for kernel threads
*/
struct Kernel_Thread* Start_Kernel_Thread(
Thread_Start_Func startFunc,
ulong_t arg,
int priority,
bool detached
)
{
struct Kernel_Thread* kthread = Create_Thread(priority, detached);
if (kthread != 0 ) {
/*
* Create the initial context for the thread to make
* it schedulable.
*/
Setup_Kernel_Thread(kthread, startFunc, arg);
/* Atomically put the thread on the run queue. */
Make_Runnable_Atomic(kthread);
}
return kthread;
}
1)Create_Thread函数主要功能为通过Alloc_Page函数为Kernel_Thread结构体和内核进程栈区分配内存空间,并通过Init_Thread函数初始化Kernel_Thread结构体。最后将该进程结构体通过Add_To_Back_Of_All_Thread_List函数加入进程队列末尾。
PS:Kernel_Thread结构体就是GeekOS中的PCB。
geekos/kthread.c
/*
* Create a new raw thread object.
* Returns a null pointer if there isn't enough memory.
*/
static struct Kernel_Thread* Create_Thread(int priority, bool detached)
{
struct Kernel_Thread* kthread;
void* stackPage = 0 ;
/*
* For now, just allocate one page each for the thread context
* object and the thread's stack.
*/
kthread = Alloc_Page();
if (kthread != 0 )
stackPage = Alloc_Page();
/* Make sure that the memory allocations succeeded. */
if (kthread == 0 )
return 0 ;
if (stackPage == 0 ) {
Free_Page(kthread);
return 0 ;
}
/*Print("New thread @ %x, stack @ %x
" , kthread, stackPage); */
/*
* Initialize the stack pointer of the new thread
* and accounting info
*/
Init_Thread(kthread, stackPage, priority, detached);
/* Add to the list of all threads in the system. */
Add_To_Back_Of_All_Thread_List(&s_allThreadList, kthread);
return kthread;
}
geekos/mem.c
/*
* Allocate a page of physical memory.
*/
void* Alloc_Page(void)
{
struct Page* page;
void *result = 0 ;
bool iflag = Begin_Int_Atomic();
/* See if we have a free page */
if (!Is_Page_List_Empty(&s_freeList)) {
/* Remove the first page on the freelist. */
page = Get_Front_Of_Page_List(&s_freeList);
KASSERT((page->flags & PAGE_ALLOCATED) == 0 );
Remove_From_Front_Of_Page_List(&s_freeList);
/* Mark page as having been allocated. */
page->flags |= PAGE_ALLOCATED;
g_freePageCount
result = (void*) Get_Page_Address(page);
}
End_Int_Atomic(iflag);
return result ;
}
geekos/kthread.c
static void Init_Thread(struct Kernel_Thread* kthread, void * stackPage,
int priority, bool detached)
{
static int nextFreePid = 1 ;
struct Kernel_Thread* owner = detached ? (struct Kernel_Thread* )0 : g_currentThread;
memset(kthread, '