2021年01月27日
情報科学類 オペレーティングシステム II
筑波大学 システム情報系
新城 靖
<yas@cs.tsukuba.ac.jp>
このページは、次の URL にあります。
http://www.coins.tsukuba.ac.jp/~yas/coins/os2-2020/2021-01-27
あるいは、次のページから手繰っていくこともできます。
http://www.coins.tsukuba.ac.jp/~yas/
http://www.cs.tsukuba.ac.jp/~yas/
struct timeval {
time_t tv_sec; /* seconds. long int */
suseconds_t tv_usec; /* microseconds. long int */
};
int gettimeofday(struct timeval *tp, struct timezone *tzp);
int settimeofday(const struct timeval *tp, const struct timezone *tzp);
使い方
1: /*
2: gettimeofday-print.c -- get colander time and print
3: Created on: 2014/01/22 20:40:34
4: */
5:
6: #include <sys/time.h> /* gettimeofday() */
7: #include <time.h> /* ctime() */
8: #include <stdio.h>
9:
10: main()
11: {
12: struct timeval tv;
13: time_t sec;
14: gettimeofday( &tv, NULL );
15: sec = tv.tv_sec;
16: printf("%s", ctime(&sec) );
17: }
$ make gettimeofday-print
cc gettimeofday-print.c -o gettimeofday-print
$ ./gettimeofday-print
Wed Jan 20 18:38:05 2021
$ date
Wed Jan 20 18:38:06 JST 2021
$
POSIX 1003.1, 2003 の
struct timespec
では、ナノ秒単位。
struct timespec {
time_t tv_sec; /* Seconds. */
long int tv_nsec; /* Nanoseconds. */
};
int clock_settime(clockid_t clock_id, const struct timespec *tp);
int clock_gettime(clockid_t clock_id, struct timespec *tp);
int clock_getres(clockid_t clock_id, struct timespec *res);
clock_id としては、CLOCK_REALTIME (カレンダ時刻)やCLOCK_MONOTONIC があ
る。
カレンダ時刻は、変更できる。逆走させることも可能。
順方向のジャンプや逆走を避けて、カレンダ時刻を合わせるには、adjtime() を使う。
int adjtime(const struct timeval *delta, struct timeval *olddelta);adjtime() を使った時刻同期の方法。
struct itimerval {
struct timeval it_interval; /* next value */
struct timeval it_value; /* current value */
};
int setitimer(int which, const struct itimerval *value,
struct itimerval *ovalue);
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
struct timeval *timeout);
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
int kevent(int kq, const struct kevent *changelist, int nchanges,
struct kevent *eventlist, int nevents, const struct timespec *timeout);
ネットワーク・プログラムでよく使う。複数の入力を監視する。指定された時
間、入力がなければ、システム・コールから復帰する。
なにもしない時間切れ。
unsigned int sleep(unsigned int seconds); int usleep(useconds_t usec); int nanosleep(const struct timespec *rqtp, struct timespec *rmtp);

