kvm线程-004-线程切换

本文介绍kvm中线程切换的实现.

通过kvm线程-001的介绍.可以得到如下事实:

  1. 对于通过new Thread() 创建的线程,其分配的时间片为:priority * 1000.其代码如下:

        void Java_java_lang_Thread_setPriority0(void)
    {
        int priority = popStack();
        THREAD VMthread;
    
        /*  Ensure that the internal thread execution */
        /*  structure has been created */
        START_TEMPORARY_ROOTS
            DECLARE_TEMPORARY_ROOT(JAVATHREAD, javaThread,
                                   popStackAsType(JAVATHREAD));
            javaThread->priority = (priority > MAX_PRIORITY ? MAX_PRIORITY :
                       (priority < MIN_PRIORITY ? MIN_PRIORITY : priority));
            VMthread = getVMthread(&javaThread);
            /* The actual VM-level timeslice of the thread is calculated by
             * multiplying the given priority */
            VMthread->timeslice = javaThread->priority * TIMESLICEFACTOR;
        END_TEMPORARY_ROOTS
    }
    
  2. 对于主线程,kvm为其分配的时间片为: 1000.代码如下:

    newThread->timeslice = BASETIMESLICE; // 1000
    
  3. 分配的时间片的含有为: 每执行一次字节码,就会将时间片减一,若为0,则需要进行线程切换.关于第三点,在下文介绍.


在FastInterpret方法中,有如下代码:


#if RESCHEDULEATBRANCH
reschedulePoint:
    RESCHEDULE

而每次字节码执行完毕后,都会有如下代码:

goto reschedulePoint;

因此实现了上文提到的 每执行一次字节码,就会将时间片减一,若为0,则需要进行线程切换. 这里的关键点是RESCHEDULE,宏展开后如下:

#define RESCHEDULE {                            \
    INC_RESHED // 此处为宏,默认为空操作                                  \
    checkRescheduleValid();  // 此处为宏,默认为空操作                    \
    if (isTimeToReschedule()) {                 \
        VMSAVE                                  \
        reschedule();                           \
        VMRESTORE                               \
    }                                           \
}

此处使用了多个宏,宏展开的结果为:

if (Timeslice-- == 0) {                 
        GlobalState.gs_ip = ip; 
        GlobalState.gs_fp = fp;
        GlobalState.gs_sp = sp; 
        GlobalState.gs_lp = lp;
        GlobalState.gs_cp = cp;                               
        
        do  {                                                         
	        ulong64 wakeupDelta;                                        
	        if (AliveThreadCount <= 0) {                                   
	            return;   /* end of program */                          
	        }                                                           
	        checkTimerQueue(&wakeupDelta); // 这个在之前的文章有介绍                              
	        InterpreterHandleEvent(wakeupDelta);  // 这个函数与线程切换关系不大                       
                                         
   		 } while (!SwitchThread());
    
                               
        ip = GlobalState.gs_ip;
        fp = GlobalState.gs_fp;
        sp = GlobalState.gs_sp;
        lp = GlobalState.gs_lp;
        cp = GlobalState.gs_cp;                               
}

这里比较重要的是SwitchThread(),其代码如下:

bool_t SwitchThread(void)
{
    THREAD threadToAdd = NIL;

    if (CurrentThread != NIL) {

        /*  1. 如果当前线程存在异常,则抛出 */
        if (CurrentThread->pendingException != NIL) {
            fatalError(KVM_MSG_BAD_PENDING_EXCEPTION);
        }

        if (CurrentThread->state == THREAD_ACTIVE) {
            
            if (RunnableThreads == NULL) {
                /*  如果只有一个线程,则不进行切换*/
                Timeslice = CurrentThread->timeslice;
                return TRUE;
            } else {
                /* 如果有其他线程,则需要进行切换 */
                storeExecutionEnvironment(CurrentThread);
                threadToAdd = CurrentThread;
                CurrentThread = NIL;
            }
        } else {
        	   // 如果线程是其他状态,则抛出异常
            fatalError(KVM_MSG_ATTEMPTING_TO_SWITCH_TO_INACTIVE_THREAD);
        }
    }

    /*  从RunnableThreads队列中删除队首,即获得一个等待运行的线程 RunnableThreads是循环队列,RunnableThreads指向队尾 */
    CurrentThread = removeQueueStart(&RunnableThreads);

    /* 将被切换的线程加入到队列中 */
    if (threadToAdd != NIL) {
        addThreadToQueue(&RunnableThreads, threadToAdd, AT_END);
    }

    /* 如果没有线程可运行,则返回false*/
    if (CurrentThread == NIL) {
        return FALSE;
    }

#if ENABLEPROFILING
    ThreadSwitchCounter++;
#endif
    /*  加载寄存器*/
    loadExecutionEnvironment(CurrentThread);

#if INCLUDEDEBUGCODE
    if (tracethreading) {
        /* Diagnostics */
        TraceThread(CurrentThread, "Switching to this thread");
    }
#endif

    /*  分配时间片*/
    Timeslice = CurrentThread->timeslice;

    /* 如果当前线程有异常,则直接抛出 */
    if (CurrentThread->pendingException != NIL) {
        char* pending = CurrentThread->pendingException;
        CurrentThread->pendingException = NIL;
        raiseException(pending);
    }

    return TRUE;
}

