4.5.4 核心线程的创建和初始化

相对于Hello China V1.0,V1.5的实现中,CreateKernelThread函数也有所不同。V1.5的实现代码如下,其中黑体标注部分,是与V1.0的实现有显著差别的地方。为了便于描述,对每个黑体标注的差别部分,采用“//POSITION x”进行标注,以方便后面的描述:

    static __KERNEL_THREAD_OBJECT* CreateKernelThread(
                        __COMMON_OBJECT*           lpThis,
                        DWORD                     dwStackSize,
                        DWORD                     dwStatus,
                        DWORD                     dwPriority,
                        __KERNEL_THREAD_ROUTINE
lpStartRoutine,
                        LPVOID
lpRoutineParam,
                        LPVOID                    lpReserved,
                        //POSITION 1
                        LPSTR                     lpszName)
    {
        __KERNEL_THREAD_OBJECT*          lpKernelThread   =NULL;
        __KERNEL_THREAD_MANAGER*          lpMgr           =NULL;
        LPVOID                          lpStack         =NULL;
        BOOL                            bSuccess        =FALSE;
        DWORD*                          lpStackPtr       =NULL;
        DWORD                           i;
        if((NULL==lpThis) || (NULL==lpStartRoutine))
          goto __TERMINAL;
        if((KERNEL_THREAD_STATUS_READY !=dwStatus) &&
                (KERNEL_THREAD_STATUS_SUSPENDED !=dwStatus))
    goto __TERMINAL;
        lpMgr=(__KERNEL_THREAD_MANAGER*)lpThis;
        lpKernelThread                                            =
(__KERNEL_THREAD_OBJECT*)ObjectManager.CreateObject(&ObjectManager,
          NULL,
          OBJECT_TYPE_KERNEL_THREAD);
        if(NULL==lpKernelThread)
          goto __TERMINAL;
            if(!lpKernelThread->Initialize((__COMMON_OBJECT*)lpKernelThread))
          goto __TERMINAL;
        if(0==dwStackSize)
        {
          dwStackSize=DEFAULT_STACK_SIZE;
        }
        else
        {
          if(dwStackSize < MIN_STACK_SIZE)
          {
        dwStackSize=MIN_STACK_SIZE;
          }
        }
        lpStack=KMemAlloc(dwStackSize,KMEM_SIZE_TYPE_ANY);
        if(NULL==lpStack)   //Failed to create kernel thread stack.
        {
          goto __TERMINAL;
        }
        //The following code initializes the kernel thread object
//created just now.
        lpKernelThread->dwThreadID  =lpKernelThread->dwObjectID;
        lpKernelThread->dwThreadStatus     =dwStatus;
        lpKernelThread->dwThreadPriority   =dwPriority;
        lpKernelThread->dwScheduleCounter  =dwPriority;
//***** CAUTION!!! *****
        lpKernelThread->dwReturnValue      =0L;
        lpKernelThread->dwTotalRunTime     =0L;
        lpKernelThread->dwTotalMemSize     =0L;
        lpKernelThread->bUsedMath         =FALSE;
        lpKernelThread->dwStackSize               =dwStackSize  ?
dwStackSize : DEFAULT_STACK_SIZE;
        lpKernelThread->lpInitStackPointer     =(LPVOID)((DWORD)
lpStack+dwStackSize);
        lpKernelThread->KernelThreadRoutine=lpStartRoutine;
        lpKernelThread->lpRoutineParam     =lpRoutineParam;
        lpKernelThread->ucMsgQueueHeader   =0;
        lpKernelThread->ucMsgQueueTrial    =0;
        lpKernelThread->ucCurrentMsgNum    =0;
        lpKernelThread->dwLastError        =0L;
        lpKernelThread->dwWaitingStatus    =OBJECT_WAIT_WAITING;
        //Copy kernel thread name.
    //POSITION 2
        if(lpszName)
        {
            for(i=0;i < MAX_THREAD_NAME-1;i++)
            {
                if(lpszName[i]==0)  //End.
                {
    break;
                }
            lpKernelThread->KernelThreadName[i]=lpszName[i];
            }
        }
        lpKernelThread->KernelThreadName[i]=0;  //Set  string's
terminator.
        //
        //The following routine initializes the hardware context
        //of the kernel thread.
        //It's implementation depends on the hardware platform,so
        //this routine is implemented in ARCH directory.
        //
            //POSITION 3
        InitKernelThreadContext(lpKernelThread,KernelThreadWrapper);
        if(KERNEL_THREAD_STATUS_READY==dwStatus)
        {
          lpMgr->AddReadyKernelThread((__COMMON_OBJECT*)lpMgr,
        lpKernelThread);
        }
        else      //Add into Suspended Queue.
        {
          if(!lpMgr->lpSuspendedQueue->InsertIntoQueue(
    (__COMMON_OBJECT*)lpMgr->lpSuspendedQueue,
        (__COMMON_OBJECT*)lpKernelThread,dwPriority))
        goto __TERMINAL;
        }
        //Call the create hook.
            //POSITION 4
        lpMgr->CallThreadHook(THREAD_HOOK_TYPE_CREATE,lpKernelThread,
          NULL);
        bSuccess=TRUE;
            __TERMINAL:
        if(!bSuccess)
        {
          //First,release the resources created successfully.
          if(NULL !=lpKernelThread)
  ObjectManager.DestroyObject(&ObjectManager,    (__COMMON_OBJECT*)
lpKernelThread);
          if(NULL !=lpStack)
        KMemFree(lpStack,KMEM_SIZE_TYPE_ANY,0L);
          return NULL;
        }
        else
            return lpKernelThread;
    }