図? タイマ関連のハードウェアの基本モデル
2つの機能がある。
その他の割込み
linux-5.10.3/include/asm-generic/param.h 8: # define HZ CONFIG_HZ /* Internal kernel timer frequency */ linux-5.10.3/include/generated/autoconf.h 1100: #define CONFIG_HZ 1000 linux-5.10.3/include/linux/jiffies.h 79: extern u64 __cacheline_aligned_in_smp jiffies_64; 80: extern unsigned long volatile __cacheline_aligned_in_smp __jiffy_arch_data jiffies;
linux-5.10.3/kernel/time/tick-common.c
84: static void tick_periodic(int cpu)
85: {
86: if (tick_do_timer_cpu == cpu) {
...
93: do_timer(1);
...
96: update_wall_time();
97: }
...
99: update_process_times(user_mode(get_irq_regs()));
...
101: }
linux-5.10.3/kernel/time/timer.c
59: __visible u64 jiffies_64 __cacheline_aligned_in_smp = INITIAL_JIFFIES;
linux-5.10.3/kernel/time/timekeeping.c
2267: void do_timer(unsigned long ticks)
2268: {
2269: jiffies_64 += ticks;
2270: calc_global_load();
2271: }
xtime_nsec >> shift でナノ秒を表す。
linux-5.10.3/include/linux/timekeeper_internal.h
92: struct timekeeper {
93: struct tk_read_base tkr_mono;
...
95: u64 xtime_sec;
...
139: };
34: struct tk_read_base {
35: struct clocksource *clock;
...
39: u32 shift;
40: u64 xtime_nsec;
...
43: };
linux-5.10.3/kernel/time/timekeeping.c
127: static inline struct timespec64 tk_xtime(const struct timekeeper *tk)
128: {
129: struct timespec64 ts;
130:
131: ts.tv_sec = tk->xtime_sec;
132: ts.tv_nsec = (long)(tk->tkr_mono.xtime_nsec >> tk->tkr_mono.shift);
133: return ts;
134: }
linux-5.10.3/kernel/time/time.c
140: SYSCALL_DEFINE2(gettimeofday, struct __kernel_old_timeval __user *, tv,
141: struct timezone __user *, tz)
142: {
143: if (likely(tv != NULL)) {
144: struct timespec64 ts;
145:
146: ktime_get_real_ts64(&ts);
147: if (put_user(ts.tv_sec, &tv->tv_sec) ||
148: put_user(ts.tv_nsec / 1000, &tv->tv_usec))
149: return -EFAULT;
150: }
151: if (unlikely(tz != NULL)) {
152: if (copy_to_user(tz, &sys_tz, sizeof(sys_tz)))
153: return -EFAULT;
154: }
155: return 0;
156: }
linux-5.10.3/kernel/time/timekeeping.c
800: void ktime_get_real_ts64(struct timespec64 *ts)
801: {
802: struct timekeeper *tk = &tk_core.timekeeper;
803: unsigned int seq;
804: u64 nsecs;
...
811: ts->tv_sec = tk->xtime_sec;
812: nsecs = timekeeping_get_ns(&tk->tkr_mono);
...
816: ts->tv_nsec = 0;
817: timespec64_add_ns(ts, nsecs);
818: }
linux-5.10.3/include/linux/timer.h
11: struct timer_list {
...
17: unsigned long expires;
18: void (*function)(struct timer_list *);
...
24: };
25:
jiffies が増加して expires に達すれば、(*function)(tl) を呼ぶ。 引数 tl は、truct timer_list *。
主に次の関数で操作する。
(*function)() で独自のデータ(以下の例では struct s1 *)を得るには、次の ようにする。
struct s1 {
...
struct timer_list s_timer;
...
int s_x;
...
};
void timer_list_handler(struct timer_list *tl) {
struct s1 *p1 = from_timer(p1, tl, s_timer);
f( p1->s_x );
}

