読者です 読者をやめる 読者になる 読者になる

NetBSDのスケジューラを読んでみるめも4

netbsd kernel

〆(.. )カリカリッ!! sched_schedclock()です。

関数本体よりもコメントの方が多いですね。

    376 /*
    377  * We adjust the priority of the current LWP.  The priority of a LWP
    378  * gets worse as it accumulates CPU time.  The CPU usage estimator (l_estcpu)
    379  * is increased here.  The formula for computing priorities will compute a
    380  * different value each time l_estcpu increases. This can cause a switch,
    381  * but unless the priority crosses a PPQ boundary the actual queue will not
    382  * change.  The CPU usage estimator ramps up quite quickly when the process
    383  * is running (linearly), and decays away exponentially, at a rate which is
    384  * proportionally slower when the system is busy.  The basic principle is
    385  * that the system will 90% forget that the process used a lot of CPU time
    386  * in 5 * loadav seconds.  This causes the system to favor processes which
    387  * haven't run much recently, and to round-robin among other processes.
    388  */
    389 
    390 void
    391 sched_schedclock(struct lwp *l)
    392 {
    393 
    394 	if (l->l_class != SCHED_OTHER)
    395 		return;
    396 
    397 	KASSERT(!CURCPU_IDLE_P());
    398 	l->l_estcpu = ESTCPULIM(l->l_estcpu + ESTCPU_ACCUM);
    399 	lwp_lock(l);
    400 	resetpriority(l);
    401 	lwp_unlock(l);
    402 }

まずスケジューリングポリシーをチェックしてSCHED_OTHER以外のFIFOとかRound-robinなプロセスの場合は何もせずにreturnします。
次にcpu使用率の計算をESTCPULIMマクロを使って実行。このマクロはsched_4bsd.c内で定義されています。

    167 #define	ESTCPU_SHIFT	11
    168 #define	ESTCPU_MAX	((PRIO_MAX - 2) << ESTCPU_SHIFT)
    169 #define	ESTCPU_ACCUM	(1 << (ESTCPU_SHIFT - 1))
    170 #define	ESTCPULIM(e)	min((e), ESTCPU_MAX)

ここでESTCPU_MAXのところで(PRIO_MAX - 2)という式がありますが、この-2の意味はどこかで読んだ気がするのですが思い出せません(´・ω・`)

PRIO_MAXはsys/sys/resource.hで定義されています。

     40 /*
     41  * Process priority specifications to get/setpriority.
     42  */
     43 #define	PRIO_MIN	-20
     44 #define	PRIO_MAX	20

この様な感じでl_estcpuを設定したらresetpriority()を呼ぶという流れですね。

sched_schedclock()のコールフローはこのような流れです。

hardclock() --> schedclock() --> sched_schedclock()

hardclock()はkern_clock.cにあります。
タイマ割り込みを契機としてhardclock()が実行され、状況によってschedclock()を呼ぶようですね。

    214 	/*
    215 	 * If no separate schedclock is provided, call it here
    216 	 * at about 16 Hz.
    217 	 */
    218 	if (schedhz == 0) {
    219 		if ((int)(--ci->ci_schedstate.spc_schedticks) <= 0) {
    220 			schedclock(l);
    221 			ci->ci_schedstate.spc_schedticks = hardscheddiv;
    222 		}
    223 	}

schedhzが何者かというところですがsys/sys/sched.hでは16が理想と書かれています。

   203 extern int schedhz;			/* ideally: 16 */

nxrで検索したのですがi386とかamd64の場合は分かりませんでした(´・ω・`)ガッカリ…
しょうがないのでカーネルモジュール作ってprintfさせてみましたw 一応DragonFly BSDのカーネルモジュールはhello worldレベル程度は作ったことあるので似たようなもんだろうと思いつつ調べてたらカーネルソース内にサンプルを見つけたので運が良かったかも。
モジュールのコードはこれでschedhzをprintfするだけです。

#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/sched.h>

MODULE(MODULE_CLASS_MISC, test_mod, NULL);

static int
test_mod_modcmd(modcmd_t cmd, void *arg)
{
    switch (cmd) {
        case MODULE_CMD_INIT:
            printf("shcedhz is %d\n", schedhz);
            break;
        case MODULE_CMD_FINI:
            printf("goodbye\\n");
            break;
        case MODULE_CMD_STAT:
            break;
        default:
            printf("unknown param %d\n", cmd);
            return ENOTTY;
    }

    return 0;
}

Makefileはこちら。

KMOD?=          test-mod
SRCS=           test-mod.c

.include <bsd.kmodule.mk>

あとは↓のようにモジュールをロードしてdmesgで確認すると…

[masami@nbsd:~/test-mod]$ sudo /sbin/modload ./test-mod.kmod

結果は0でした。

wsdisplay0: screen 1 added (80x25, vt100 emulation)
wsdisplay0: screen 2 added (80x25, vt100 emulation)
wsdisplay0: screen 3 added (80x25, vt100 emulation)
wsdisplay0: screen 4 added (80x25, vt100 emulation)
shcedhz is 0
[masami@nbsd:~/test-mod]$

それでは話をコールフローに戻して。。。
schedclock()もsys/kern/kern_clock.cにあり、このような関数です。

    330 void
    331 schedclock(struct lwp *l)
    332 {
    333 	if ((l->l_flag & LW_IDLE) != 0)
    334 		return;
    335 
    336 	sched_schedclock(l);
    337 }
    338 

アイドルプロセス以外ならsched_schedclock()を呼んでますね。