与V1.0相比,主要有三个不同的地方:

(1)在V1.5的实现中,允许为核心线程指定一个名称(对应代码中POSITION1和POSITION2)。这样可对核心线程进行有意义的标识,在CPU统计等一些系统输出中,也会同时输出核心线程的名字和ID,这样使得每个核心线程的标识更加直观。当然,在V1.5的实现中,为核心线程对象的定义增加了一个字符串数组成员,用于存放核心线程的名字。

(2)对核心线程上下文的初始化(对应代码中POSITION3)。在V1.0的实现中,使用了一个宏INIT_KERNEL_THREAD_CONTEXT完成对核心线程上下文对象的初始化。而在V1.5的实现中,由于硬件上下文直接保存在堆栈中,因此在创建线程过程中,其硬件上下文的初始化方式需要改变。在V1.5的实现中,只需要建立合适的堆栈框架即可,这个堆栈框架,应该是符合__SwitchTo函数要求的,因为线程刚创建完毕后,并不会马上投入运行,而是插到就绪队列中,直到下一次调度时机(比如,中断或另外线程的系统调用)到来的时候,才会被调度。在V1.5的实现中,通过一个InitKernelThreadContext函数,完成了对核心线程上下文的初始化。详细的实现,请参考后文。

(3)调用了线程创建回调函数(对应代码中POSITION4)。线程回调函数是V1.5引入的一个机制,可在核心线程被创建、被销毁、被换出CPU、被调入CPU的四个时刻得到调用,从而完成一些系统级的任务。

InitKernelThreadContext函数初始化了一个核心线程的硬件上下文,该函数代码如下:

VOID InitKernelThreadContext(__KERNEL_THREAD_OBJECT* lpKernelThread,
                            __KERNEL_THREAD_WRAPPER lpStartAddr)
{
    DWORD*       lpStackPtr=NULL;
    DWORD         dwStackSize=0;
    if((NULL==lpKernelThread) || (NULL==lpStartAddr))
    {
        return;
    }
    lpStackPtr=(DWORD*)lpKernelThread->lpInitStackPointer;
    __PUSH(lpStackPtr,lpKernelThread);
              //Push lpKernelThread to stack.
    __PUSH(lpStackPtr,NULL);
              //Push a new return address,simulate a call.
    __PUSH(lpStackPtr,INIT_EFLAGS_VALUE);   //Push EFlags.
    __PUSH(lpStackPtr,0x00000008);          //Push CS.
    __PUSH(lpStackPtr,lpStartAddr);  //Push start address.
    __PUSH(lpStackPtr,0L);                 //Push eax.
    __PUSH(lpStackPtr,0L);
    __PUSH(lpStackPtr,0L);
    __PUSH(lpStackPtr,0L);
    __PUSH(lpStackPtr,0L);
    __PUSH(lpStackPtr,0L);
    __PUSH(lpStackPtr,0L);
    //Save context.
    lpKernelThread->lpKernelThreadContext=
              (__KERNEL_THREAD_CONTEXT*)lpStackPtr;
    return;
  }

其中,PUSH是预定义的一个宏,如下:

  #define __PUSH(stackptr,val) \
    do{  \
    (DWORD*)(stackptr)-=1; \
    *((DWORD*)stackptr)=(DWORD)(val); \
    }while(0)

这个宏模拟了一个堆栈PUSH动作,首先把堆栈指针减去1(实际上是减去四个字节),然后把val存放在栈顶。

InitkernelThreadContext函数通过PUSH宏,建立了如图4-19所示的堆栈框架。

图4-19 新创建的核心线程的堆栈框架

其中,lpStartAddr就是Hello China提供的核心线程封装函数(KernelThreadWrapper)。堆栈框架建立完成之后,就把lpStackPtr赋值给当前核心线程对象的lpKernelThreadContext变量。待该核心线程得到调度的时候,通过lpKernelThreadContext变量,就可得到上述堆栈框架的栈顶指针,然后依次恢复通用寄存器,并执行iretd指令,就可跳转到lpStartAddr位置处(即KernelThreadWrapper函数处,这是所有核心线程的统一入口点)。

由于KernelThreadWrapper是一个函数,接受一个核心线程对象作为其参数,因此我们在开始的时候,压入了当前核心线程对象的指针和一个NULL值,以模拟一个CALL指令执行过程。