図? timer_list から外側の構造体を求める
linux-5.10.3/kernel/time/timer.c
1789: struct process_timer {
1790: struct timer_list timer;
1791: struct task_struct *task;
1792: };
1832: signed long __sched schedule_timeout(signed long timeout)
1833: {
1834: struct process_timer timer;
1835: unsigned long expire;
...
1866: expire = timeout + jiffies;
1867:
1868: timer.task = current;
1869: timer_setup_on_stack(&timer.timer, process_timeout, 0);
1870: __mod_timer(&timer.timer, expire, MOD_TIMER_NOTPENDING);
1871: schedule();
1872: del_singleshot_timer_sync(&timer.timer);
1873:
1874: /* Remove the timer from the object tracker */
1875: destroy_timer_on_stack(&timer.timer);
1876:
1877: timeout = expire - jiffies;
1878:
1879: out:
1880: return timeout < 0 ? 0 : timeout;
1881: }
1794: static void process_timeout(struct timer_list *t)
1795: {
1796: struct process_timer *timeout = from_timer(timeout, t, timer);
1797:
1798: wake_up_process(timeout->task);
1799: }
linux-5.10.3/include/linux/timer.h
153: #define from_timer(var, callback_timer, timer_fieldname) \
154: container_of(callback_timer, typeof(*var), timer_fieldname)
linux-5.10.3/include/linux/kernel.h
851: #define container_of(ptr, type, member) ({ \
852: void *__mptr = (void *)(ptr); \
853: BUILD_BUG_ON_MSG(!__same_type(*(ptr), ((type *)0)->member) && \
854: !__same_type(*(ptr), void), \
855: "pointer type mismatch in container_of()"); \
856: ((type *)(__mptr - offsetof(type, member))); }
)
linux-5.10.3/include/linux/hrtimer.h
39: enum hrtimer_mode {
40: HRTIMER_MODE_ABS = 0x00,
41: HRTIMER_MODE_REL = 0x01,
...
60: };
65: enum hrtimer_restart {
66: HRTIMER_NORESTART, /* Timer is not restarted */
67: HRTIMER_RESTART, /* Timer must be restarted */
68: };
118: struct hrtimer {
...
121: enum hrtimer_restart (*function)(struct hrtimer *);
...
127: };
主に次の関数で操作する。
struct hrtimer my_timer;
hrtimer_init(&my_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
my_timer.function = my_timer_handler;
...
hrtimer_start(&my_timer, ktime_set(0, t_nano), HRTIMER_MODE_REL);
...
enum hrtimer_restart my_timer_handler(struct hrtimer *timer)
{
...
return HRTIMER_NORESTART;
}
例: Ethernet のドライバでモードを変更して 2 マイクロ秒だけ待つ。
様々な方法がある。
例1: 10 tick (インターバル・タイマによる割り込み)を待つ。
unsigned long timeout = jiffies + 10; // 10 ticks
while (time_before(jiffies,timeout))
continue;
例2: 2秒待つ
unsigned long delay = jiffies + 2*HZ; // 2秒
while (time_before(jiffies,delay))
continue;
unsigned long timeout = jiffies + 10; // 10 ticks
while (jiffies<timeout)
continue;
引き算して 0 と比較すると、オーバフローの問題が解決できる。
unsigned long timeout = jiffies + 10; // 10 ticks
while (jiffies-timeout<0)
continue;
次のマクロを使う方法もある。
linux-5.10.3/include/linux/jiffies.h 104: #define time_after(a,b) \ 105: (typecheck(unsigned long, a) && \ 106: typecheck(unsigned long, b) && \ 107: ((long)((b) - (a)) < 0)) 108: #define time_before(a,b) time_after(b,a) 109: 110: #define time_after_eq(a,b) \ 111: (typecheck(unsigned long, a) && \ 112: typecheck(unsigned long, b) && \ 113: ((long)((a) - (b)) >= 0)) 114: #define time_before_eq(a,b) time_after_eq(b,a)
unsigned long delay = jiffies + 2*HZ; // 2秒
while (time_before(jiffies,delay))
cond_resched();
他に実行すべき重要なプロセスが存在する(条件)時には、スケジューラを呼ん
で、実行する。存在しなければ、空ループと同じ。ただし、スケジューラを呼
ぶ(sleepする可能性がある)ので、割り込みコンテキストからは使えない。
void ndelay(unsigned long nsecs) void udelay(unsigned long usecs) void mdelay(unsigned long msecs)udelay() は、ある回数のループで実装されている。回数は、CPUの速度等で決 まる。ndelay(), mdelay() は、udelay() を呼んでいる。
udelay() で1ミリ秒以上待ってはいけない。 ループのインデックスがオーバフローする可能性がある。
set_current_state( TASK_INTERRUPTIBLE ); // signal で起きる可能性がある schedule_timeout( s * HZ );実装には struct timer_list が使われている。
| 表示 | 説明 |
| NI | Nice。優先度を表す値。 |
$ /bin/ps l
F UID PID PPID PRI NI VSZ RSS WCHAN STAT TTY TIME COMMAND
0 1013 20638 20636 20 0 123572 2100 wait Ss pts/2 0:00 -bash
0 1013 21139 20638 20 0 155660 5900 poll_s S pts/2 0:02 xterm -class UXTerm -title uxterm -u8
0 1013 21150 21139 20 0 123552 2144 wait Ss pts/3 0:00 bash
0 1013 21560 20638 20 0 267808 22928 poll_s S+ pts/2 0:09 emacs -nw
0 1013 21784 21150 20 0 103748 956 signal T pts/3 0:00 lv kernel/time/timer.c
0 1013 27031 21150 20 0 108132 980 - R+ pts/3 0:00 /bin/ps l
$ /bin/nice /bin/ps l
F UID PID PPID PRI NI VSZ RSS WCHAN STAT TTY TIME COMMAND
0 1013 20638 20636 20 0 123572 2100 wait Ss pts/2 0:00 -bash
0 1013 21139 20638 20 0 155660 5900 poll_s S pts/2 0:02 xterm -class UXTerm -title uxterm -u8
0 1013 21150 21139 20 0 123552 2144 wait Ss pts/3 0:00 bash
0 1013 21560 20638 20 0 267808 22928 poll_s S+ pts/2 0:09 emacs -nw
0 1013 21784 21150 20 0 103748 956 signal T pts/3 0:00 lv kernel/time/timer.c
0 1013 27034 21150 30 10 108136 984 - RN+ pts/3 0:00 /bin/ps l
$ /bin/nice -19 /bin/ps l
F UID PID PPID PRI NI VSZ RSS WCHAN STAT TTY TIME COMMAND
0 1013 20638 20636 20 0 123572 2100 wait Ss pts/2 0:00 -bash
0 1013 21139 20638 20 0 155660 5900 - R pts/2 0:02 xterm -class UXTerm -title uxterm -u8
0 1013 21150 21139 20 0 123552 2144 wait Ss pts/3 0:00 bash
0 1013 21560 20638 20 0 267808 22928 poll_s S+ pts/2 0:09 emacs -nw
0 1013 21784 21150 20 0 103748 956 signal T pts/3 0:00 lv kernel/time/timer.c
0 1013 27035 21150 39 19 108132 984 - RN+ pts/3 0:00 /bin/ps l
$
1: /*
2: getpriority-pid.c -- 優先度の表示
3: ~yas/syspro/proc/getpriority-pid.c
4: Created on: 2009/12/14 12:15:11
5: */
6:
7: #include <stdio.h> /* stderr, fprintf() */
8: #include <sys/time.h> /* getpriority() */
9: #include <sys/resource.h> /* getpriority() */
10: #include <stdlib.h> /* strtol() */
11: #include <limits.h> /* strtol() */
12:
13: main( int argc, char *argv[] )
14: {
15: int which, who, prio;
16: pid_t pid;
17: if( argc != 2 )
18: {
19: fprintf(stderr,"Usage: %% %s pid\n",argv[0] );
20: exit( 1 );
21: }
22: pid = strtol( argv[1], NULL, 10 );
23: prio = getpriority( PRIO_PROCESS, pid );
24: printf("pid==%d, priority==%d\n", pid, prio);
25: }
$ ./getpriority-pid
Usage: % ./getpriority-pid pid
$ echo $$
21150
$ ./getpriority-pid
Usage: % ./getpriority-pid pid
$ ./getpriority-pid $$
pid==21150, priority==0
$ ./getpriority-pid 0
pid==0, priority==0
$ /bin/nice -10 ./getpriority-pid 0
pid==0, priority==10
$ /bin/nice -20 ./getpriority-pid 0
pid==0, priority==19
$
linux-5.10.3/include/linux/sched.h
640: struct task_struct {
...
649: volatile long state;
...
686: int prio;
687: int static_prio;
688: int normal_prio;
689: unsigned int rt_priority;
690:
691: const struct sched_class *sched_class;
692: struct sched_entity se;
693: struct sched_rt_entity rt;
...
697: struct sched_dl_entity dl;
...
721: unsigned int policy;
...
1366: };
451: struct sched_entity {
...
453: struct load_weight load;
454: struct rb_node run_node;
...
456: unsigned int on_rq;
...
458: u64 exec_start;
459: u64 sum_exec_runtime;
460: u64 vruntime;
...
487: };
325: struct load_weight {
326: unsigned long weight;
327: u32 inv_weight;
328: };
struct task_struct の中に、prio 等のフィールドやstruct sched_entity が
ある。
linux-5.10.3/include/uapi/linux/sched.h 114: #define SCHED_NORMAL 0 115: #define SCHED_FIFO 1 116: #define SCHED_RR 2 117: #define SCHED_BATCH 3 118: /* SCHED_ISO: reserved but not implemented yet */ 119: #define SCHED_IDLE 5 120: #define SCHED_DEADLINE 6
linux-5.10.3/kernel/sys.c
267: SYSCALL_DEFINE2(getpriority, int, which, int, who)
268: {
269: struct task_struct *g, *p;
270: struct user_struct *user;
271: const struct cred *cred = current_cred();
272: long niceval, retval = -ESRCH;
273: struct pid *pgrp;
274: kuid_t uid;
...
281: switch (which) {
282: case PRIO_PROCESS:
283: if (who)
284: p = find_task_by_vpid(who);
285: else
286: p = current;
287: if (p) {
288: niceval = nice_to_rlimit(task_nice(p));
289: if (niceval > retval)
290: retval = niceval;
291: }
292: break;
...
293: case PRIO_PGRP:
...
304: case PRIO_USER:
...
324: }
...
329: return retval;
330: }
linux-5.10.3/include/linux/sched/prio.h
5: #define MAX_NICE 19
6: #define MIN_NICE -20
7: #define NICE_WIDTH (MAX_NICE - MIN_NICE + 1)
...
22: #define MAX_USER_RT_PRIO 100
23: #define MAX_RT_PRIO MAX_USER_RT_PRIO
24:
25: #define MAX_PRIO (MAX_RT_PRIO + NICE_WIDTH)
26: #define DEFAULT_PRIO (MAX_RT_PRIO + NICE_WIDTH / 2)
...
33: #define NICE_TO_PRIO(nice) ((nice) + DEFAULT_PRIO)
34: #define PRIO_TO_NICE(prio) ((prio) - DEFAULT_PRIO)
linux-5.10.3/include/linux/sched.h
1678: static inline int task_nice(const struct task_struct *p)
1679: {
1680: return PRIO_TO_NICE((p)->static_prio);
1681: }
linux-5.10.3/include/linux/sched/prio.h
48: static inline long nice_to_rlimit(long nice)
49: {
50: return (MAX_NICE - nice + 1);
51: }
glibc-2.12/sysdeps/unix/sysv/linux/getpriority.c
28: #define PZERO 20
...
35: int
36: getpriority (enum __priority_which which, id_t who)
37: {
38: int res;
39:
40: res = INLINE_SYSCALL (getpriority, 2, (int) which, who);
41: if (res >= 0)
42: res = PZERO - res;
43: return res;
44: }
linux-5.10.3/kernel/sched/core.c
8445: /*
8446: * Nice levels are multiplicative, with a gentle 10% change for every
8447: * nice level changed. I.e. when a CPU-bound task goes from nice 0 to
8448: * nice 1, it will get ~10% less CPU time than another CPU-bound task
8449: * that remained on nice 0.
8450: *
8451: * The "10% effect" is relative and cumulative: from _any_ nice level,
8452: * if you go up 1 level, it's -10% CPU usage, if you go down 1 level
8453: * it's +10% CPU usage. (to achieve that we use a multiplier of 1.25.
8454: * If a task goes up by ~10% and another task goes down by ~10% then
8455: * the relative distance between them is ~25%.)
8456: */
8457: const int sched_prio_to_weight[40] = {
8458: /* -20 */ 88761, 71755, 56483, 46273, 36291,
8459: /* -15 */ 29154, 23254, 18705, 14949, 11916,
8460: /* -10 */ 9548, 7620, 6100, 4904, 3906,
8461: /* -5 */ 3121, 2501, 1991, 1586, 1277,
8462: /* 0 */ 1024, 820, 655, 526, 423,
8463: /* 5 */ 335, 272, 215, 172, 137,
8464: /* 10 */ 110, 87, 70, 56, 45,
8465: /* 15 */ 36, 29, 23, 18, 15,
8466: };
848: static void set_load_weight(struct task_struct *p, bool update_load)
849: {
850: int prio = p->static_prio - MAX_RT_PRIO;
851: struct load_weight *load = &p->se.load;
...
869: load->weight = scale_load(sched_prio_to_weight[prio]);
870: load->inv_weight = sched_prio_to_wmult[prio];
...
872: }
linux-5.10.3/kernel/sched/sched.h
134: # define scale_load(w) (w)
| 名前 | 説明 |
|---|---|
| enqueue_task | プロセスが実行可能(runnable)になった |
| dequeue_task | プロセスが実行可能ではなくなった |
| yield_task | CPUを譲る。dequeueしてenqueue |
| check_preempt_curr | 実行可能になった時にCPUを横取りすべきかをチェック |
| pick_next_task | 次に実行すべきプロセスを選ぶ |
| set_curr_task | スケジューリング・クラスが変更された |
| task_tick | タイマ割込み(tick)の時に呼ばれる |
| task_new | 新しいプロセスが生成された |
linux-5.10.3/kernel/sched/core.c
1561: static inline void enqueue_task(struct rq *rq, struct task_struct *p, int flags)
1562: {
...
1572: p->sched_class->enqueue_task(rq, p, flags);
1573: }
1575: static inline void dequeue_task(struct rq *rq, struct task_struct *p, int flags)
1576: {
...
1586: p->sched_class->dequeue_task(rq, p, flags);
1587: }
5169: static void __setscheduler(struct rq *rq, struct task_struct *p,
5170: const struct sched_attr *attr, bool keep_boost)
5171: {
...
5179: __setscheduler_params(p, attr);
...
5185: p->prio = normal_prio(p);
...
5189: if (dl_prio(p->prio))
5190: p->sched_class = &dl_sched_class;
5191: else if (rt_prio(p->prio))
5192: p->sched_class = &rt_sched_class;
5193: else
5194: p->sched_class = &fair_sched_class;
5195: }
5143: static void __setscheduler_params(struct task_struct *p,
5144: const struct sched_attr *attr)
5145: {
5146: int policy = attr->sched_policy;
5147:
5148: if (policy == SETPARAM_POLICY)
5149: policy = p->policy;
5150:
5151: p->policy = policy;
5152:
5153: if (dl_policy(policy))
5154: __setparam_dl(p, attr);
5155: else if (fair_policy(policy))
5156: p->static_prio = NICE_TO_PRIO(attr->sched_nice);
...
5163: p->rt_priority = attr->sched_priority;
5164: p->normal_prio = normal_prio(p);
5165: set_load_weight(p, true);
5166: }
p->prio
をpolicy に応じて設定する。
p->prio の値に応じて
&dl_sched_class か
&rt_sched_class か
&fair_sched_class のいずれかを指すようにする。
CPUバウンドのプロセスが複数存在した時、ある期間を定めて、この期間の間に、 (優先度を考慮して)公平になるようにCPU資源を割り当てる。この期間の間に、 1度はCPUを割り当てられるようにがんばる。この期間は、 kernel.sched_latency_ns で設定されている。以下の例では、15ミリ秒。
$ sysctl kernel.sched_latency_ns
kernel.sched_latency_ns = 15000000
$
たとえば、もし優先度が同じプロセスAとプロセスBが存在した時には、15ミリ
秒の間にプロセスAに7.5ミリ秒、プロセスBに7.5ミリ秒のCPU資源を割り当てる
ようにがんばる。
Linux CFS は、次の方法でスケジューリングを行なう。

図? runqueueの構造
linux-5.10.3/kernel/sched/core.c
45: DEFINE_PER_CPU_SHARED_ALIGNED(struct rq, runqueues);
linux-5.10.3/kernel/sched/sched.h
895: struct rq {
...
931: struct cfs_rq cfs;
932: struct rt_rq rt;
933: struct dl_rq dl;
...
1051: };
519: struct cfs_rq {
...
531: struct rb_root_cached tasks_timeline;
...
537: struct sched_entity *curr;
...
606: };

図? runqueueの構造(red-black tree)
linux-5.10.3/kernel/sched/fair.c
575: static void __enqueue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se)
576: {
577: struct rb_node **link = &cfs_rq->tasks_timeline.rb_root.rb_node;
578: struct rb_node *parent = NULL;
579: struct sched_entity *entry;
580: bool leftmost = true;
581:
582: /*
583: * Find the right place in the rbtree:
584: */
585: while (*link) {
586: parent = *link;
587: entry = rb_entry(parent, struct sched_entity, run_node);
588: /*
589: * We dont care about collisions. Nodes with
590: * the same key stay together.
591: */
592: if (entity_before(se, entry)) {
593: link = &parent->rb_left;
594: } else {
595: link = &parent->rb_right;
596: leftmost = false;
597: }
598: }
599:
600: rb_link_node(&se->run_node, parent, link);
601: rb_insert_color_cached(&se->run_node,
602: &cfs_rq->tasks_timeline, leftmost);
603: }
604:
605: static void __dequeue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se)
606: {
607: rb_erase_cached(&se->run_node, &cfs_rq->tasks_timeline);
608: }
534: static inline int entity_before(struct sched_entity *a,
535: struct sched_entity *b)
536: {
537: return (s64)(a->vruntime - b->vruntime) < 0;
538: }
&parent->rb_left), 大きければ右(&parent->rb_right) に進む。
cfs_rq->tasks_timeline->rb_leftmost にも保存。
3984: void scheduler_tick(void)
3985: {
3986: int cpu = smp_processor_id();
3987: struct rq *rq = cpu_rq(cpu);
3988: struct task_struct *curr = rq->curr;
...
4000: curr->sched_class->task_tick(rq, curr, 0);
...
4012: }
linux-5.10.3/kernel/sched/fair.c
10693: static void task_tick_fair(struct rq *rq, struct task_struct *curr, int queued)
10694: {
10695: struct cfs_rq *cfs_rq;
10696: struct sched_entity *se = &curr->se;
10697:
10698: for_each_sched_entity(se) {
10699: cfs_rq = cfs_rq_of(se);
10700: entity_tick(cfs_rq, se, queued);
10701: }
...
10708: }
4511: static void
4512: entity_tick(struct cfs_rq *cfs_rq, struct sched_entity *curr, int queued)
4513: {
...
4517: update_curr(cfs_rq);
...
4544: }
842: static void update_curr(struct cfs_rq *cfs_rq)
843: {
844: struct sched_entity *curr = cfs_rq->curr;
845: u64 now = rq_clock_task(rq_of(cfs_rq));
846: u64 delta_exec;
...
851: delta_exec = now - curr->exec_start;
...
855: curr->exec_start = now;
...
860: curr->sum_exec_runtime += delta_exec;
...
863: curr->vruntime += calc_delta_fair(delta_exec, curr);
864: update_min_vruntime(cfs_rq);
...
875: }
671: static inline u64 calc_delta_fair(u64 delta, struct sched_entity *se)
672: {
673: if (unlikely(se->load.weight != NICE_0_LOAD))
674: delta = __calc_delta(delta, NICE_0_LOAD, &se->load);
675:
676: return delta;
677: }
$ cat /proc/sched_debug
Sched Debug Version: v0.09, 2.6.32-431.3.1.el6.x86_64 #1
now at 7955627655.961573 msecs
.jiffies : 12250294951
...
cpu#0, 2100.000 MHz
.nr_running : 1
...
.curr->pid : 30990
...
cfs_rq[0]:/
.exec_clock : 40812852.059736
...
rt_rq[0]:/
.rt_nr_running : 0
...
.nr_running : 1
...
runnable tasks:
task PID tree-key switches prio exec-runtime sum-exec sum-sleep
----------------------------------------------------------------------------------------------------------
R cat 30990 32644150.029656 2 120 32644150.029656 1.072543 0.366310 /
...
cpu#1, 2100.000 MHz
...
cpu#2, 2100.000 MHz
...
cpu#3, 2100.000 MHz
...
$ cat /proc/self/sched
cat (31354, #threads: 1)
---------------------------------------------------------
se.exec_start : 7962193228.073935
se.vruntime : 51856286.476132
se.sum_exec_runtime : 1.211193
...
se.load.weight : 1024
policy : 0
prio : 120
clock-delta : 127
$

図? 4つの要素を持つリスト構造
注意: 正しい二分探索木は、複数存在する。