GeekOS建立一个可运行进程单位的过程

一般来说,进程创建过程为:

  • 在进程列表中增加一项,从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)
{
     /* this thread will load&run  ELF files, see the rest in lprog.c */
     Print("Starting the Spawner thread...\n");
     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\n", 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

/*
 * Initialize a new Kernel_Thread.
 */
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, '\0', sizeof(*kthread));
    kthread->stackPage = stackPage;
    kthread->esp = ((ulong_t) kthread->stackPage) + PAGE_SIZE;
    kthread->numTicks = 0;
    kthread->priority = priority;
    kthread->userContext = 0;
    kthread->owner = owner;

    /*
     * The thread has an implicit self-reference.
     * If the thread is not detached, then its owner
     * also has a reference to it.
     */
    kthread->refCount = detached ? 1 : 2;

    kthread->alive = true;
    Clear_Thread_Queue(&kthread->joinQueue);
    kthread->pid = nextFreePid++;

}
2)Setup_Kernel_Thread函数主要是初始化进程栈区,包括参数地址、返回地址、入口地址、寄存器值等。

geekos/kthread.c

/*
 * Set up the initial context for a kernel-mode-only thread.
 */
static void Setup_Kernel_Thread(
    struct Kernel_Thread* kthread,
    Thread_Start_Func startFunc,
    ulong_t arg)
{
    /*
     * Push the argument to the thread start function, and the
     * return address (the Shutdown_Thread function, so the thread will
     * go away cleanly when the start function returns).
     */
    Push(kthread, arg);
    Push(kthread, (ulong_t) &Shutdown_Thread);

    /* Push the address of the start function. */
    Push(kthread, (ulong_t) startFunc);

    /*
     * To make the thread schedulable, we need to make it look
     * like it was suspended by an interrupt.  This means pushing
     * an "eflags, cs, eip" sequence onto the stack,
     * as well as int num, error code, saved registers, etc.
     */

    /*
     * The EFLAGS register will have all bits clear.
     * The important constraint is that we want to have the IF
     * bit clear, so that interrupts are disabled when the
     * thread starts.
     */
    Push(kthread, 0UL);  /* EFLAGS */

    /*
     * As the "return address" specifying where the new thread will
     * start executing, use the Launch_Thread() function.
     */
    Push(kthread, KERNEL_CS);
    Push(kthread, (ulong_t) &Launch_Thread);

    /* Push fake error code and interrupt number. */
    Push(kthread, 0);
    Push(kthread, 0);

    /* Push initial values for general-purpose registers. */
    Push_General_Registers(kthread);

    /*
     * Push values for saved segment registers.
     * Only the ds and es registers will contain valid selectors.
     * The fs and gs registers are not used by any instruction
     * generated by gcc.
     */
    Push(kthread, KERNEL_DS);  /* ds */
    Push(kthread, KERNEL_DS);  /* es */
    Push(kthread, 0);  /* fs */
    Push(kthread, 0);  /* gs */
}
3)Make_Runnable_Atomic函数将该进程设置为就绪态,加入等待执行队列。

geekos/kthread.c

/*
 * Atomically make a thread runnable.
 * Assumes interrupts are currently enabled.
 */
void Make_Runnable_Atomic(struct Kernel_Thread* kthread)
{
    Disable_Interrupts();
    Make_Runnable(kthread);
    Enable_Interrupts();
}
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值