- 论坛徽章:
- 0
|
本帖最后由 71v5 于 2014-06-25 21:27 编辑
[当一个线程需要睡眠或者被抢占,或者需要重新调度时,内核代码调用函数mi_switch完成线程切换与硬件上下文无关的的处理]:
mi_switch函数用到的相关标志:- 662 /* Types and flags for mi_switch(). */
- 663 #define SW_TYPE_MASK 0xff /* First 8 bits are switch type */
- 664 #define SWT_NONE 0 /* Unspecified switch. */
- /* 665:因为抢占 8/
- 665 #define SWT_PREEMPT 1 /* Switching due to preemption. */
- /* 666:从临界区退出时,但是此时还有其他thread访问临界区时,就会以SWT_OWEPREEMPT标志调用mini_switch函数 */
- 666 #define SWT_OWEPREEMPT 2 /* Switching due to opepreempt. */
- /* 667:当thread要互斥的访问某个变量,而该变量此刻忙,就会以SWT_TURNSTILE标志调用mini_switch函数 */
- 667 #define SWT_TURNSTILE 3 /* Turnstile contention. */
- /**************************************************************************
- * 668-669:如果thread睡眠,就会以SWT_SLEEPQ或者SWT_SLEEPQTIMO标志调用
- mini_switch函数。
- *************/
- 668 #define SWT_SLEEPQ 4 /* Sleepq wait. */
- 669 #define SWT_SLEEPQTIMO 5 /* Sleepq timeout wait. */
- /**************************************************************************
- * 670: 当发出一个yield系统调用时,该系统调用的内部实现会以SWT_RELINQUISH
- 标志调用mini_switch函数,即主动放弃cpu。
- *************/
- 670 #define SWT_RELINQUISH 6 /* yield call. */
- /****************************************************************
- * 671:时钟中断发生时,会递减当前正在运行thread的时间片,如果
- 为零,先重新计算其时间片,然后设置其TDF_NEEDRESCHED标志,
- 在中断返回时,会检查thread是否设置了TDF_NEEDRESCHED标志,
- 如果设置了,就会以SWT_NEEDRESCHED标志调用mini_switch函数。
- *************/
- 671 #define SWT_NEEDRESCHED 7 /* NEEDRESCHED was set. */
- /****************************************************************
- * 672:中断线程在等待一个中断的到来的时候会以标志SWT_IDLE调用
- mini_switch函数。
- *****/
- 672 #define SWT_IDLE 8 /* Switching from the idle thread. */
- /*********************
- 673 #define SWT_IWAIT 9 /* Waiting for interrupts. */
- /*******************************************************************
- * 674:当thread接收到一个默认操作是top的信号时,并且相应的信号
- 处理程序没有被捕获时,此时thread就会以SWT_SUSPEND标志
- 调用mini_switch函数。
- ************/
- 674 #define SWT_SUSPEND 10 /* Thread suspended. */
- /*******************************************************************
- * 675-676:
- 在smp系统中,cpu0向cpu1发出了一个抢占的IPI中断,如果此时cpu1上
- 运行的是idle线程,就以SWT_REMOTEWAKEIDLE标志调用mini_switch函数。
- 如果此时cpu1上运行的是普通线程,就以SWT_REMOTEPREEMPT标志调用
- mini_switch函数。
- ********/
- 675 #define SWT_REMOTEPREEMPT 11 /* Remote processor preempted. */
- 676 #define SWT_REMOTEWAKEIDLE 12 /* Remote processor preempted idle. */
- /*******************************************************************
- * 677:sched_switch_stats数组的大小。
- *********/
- 677 #define SWT_COUNT 13 /* Number of switch types. */
- 678 /*******************************************************************
- * 679: 当发出一个yield系统调用时,该系统调用的内部实现会以SW_VOL
- 标志调用mini_switch函数,即主动放弃cpu。
- **************/
- 679 #define SW_VOL 0x0100 /* Voluntary switch. 主动进行线程切换 */
- /*******************************************************************
- * 680-681:
- 如果一个thread被抢占,从sched_preempt函数中会以SW_INVOL和
- SW_PREEMPT标志调用mi_switch函数时。
- **************/
- 680 #define SW_INVOL 0x0200 /* Involuntary switch. 被动进行线程切换 */
- 681 #define SW_PREEMPT 0x0400 /* The invol switch is a preemption 因为抢占? */
复制代码-
- 419
- 420
- /**********************************************************
- * The machine independent parts of context switching.
- 参数描述:
- flags:传递给mi_switch函数的一些标志,如上所示。
- newtd:将要被调度运行的新线程对应的struct thread对象。
- volatile int ticks; 系统启动以来经过的节拍数,
- 由函数hardclock_cnt维护
- **************************/
- 421
- 422 void
- 423 mi_switch(int flags, struct thread *newtd)
- 424 {
- /*******************************************************************
- * runtime:线程的运行时间,使用intel cpu中的TIME-STAMP COUNTER
- 来描述。
- new_switchtime:执行线程切换时的时间戳,使用intel cpu中的
- TIME-STAMP COUNTER来描述。
- td:将要被替换的thread对应的struct thread对象,这里为curthread。
- p:和td相关联的struct proc对象。
- 为了描述方便,这里将td,p简单记为old_td,old_p。
- *********************************/
- 425 uint64_t runtime, new_switchtime;
- 426 struct thread *td;
- 427 struct proc *p;
- 428
- 429 td = curthread; /* XXX */
- 430 THREAD_LOCK_ASSERT(td, MA_OWNED | MA_NOTRECURSED);
- 431 p = td->td_proc; /* XXX */
- 432 KASSERT(!TD_ON_RUNQ(td), ("mi_switch: called by old code"));
- 433 #ifdef INVARIANTS
- 434 if (!TD_ON_LOCK(td) && !TD_IS_RUNNING(td))
- 435 mtx_assert(&Giant, MA_NOTOWNED);
- 436 #endif
- 437 KASSERT(td->td_critnest == 1 || panicstr,
- 438 ("mi_switch: switch in a critical section"));
- 439 KASSERT((flags & (SW_INVOL | SW_VOL)) != 0,
- 440 ("mi_switch: switch must be voluntary or involuntary"));
- 441 KASSERT(newtd != curthread, ("mi_switch: preempting back to ourself"));
- 442
- 443 /*
- 444 * Don't perform context switches from the debugger.
- 446-447:debugger被激活?
- 445 */
- 446 if (kdb_active)
- 447 kdb_switch();
- /**************************************************************************
- * td_stopsched:如果非零,就表示调度程序被暂停,此时将不会发生线程切换,
- 函数panic会将该成员设置为1.
- * #define SCHEDULER_STOPPED() __predict_false(curthread->td_stopsched)
- ****************/
- 448 if (SCHEDULER_STOPPED())
- 449 return;
- /****************************************************************
- * 450-454:
- 递增相应的计数器。
- #define SW_VOL 0x0100 Voluntary switch. 主动进行线程切换
-
- td_swvoltick:当以标志SW_VOL调用函数mi_switch时,该成员被设置为
- 当时的ticks。
- long ru_nvcsw; voluntary context switches
- long ru_nivcsw; involuntary
- **********************************/
- 450 if (flags & SW_VOL) {
- 451 td->td_ru.ru_nvcsw++;
- 452 td->td_swvoltick = ticks;
- 453 } else
- 454 td->td_ru.ru_nivcsw++;
- 455 #ifdef SCHED_STATS
- 456 SCHED_STAT_INC(sched_switch_stats[flags & SW_TYPE_MASK]);
- 457 #endif
- 458
- 459
- 460
- /*****************************************************************
- * Compute the amount of time during which the current
- * thread was running, and add that to its total so far.
- 462:函数cpu_ticks读取intel cpu的TIME-STAMP COUNTER,用该
- 计数器的值初始化new_switchtime。
- 463:runtime记录了线程的运行时间,cpu_ticks描述。
- 464-465:将runtime累积到td_runtime和td_incruntime上。
- 466:更新struct pcpu对象的pc_switchtime成员。
- *****************************/
- 461
- 462 new_switchtime = cpu_ticks();
- 463 runtime = new_switchtime - PCPU_GET(switchtime);
- 464 td->td_runtime += runtime;
- 465 td->td_incruntime += runtime;
- 466 PCPU_SET(switchtime, new_switchtime);
- 467 td->td_generation++; /* bump preempt-detect counter */
- /******************************************************
- * 468:递增struct vmmeter对象的v_swtch成员,该成员
- 记录了系统中执行线程切换的次数。
- 469:更新struct pcpu对象的pc_switchticks成员,以
- 全局的ticks来描述。
- *************************/
- 468 PCPU_INC(cnt.v_swtch);
- 469 PCPU_SET(switchticks, ticks);
- 470 CTR4(KTR_PROC, "mi_switch: old thread %ld (td_sched %p, pid %ld, %s)",
- 471 td->td_tid, td->td_sched, p->p_pid, td->td_name);
- 472 #if (KTR_COMPILE & KTR_SCHED) != 0
- 473 if (TD_IS_IDLETHREAD(td))
- 474 KTR_STATE1(KTR_SCHED, "thread", sched_tdname(td), "idle",
- 475 "prio:%d", td->td_priority);
- 476 else
- 477 KTR_STATE3(KTR_SCHED, "thread", sched_tdname(td), KTDSTATE(td),
- 478 "prio:%d", td->td_priority, "wmesg:\"%s\"", td->td_wmesg,
- 479 "lockname:\"%s\"", td->td_lockname);
- 480 #endif
- 481 SDT_PROBE0(sched, , , preempt);
- 482 #ifdef XEN
- 483 PT_UPDATES_FLUSH();
- 484 #endif
- /* 485:调用调度程序相关的sched_switch函数 */
- 485 sched_switch(td, newtd, flags);
- 486 KTR_STATE1(KTR_SCHED, "thread", sched_tdname(td), "running",
- 487 "prio:%d", td->td_priority);
- 488
- 489 CTR4(KTR_PROC, "mi_switch: new thread %ld (td_sched %p, pid %ld, %s)",
- 490 td->td_tid, td->td_sched, p->p_pid, td->td_name);
- 491
- 492 /*
- 493 * If the last thread was exiting, finish cleaning it up.
- 494 */
- 495 if ((td = PCPU_GET(deadthread))) {
- 496 PCPU_SET(deadthread, NULL);
- 497 thread_stash(td);
- 498 }
- 499 }
- [ULE调度程序-sched_switch函数]:
- / ********************************************************************
- * Switch threads. This function has to handle threads coming in while
- * blocked for some reason, running, or idle. It also must deal with
- * migrating a thread from one queue to another as running threads may
- * be assigned elsewhere via binding.
- 函数sched_switch完成线程切换,在SMP系统中,还要将线程在系统中cpu
- 的运行队列来回迁移,以保持运行队列的平衡。
-
- 参数描述:
- td:将要被替换的thread。
- newtd:将要被调度运行的thread。
- flags:一些标志。
- *********************************/
- 1829 void
- 1830 sched_switch(struct thread *td, struct thread *newtd, int flags)
- 1831 {
- /*********************************************************
- * 局部变量描述:
- tdq:指向当前cpu对应的struct tdq对象。
- ts:指向和td相关联的struct td_sched数据对象。
- mtx:指向防止并发访问的互斥数据结构。
- cpuid:当前cpu的logical id。
- preempted:如果preempted非零,就表示是由于被抢占而进行
- 的线程切换;如果preempted为零,就表示是因为
- 线程的时间片用完而进行的线程切换。
- *************************************/
- 1832 struct tdq *tdq;
- 1833 struct td_sched *ts;
- 1834 struct mtx *mtx;
- 1835 int srqflag;
- 1836 int cpuid, preempted;
- 1837
- 1838 THREAD_LOCK_ASSERT(td, MA_OWNED);
- 1839 KASSERT(newtd == NULL, ("sched_switch: Unsupported newtd argument"));
- 1840
- /*************************************************************
- * 1841:获取当前cpu的logical id。
- 1842:tdq指向数组元素tdq_cpu[cpuid]。
- 1843:ts指向和td相关联的struct td_sched数据对象。
- 1845:函数sched_pctcpu_update更新struct td_sched对象的
- 相关成员。
- 1846:将struct td_sched对象的ts_rlick成员设置为ticks。
- *************************************/
- 1841 cpuid = PCPU_GET(cpuid);
- 1842 tdq = TDQ_CPU(cpuid);
- 1843 ts = td->td_sched;
- 1844 mtx = td->td_lock;
- 1845 sched_pctcpu_update(ts, 1);
- 1846 ts->ts_rltick = ticks;
- /****************************************************************
- * 1847-1848:
- #define NOCPU 0xff For when we aren't on a CPU.
- u_char td_lastcpu; Last cpu we were on.
- u_char td_oncpu; Which cpu we are on.
- td_oncpu:负责执行线程td的cpu的logical id,在线程切换时,
- td_lastcpu成员会被更新为td_oncpu的值,当线程
- td重新被调度运行时,td_oncpu会被更新。
- **************************************/
- 1847 td->td_lastcpu = td->td_oncpu;
- 1848 td->td_oncpu = NOCPU;
- /***********************************************************************
- * #define TDF_SCHED2 0x04000000 Reserved for scheduler private use
- * #define TDF_SLICEEND TDF_SCHED2 Thread time slice is over.
- #define TD_IS_IDLETHREAD(td) ((td)->td_flags & TDF_IDLETD)
- #define TDF_NEEDRESCHED 0x00010000 Thread needs to yield.
- 1849:线程切换是被抢占还是自身时间片用完?
- 1850:清TDF_NEEDRESCHED和TDF_SLICEEND标志。
- 1851:暂时还不清楚其作用。
- volatile u_char td_owepreempt; (k*) Preempt on last critical_exit。
- 1852-1853:如果td不是idle线程,就递增相应的计数器。
- short tdq_switchcnt; Switches this tick.
- ********************************/
- 1849 preempted = !(td->td_flags & TDF_SLICEEND);
- 1850 td->td_flags &= ~(TDF_NEEDRESCHED | TDF_SLICEEND);
- 1851 td->td_owepreempt = 0;
- 1852 if (!TD_IS_IDLETHREAD(td))
- 1853 tdq->tdq_switchcnt++;
- /**********************************************************************
- * The lock pointer in an idle thread should never change. Reset it
- * to CAN_RUN as well.
- 1858-1883:如何处理td(将要被替换的线程thread)。
- 1858-1861:td为idle线程->
- #define TD_SET_CAN_RUN(td) (td)->td_state = TDS_CAN_RUN
- 此时宏TD_SET_CAN_RUN将td的状态设置为TDS_CAN_RUN。
- ********************************/
- 1858 if (TD_IS_IDLETHREAD(td)) {
- 1859 MPASS(td->td_lock == TDQ_LOCKPTR(tdq));
- 1860 TD_SET_CAN_RUN(td);
- 1861 } else if (TD_IS_RUNNING(td)) {
- /***********************************************************************************
- * 1861-1878:
- 线程td此时处于运行状态,或者被抢占,或者自身的时间片用完,进而执行
- 了线程切换。
-
- 1863-1865:确定srqflag,该变量包含了一些标志,这些标志标识了进行
- 线程切换的reason。
- 1866-1869:
- SMP支持,判断是否将td迁移到系统中另一个cpu的运行队列上。函数sched_pickcpu的任务
- 如下:
- 选择一个负载最小,并且该cpu在线程td的CPU affinity sets(td_cpuset)集合中,并返回
- 该cpu 的logical id。
-
- #define THREAD_CAN_MIGRATE(td) ((td)->td_pinned == 0)
- #define THREAD_CAN_SCHED(td, cpu) CPU_ISSET((cpu), &(td)->td_cpuset->cs_mask)
- ****************************************/
- 1862 MPASS(td->td_lock == TDQ_LOCKPTR(tdq));
- 1863 srqflag = preempted ?
- 1864 SRQ_OURSELF|SRQ_YIELDING|SRQ_PREEMPTED :
- 1865 SRQ_OURSELF|SRQ_YIELDING;
- 1866 #ifdef SMP
- 1867 if (THREAD_CAN_MIGRATE(td) && !THREAD_CAN_SCHED(td, ts->ts_cpu))
- 1868 ts->ts_cpu = sched_pickcpu(td, 0);
- 1869 #endif
- /************************************************************
- * 1870-1872:将在同一个cpu上运行?
- 根据srqflag参数中的标志,将td重新连接到cpu的运行队列中。
- 1872-1877:不在同一个cpu上运行。函数sched_switch_migrate
- 将td迁移到相应cpu的运行队列中。
- ******************************/
- 1870 if (ts->ts_cpu == cpuid)
- 1871 tdq_runq_add(tdq, td, srqflag);
- 1872 else {
- 1873 KASSERT(THREAD_CAN_MIGRATE(td) ||
- 1874 (ts->ts_flags & TSF_BOUND) != 0,
- 1875 ("Thread %p shouldn't migrate", td));
- 1876 mtx = sched_switch_migrate(tdq, td, srqflag);
- 1877 }
- 1878 } else {
- 1879
- /********************************************************
- * This thread must be going to sleep.
- 1880-1882:线程将要睡眠。函数tdq_load_rem更新相应cpu
- 的负载等。
-
- *********************/
- 1880 TDQ_LOCK(tdq);
- 1881 mtx = thread_lock_block(td);
- 1882 tdq_load_rem(tdq, td);
- 1883 }
- /**********************************************************************
- * We enter here with the thread blocked and assigned to the
- * appropriate cpu run-queue or sleep-queue and with the current
- * thread-queue locked.
- 1890:函数choosethread从当前cpu的运行队列中选择一个优先级最高的
- thread带来运行,并返回该thread对应的struct thread对象。
- **********************************/
- 1889 TDQ_LOCK_ASSERT(tdq, MA_OWNED | MA_NOTRECURSED);
- 1890 newtd = choosethread();
- /********************************************************
- * Call the MD code to switch contexts if necessary.
- 1894-1930:只有当td和newtd不是同一个thread时,才调用
- cpu_switch函数完成线程硬件上下文的切换。
- **********************************/
- 1894 if (td != newtd) {
- 1895 #ifdef HWPMC_HOOKS
- 1896 if (PMC_PROC_IS_USING_PMCS(td->td_proc))
- 1897 PMC_SWITCH_CONTEXT(td, PMC_FN_CSW_OUT);
- 1898 #endif
- 1899 SDT_PROBE2(sched, , , off_cpu, newtd, newtd->td_proc);
- 1900 lock_profile_release_lock(&TDQ_LOCKPTR(tdq)->lock_object);
- 1901 TDQ_LOCKPTR(tdq)->mtx_lock = (uintptr_t)newtd;
- 1902 sched_pctcpu_update(newtd->td_sched, 0);
- 1903
- 1904 #ifdef KDTRACE_HOOKS
- 1905 /*
- 1906 * If DTrace has set the active vtime enum to anything
- 1907 * other than INACTIVE (0), then it should have set the
- 1908 * function to call.
- 1909 */
- 1910 if (dtrace_vtime_active)
- 1911 (*dtrace_vtime_switch_func)(newtd);
- 1912 #endif
- 1913 /* 1914:cpu_switch完成线程硬件上下文的切换 */
- 1914 cpu_switch(td, newtd, mtx);
- /****************************************************************
- * We may return from cpu_switch on a different cpu. However,
- * we always return with td_lock pointing to the current cpu's
- * run queue lock.
- 当线程(刚创建的线程,内核线程等除外)被重新调度运行时,会从
- 这里开始执行。
- 1920:cpuid为当前cpu的logical id。
- 1921:tdq为logical id为cpuid的cpu对应的运行队列。
- *************************************/
- 1920 cpuid = PCPU_GET(cpuid);
- 1921 tdq = TDQ_CPU(cpuid);
- 1922 lock_profile_obtain_lock_success(
- 1923 &TDQ_LOCKPTR(tdq)->lock_object, 0, 0, __FILE__, __LINE__);
- 1924
- 1925 SDT_PROBE0(sched, , , on_cpu);
- 1926 #ifdef HWPMC_HOOKS
- 1927 if (PMC_PROC_IS_USING_PMCS(td->td_proc))
- 1928 PMC_SWITCH_CONTEXT(td, PMC_FN_CSW_IN);
- 1929 #endif
- 1930 } else {
- 1931 thread_unblock_switch(td, mtx);
- 1932 SDT_PROBE0(sched, , , remain_cpu);
- 1933 }
- /**************************************************
- * Assert that all went well and return.
- 1939:更新struct thread对象的td_oncpu成员。
- **************************/
- 1937 TDQ_LOCK_ASSERT(tdq, MA_OWNED|MA_NOTRECURSED);
- 1938 MPASS(td->td_lock == TDQ_LOCKPTR(tdq));
- 1939 td->td_oncpu = cpuid;
- 1940 }
复制代码 |
|