免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 4203 | 回复: 0

[FreeBSD] freebsd9.2-ULE线程调度-选择一个线程来运行-choosethread函数 [复制链接]

论坛徽章:
0
发表于 2014-07-01 20:09 |显示全部楼层
本帖最后由 71v5 于 2014-07-02 03:39 编辑

[函数choosethread]-当要选择一个线程来运行时,就会调用choosethread函数,比如之前描述的mini_switch函数就会调用该函数:
  1. /*******************************************************************
  2. * Select the thread that will be run next.
  3. ***************************/
  4.    153        struct thread *
  5.    154        choosethread(void)
  6.    155        {
  7.    156                struct thread *td;
  8.    157       
  9. /********************************************************************
  10. * 函数sched_choose选择一个线程,见下面的简要分析。
  11. **************************/
  12.    158        retry:
  13.    159                td = sched_choose();
  14.    160       
  15. /***********************************************************************************
  16. * If we are in panic, only allow system threads,
  17. * plus the one we are running in, to be run.

  18.    const char *panicstr;bsd内核中变量panicstr的说明如下:
  19.    Variable panicstr contains argument to first call to panic; used as flag
  20.    to indicate that the kernel has already called panic.
  21.    说明如果panicstr非空,就表明已经调用了panic函数。

  22.    对于TDF_INPANIC标志:
  23.    #define TDF_INPANIC 0x00000002 Caused a panic, let it drive crashdump.
  24.    如果线程td调用了panic函数,就会设置其TDF_INPANIC标志。

  25.    对于P_SYSTEM标志:
  26.    只有系统进程,才会设置该标志,内核初始化阶段创建的进程都属于系统进程,比如
  27.    pageout daemon,vm daemon,buffer daemon等等。

  28.    bsd内核中对于函数panic的说明:
  29.    Panic is called on unresolvable fatal errors.  It prints "panic: mesg",
  30.    and then reboots.  If we are called twice, then we avoid trying to sync
  31.    the disks as this often leads to recursive panics
  32.    
  33.    这里为了简化讨论,假设线程td没有调用panic函数,所以:
  34.    如果此时已经调用过panic函数,那么内核只会选择系统进程来运行。
  35.    
  36. ********************************************/
  37.    165                if (panicstr && ((td->td_proc->p_flag & P_SYSTEM) == 0 &&
  38.    166                    (td->td_flags & TDF_INPANIC) == 0)) {
  39.    167                        /* note that it is no longer on the run queue */
  40.    168                        TD_SET_CAN_RUN(td);
  41.    169                        goto retry;
  42.    170                }
  43.    171       
  44. /***********************************************************************
  45. * 此时线程td将要被调度运行,重新设置其状态。
  46.    #define        TD_SET_RUNNING(td)        (td)->td_state = TDS_RUNNING
  47. ********************************/
  48.    172                TD_SET_RUNNING(td);
  49.    173                return (td);
  50.    174        }
复制代码
这里再来看看struct tdq对象中下面三个成员:
  1. /*****************************************************************************
  2. * 从sched_add函数的实现可以看出:

  3.    #define PRI_MIN_BATCH           152
  4.    #define PRI_MAX_BATCH           223

  5.    tdq_realtime队列中线程的优先级小于PRI_MIN_BATCH。
  6.    tdq_timeshare队列中线程的优先级在区间[PRI_MIN_BATCH,PRI_MAX_BATCH]中。
  7.    tdq_idle队列中线程的优先级在区间(223,255]中。
  8.    
  9.    从这里可以得出:
  10.    tdq_realtime队列中线程的优先级最高。
  11.    tdq_timeshare队列中线程的优先级居中。
  12.    tdq_idle队列中线程的优先级最低。
  13.    
  14. ****************************************************/
  15. struct runq     tdq_realtime;           /* real-time run queue. */
  16. struct runq     tdq_timeshare;          /* timeshare run queue. */
  17. struct runq     tdq_idle;               /* Queue of IDLE threads. */
复制代码
[struct rqbits对象]:
  1. /**************************************************************************************
  2. * Bit array which maintains the status of a run queue.  When a queue is
  3. * non-empty the bit corresponding to the queue number will be set.

  4.    typedef u_int32_t       rqb_word_t;
  5.    #define        RQB_LEN                (2)        Number of priority status words.

  6.    对于链表rq_queues[i],位图rqb_bits中的第i个bit位用来描述链表rq_queues[i]是否为空:
  7.    第i个bit位为0:链表rq_queues[i]为空
  8.    第i个bit位非零:链表rq_queues[i]非空
  9. ***********************************/
  10.     51         */
  11.     52        struct rqbits {
  12.     53                rqb_word_t rqb_bits[RQB_LEN];
  13.     54        };