这里需要强调的是: RunnableThreads是循环队列,RunnableThreads指向队尾.定义如下:

THREAD RunnableThreads;
typedef struct threadQueue*         THREAD;

而关于threadQueue,在kvm线程-001 中有介绍.

而storeExecutionEnvironment(), loadExecutionEnvironment(CurrentThread) 代码分别如下:

void storeExecutionEnvironment(THREAD thisThread)
{
    /* Save the current thread execution environment
     * (virtual machine registers) to the thread structure.
     */
    thisThread->fpStore = getFP();
    thisThread->spStore = getSP();
    thisThread->ipStore = getIP();
}

void loadExecutionEnvironment(THREAD thisThread)
{
    /* Restore the thread execution environment */
    /* (VM registers) from the thread structure */
    setFP(thisThread->fpStore);
    setLP(FRAMELOCALS(getFP()));
    setCP(getFP()->thisMethod->ofClass->constPool);
    setSP(thisThread->spStore);
    setIP(thisThread->ipStore);
}

这两个方法也是在前文有介绍,这里就不在展开了.

在SwitchThread方法中,比较重要的是: removeQueueStart()方法和addThreadToQueue()方法.分别介绍如下:

  1. removeQueueStart() --> 从RunnableThreads队列中删除队首,即获得一个等待运行的线程,使其运行.代码如下:

        static THREAD removeQueueStart(THREAD *queue)
     {
         THREAD thisThread;
     
         START_CRITICAL_SECTION
             if (*queue == NULL) {
                 thisThread = NULL;
             } else {
                 thisThread = (*queue)->nextThread;
                 if (thisThread == *queue) {// 如果当前只有一个元素的话,则队列移除后就为空了
                    
                     *queue = NULL;
                 } else {
                     /* 从队列中删除*/
                     (*queue)->nextThread = thisThread->nextThread;
                 }
                 thisThread->nextThread = NIL;
             }
         END_CRITICAL_SECTION
     
         return thisThread;
        	} 
    

START_CRITICAL_SECTION, END_CRITICAL_SECTION为宏,是用来实现锁的.在kvm垃圾收集-002 中有介绍.

  1. addThreadToQueue() --> 将被切换的线程,加入到RunnableThreads的队尾.代码如下:

        static void
     addThreadToQueue(THREAD *queue, THREAD thisThread, queueWhere where)
     {
         START_CRITICAL_SECTION
             if (*queue == NIL) { // 如果RunnableThreads为null的话
                 *queue = thisThread;
                 thisThread->nextThread = thisThread;
             } else { // 加入到队列中
                 
                 thisThread->nextThread = (*queue)->nextThread;
                 (*queue)->nextThread = thisThread;
                 if (where == AT_START) { // 如果是加入到队首的话,则thisThread->nextThread = (*queue)->nextThread; (*queue)->nextThread = thisThread; 这两个操作已经完成了
                    
                     ;
                 } else {
                     /*   如果是队尾的话,则需要将RunnableThreads执行新加入的线程*/
                     *queue = thisThread;
                 }
             }
         END_CRITICAL_SECTION
     }
    

关于此处, RunnableThreads为循环队列,这需理解这一数据结构就不能理解.关于循环队列的讲解,这里就不展开了.可以参考如下链接:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值