复制代码
[struct runq对象]:
  1. /*******************************************************************************************
  2. * Run queue structure.  Contains an array of run queues on which processes
  3. * are placed, and a structure to maintain the status of each queue.

  4.    #define RQ_NQS   (64)   Number of run queues.
  5.    #define RQ_PPQ   (4)    Priorities per queue.
  6.   
  7.    成员描述:
  8.    rq_status:
  9.    类型为struct rqbits,见上面对struct rqbits类型的描述,这里为了分析方便,按下面的方式
  10.    引用rq_status成员中的bit位:
  11.    rq_status.rqb_bits->num,引用第num个bit位。

  12.    rq_queues:
  13.    一个数组,数组元素的数目为64,每个数组元素都是一个链表的head,链接了相应优先级的线程,
  14.    对于数组索引a,b,如果a大于b,那么链表rq_queues[a]上线程的优先级比链表rq_queues[b]
  15.    上线程的优先级低。

  16.    在一个特定的队列内,假设在队列tdq_realtime中,只需要确定rq_status.rqb_bits中第一个
  17.    设置为1的bit位,假设为rq_status.rqb_bits->20,那么就直接从链表rq_queues[20]上fetch
  18.    第一个元素,该元素就是优先级最高的线程(不一定准确,因为sched_add函数将线程添加到
  19.    运行队列时,同一个链表上的元素不会再次排序)。
  20.             
  21.    sched_add函数在将线程td添加到tdq_realtime队列或者tdq_timeshare队列或者tdq_idle队列
  22.    中时,会根据线程td的优先级(struct thread对象的td_priority成员)计算一个"新的优先级",
  23.    "新的优先级"被限制在区间[0,64]中,这个"新的优先级"就作为数组rq_queues的索引。

  24.    "新的优先级"这个用语可能不是很严谨,不过能说明问题就行。

  25.    假设有4个线程thread0,thread1,thread2,thread3,都将被添加到tdq_realtime队列中,
  26.    原始优先级分别为self_pri0,self_pri1,self_pri2,self_pri3,经过计算后"新的优先级"
  27.    分别为new_pri0,new_pri1,new_pri2,new_pri3,并且new_pri0,new_pri1,new_pri2,new_pri3
  28.    各不相同,那么:
  29.    thread0将被添加到链表rq_queues[new_pri0]。
  30.    thread1将被添加到链表rq_queues[new_pri1]。
  31.    thread2将被添加到链表rq_queues[new_pri2]。
  32.    thread3将被添加到链表rq_queues[new_pri3]。

  33.    rq_status.rqb_bits->new_pri0,rq_status.rqb_bits->new_pri1,
  34.    rq_status.rqb_bits->new_pri2,rq_status.rqb_bits->new_pri3,
  35.    被设置为1。
  36. *******************************************/
  37.     60        struct runq {
  38.     61                struct        rqbits rq_status;
  39.     62                struct        rqhead rq_queues[RQ_NQS];
  40.     63        };
复制代码
下面一张简图清晰的展示了struct runq对象,runq_*函数都是围绕struct runq对象展开:
08.jpg

[ULE线程调度-sched_choose函数]:
  1. [ULE线程调度-sched_choose函数]:
  2. /***************************************************************************************
  3. * Choose the highest priority thread to run.  The thread is removed from
  4. * the run-queue while running however the load remains.  For SMP we set
  5. * the tdq in the global idle bitmask if it idles here.
  6. *
  7.     这里假设当前正在执行sched_choose函数的cpu的logical cpu id为5.
  8.    
  9.     那么函数sched_choose在依次从tdq_cpu[5]包含的tdq_realtime队列,tdq_timeshare队列,
  10.     tdq_idle队列三个运行队列中选择一个优先级最高的线程来运行,同时返回该线程对应的
  11.     struct thread对象。
  12. ****************************/
  13.   2273        struct thread *
  14.   2274        sched_choose(void)
  15.   2275        {
  16.   2276                struct thread *td;
  17.   2277                struct tdq *tdq;
  18.   2278       
  19. /******************************************************************************************
  20. * 2279:宏TDQ_SELF获取当前cpu对应的struct tdq对象,这里为&tdq_cpu[5]。

  21.    2281:调用函数tdq_choose从tdq_cpu[5]包含的tdq_realtime队列,tdq_timeshare队列,
  22.          tdq_idle队列三个运行队列中选择一个优先级最高的线程,函数tdq_choose简要描述如下。

  23.    2282-2285,如果函数tdq_choose函数成功fetch一个线程,此时:

  24.    2283-函数tdq_runq_rem完成下面的任务:
  25.    1:递减tdq_cpu[5]中科迁移线程的数目。
  26.    2:将线程td从链表rq_queues[td->td_rqindex]删除。
  27.    3:如果链表rq_queues[td->td_rqindex]为空,就将rq_status.rqb_bits->d_rqindex设置为0.

  28.    2284-更新tdq_lowpri,tdq_lowpri成员保存的是当前在cpu5上运行线程的优先级(数值小)。   
  29. ****************************/
  30.   2279                tdq = TDQ_SELF();
  31.   2280                TDQ_LOCK_ASSERT(tdq, MA_OWNED);
  32.   2281                td = tdq_choose(tdq);
  33.   2282                if (td) {
  34.   2283                        tdq_runq_rem(tdq, td);
  35.   2284                        tdq->tdq_lowpri = td->td_priority;
  36.   2285                        return (td);
  37.   2286                }
  38. /**************************************************************************************************
  39. * 如果执行这里,就表示tdq_cpu[5]包含的tdq_realtime队列,tdq_timeshare队列,tdq_idle队列
  40.    三个运行队列都为空。
  41.   
  42.    #define PRI_MAX_IDLE            (PRI_MAX) 255
  43.    
  44.    struct tdq对象的tdq_lowpri成员:
  45.    tdq_lowpri成员保存的是相应CPU运行队列中线程的最高优先级(数值小)。

  46.    2287:此时将tdq_lowpri成员设置为PRI_MAX_IDLE,意味着将要选择每cpu的idle线程来运行。
  47.          在系统初始化阶段,idle_setup函数会为系统中每个cpu创建一个对应的idle线程,
  48.          描述相应idle线程的struct thread对象的地址保存在相应cpu的struct pcpu对象的
  49.          pc_idlethread成员中,并且将idle线程的优先级设置为PRI_MAX_IDLE。
  50.          这里的话,__pcpu[5].pc_idlethread指向描述cpu5的idle线程的struct thread对象。

  51.    2288:宏PCPU_GET获取__pcpu[5].pc_idlethread,并返回。

  52.    从这里看出,只有当相应cpu的tdq_realtime队列,tdq_timeshare队列,tdq_idle队列三个队列都为空
  53.    时,才会选择每cpu的ilde线程来运行。
  54. ***********************************************************/
  55.   2287                tdq->tdq_lowpri = PRI_MAX_IDLE;
  56.   2288                return (PCPU_GET(idlethread));
  57.   2289        }
复制代码
[ULE线程调度-tdq_choose函数]:
  1. /***************************************************************************
  2. * Pick the highest priority task we have and return it.
  3.    参数描述:
  4.    tdq:这里为&tdq_cpu[5]

  5. ******************************************/
  6.   1282        static struct thread *
  7.   1283        tdq_choose(struct tdq *tdq)
  8.   1284        {
  9.   1285                struct thread *td;
  10.   1286       
  11. /*************************************************************************************
  12. * 1288-1290:
  13.    首先检查tdq_realtime队列,函数runq_choose从rq_status.rqb_bits的bit0开始按序检查,
  14.    如果rq_status.rqb_bits->num为1,就从链表rq_queues[num]上fetch一个线程,并返回
  15.    该线程对应的struct thread对象。
  16. *******************************/
  17.   1287                TDQ_LOCK_ASSERT(tdq, MA_OWNED);
  18.   1288                td = runq_choose(&tdq->tdq_realtime);
  19.   1289                if (td != NULL)
  20.   1290                        return (td);
  21. /************************************************************************************
  22. * 1291-1298:
  23.    如果执行到这里,就表示tdq_realtime队列中没有线程,此时检查tdq_timeshare队列,
  24.    函数runq_choose_from从rq_status.rqb_bits->tdq_ridx开始安序检查,如果
  25.    rq_status.rqb_bits->num为1,就从链表rq_queues[num]上fetch一个线程,并返回
  26.    该线程对应的struct thread对象。

  27.    函数runq_choose_from比函数runq_choose多了一个参数,在函数runq_choose_from中,
  28.    这第二个参数idx表示将从rq_status.rqb_bits->idx开始检查,因为tdq_timeshare队列的
  29.    类型为timeshare,这个额外的参数能够防止其它线程被饿死。
  30. ******************************************/  
  31.   1291                td = runq_choose_from(&tdq->tdq_timeshare, tdq->tdq_ridx);
  32.   1292                if (td != NULL) {
  33.   1293                        KASSERT(td->td_priority >= PRI_MIN_BATCH,
  34.   1294                            ("tdq_choose: Invalid priority on timeshare queue %d",
  35.   1295                            td->td_priority));
  36.   1296                        return (td);
  37.   1297                }
  38. /************************************************************************************
  39. * 1298-1303:
  40.    如果执行到这里,就表示tdq_realtime队列,tdq_timeshare队列两个队列中都没有线程,
  41.    此时检查tdq_idle队列,函数runq_choose从rq_status.rqb_bits的bit0开始按序检查,
  42.    如果rq_status.rqb_bits->num为1,就从链表rq_queues[num]上fetch一个线程,并返回
  43.    该线程对应的struct thread对象。
  44. ******************************************/  
  45.   1298                td = runq_choose(&tdq->tdq_idle);
  46.   1299                if (td != NULL) {
  47.   1300                        KASSERT(td->td_priority >= PRI_MIN_IDLE,
  48.   1301                            ("tdq_choose: Invalid priority on idle queue %d",
  49.   1302                            td->td_priority));
  50.   1303                        return (td);
  51.   1304                }
  52.   1305       
  53. /************************************************************************************
  54. * 如果执行到这里,就表示tdq_realtime队列,tdq_timeshare队列,tdq_idle队列三个队列
  55.    中都没有线程,此时返回NULL。
  56. **************************************/
  57.   1306                return (NULL);
  58.   1307        }
复制代码
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP