2014年01月23日
情報科学類 オペレーティングシステム II
筑波大学 システム情報工学研究科
コンピュータサイエンス専攻, 電子・情報工学系
新城 靖
<yas@cs.tsukuba.ac.jp>
このページは、次の URL にあります。
http://www.coins.tsukuba.ac.jp/~yas/coins/os2-2013/2014-01-23
あるいは、次のページから手繰っていくこともできます。
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 22 20:46:12 2014
$ date
Wed Jan 22 20:46:13 JST 2014
$
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);
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);
ネットワーク・プログラムでよく使う。複数の入力を監視する。指定された時
間、入力がなければ、システム・コールから復帰する。
なにもしない時間切れ。
unsigned int sleep(unsigned int seconds); int usleep(useconds_t usec) int nanosleep(const struct timespec *rqtp, struct timespec *rmtp);

図? タイマ関連のハードウェアの基本モデル
2つの機能がある。
その他の割込み
linux-3.12.6/kernel/timer.c 55: u64 jiffies_64 __cacheline_aligned_in_smp = INITIAL_JIFFIES; linux-3.12.6/include/linux/jiffies.h 76: extern u64 __jiffy_data jiffies_64; 77: extern unsigned long volatile __jiffy_data jiffies;
linux-3.12.6/kernel/time/tick-common.c
63: static void tick_periodic(int cpu)
64: {
65: if (tick_do_timer_cpu == cpu) {
...
71: do_timer(1);
...
73: }
74:
75: update_process_times(user_mode(get_irq_regs()));
...
77: }
linux-3.12.6/kernel/timer.c
55: u64 jiffies_64 __cacheline_aligned_in_smp = INITIAL_JIFFIES;
linux-3.12.6/kernel/time/timekeeping.c
1583: void do_timer(unsigned long ticks)
1584: {
1585: jiffies_64 += ticks;
1586: update_wall_time();
...
1588: }
xtime_nsec >> shift でナノ秒を表す。
linux-3.12.6/include/linux/timekeeper_internal.h
14: struct timekeeper {
...
20: u32 shift;
...
32: /* Current CLOCK_REALTIME time in seconds */
33: u64 xtime_sec;
34: /* Clock shifted nano seconds */
35: u64 xtime_nsec;
...
72: };
74: static inline struct timespec tk_xtime(struct timekeeper *tk)
75: {
76: struct timespec ts;
77:
78: ts.tv_sec = tk->xtime_sec;
79: ts.tv_nsec = (long)(tk->xtime_nsec >> tk->shift);
80: return ts;
81: }
linux-3.12.6/kernel/time.c
101: SYSCALL_DEFINE2(gettimeofday, struct timeval __user *, tv,
102: struct timezone __user *, tz)
103: {
104: if (likely(tv != NULL)) {
105: struct timeval ktv;
106: do_gettimeofday(&ktv);
107: if (copy_to_user(tv, &ktv, sizeof(ktv)))
108: return -EFAULT;
109: }
110: if (unlikely(tz != NULL)) {
111: if (copy_to_user(tz, &sys_tz, sizeof(sys_tz)))
112: return -EFAULT;
113: }
114: return 0;
115: }
linux-3.12.6/kernel/time/timekeeping.c
478: void do_gettimeofday(struct timeval *tv)
479: {
480: struct timespec now;
481:
482: getnstimeofday(&now);
483: tv->tv_sec = now.tv_sec;
484: tv->tv_usec = now.tv_nsec/1000;
485: }
331: void getnstimeofday(struct timespec *ts)
332: {
333: WARN_ON(__getnstimeofday(ts));
334: }
298: int __getnstimeofday(struct timespec *ts)
299: {
300: struct timekeeper *tk = &timekeeper;
301: unsigned long seq;
302: s64 nsecs = 0;
...
307: ts->tv_sec = tk->xtime_sec;
308: nsecs = timekeeping_get_ns(tk);
312: ts->tv_nsec = 0;
313: timespec_add_ns(ts, nsecs);
...
321: return 0;
322: }
linux-3.12.6/include/linux/timer.h
12: struct timer_list {
...
17: struct list_head entry;
18: unsigned long expires;
19: struct tvec_base *base;
20:
21: void (*function)(unsigned long);
22: unsigned long data;
...
34: };
jiffies が増加して expires に達すれば、(*function)(data) を呼ぶ。
主に次の関数で操作する。
{
struct timer_list my_timer; // 構造体の宣言
init_timer(&my_timer); // 初期化
my_timer.expires = jiffies + delay; // どのくらい待ちたいか
my_timer.data = (unsigned long)data; // 渡したいデータ
my_timer.function = my_timer_func; // 関数
add_timer(&my_timer); // 登録
}
void my_timer_func(unsigned long data) {
...
}
主に次の関数で操作する。
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,timeout))
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-3.12.6/include/linux/jiffies.h 101: #define time_after(a,b) \ 102: (typecheck(unsigned long, a) && \ 103: typecheck(unsigned long, b) && \ 104: ((long)((b) - (a)) < 0)) 105: #define time_before(a,b) time_after(b,a) 106: 107: #define time_after_eq(a,b) \ 108: (typecheck(unsigned long, a) && \ 109: typecheck(unsigned long, b) && \ 110: ((long)((a) - (b)) >= 0)) 111: #define time_before_eq(a,b) time_after_eq(b,a)
unsigned long delay = jiffies + 2*HZ; // 2秒
while (time_before(jiffies,timeout))
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。優先度を表す値。 |
$ ps -l
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
0 S 1000 28765 28759 0 80 0 - 1363 wait pts/0 00:00:00 bash
0 T 1000 28825 28765 0 80 0 - 1270 signal pts/0 00:00:00 man
0 T 1000 28832 28825 0 80 0 - 1183 signal pts/0 00:00:00 less
0 T 1000 28833 28765 0 80 0 - 7606 signal pts/0 00:00:00 emacs
0 R 1000 28836 28765 0 80 0 - 1216 - pts/0 00:00:00 ps
$ /bin/nice ps -l
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
0 S 1000 28765 28759 0 80 0 - 1363 wait pts/0 00:00:00 bash
0 T 1000 28825 28765 0 80 0 - 1270 signal pts/0 00:00:00 man
0 T 1000 28832 28825 0 80 0 - 1183 signal pts/0 00:00:00 less
0 T 1000 28833 28765 0 80 0 - 7606 signal pts/0 00:00:00 emacs
0 R 1000 28837 28765 0 90 10 - 1216 - pts/0 00:00:00 ps
$ /bin/nice -19 ps -l
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
0 S 1000 28765 28759 0 80 0 - 1363 wait pts/0 00:00:00 bash
0 T 1000 28825 28765 0 80 0 - 1270 signal pts/0 00:00:00 man
0 T 1000 28832 28825 0 80 0 - 1183 signal pts/0 00:00:00 less
0 T 1000 28833 28765 0 80 0 - 7606 signal pts/0 00:00:00 emacs
0 R 1000 28841 28765 0 99 19 - 1216 - pts/0 00:00:00 ps
$
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: }
$ echo $$
3788
$ ./getpriority-pid
Usage: % ./getpriority-pid pid
$ ./getpriority-pid $$
pid==3788, priority==0
$ ./getpriority-pid 0
pid==0, priority==0
$ nice -10 ./getpriority-pid 0
pid==0, priority==10
$ nice -20 ./getpriority-pid 0
pid==0, priority==19
$
# ps -o state,uid,pid,ppid,policy,pri,ni,rtprio,time,comm
S UID PID PPID POL PRI NI RTPRIO TIME COMMAND
S 0 29103 29041 TS 19 0 - 00:00:00 su
S 0 29110 29103 TS 19 0 - 00:00:00 bash
T 0 29226 29110 TS 19 0 - 00:00:00 emacs
T 0 29227 29110 TS 19 0 - 00:00:00 man
T 0 29234 29227 TS 19 0 - 00:00:00 less
R 0 29247 29110 TS 19 0 - 00:00:00 ps
# /bin/nice --10 ps -o state,uid,pid,ppid,policy,pri,ni,rtprio,time,comm
S UID PID PPID POL PRI NI RTPRIO TIME COMMAND
S 0 29103 29041 TS 19 0 - 00:00:00 su
S 0 29110 29103 TS 19 0 - 00:00:00 bash
T 0 29226 29110 TS 19 0 - 00:00:00 emacs
T 0 29227 29110 TS 19 0 - 00:00:00 man
T 0 29234 29227 TS 19 0 - 00:00:00 less
R 0 29248 29110 TS 29 -10 - 00:00:00 ps
# chrt 50 ps -o state,uid,pid,ppid,policy,pri,ni,rtprio,time,comm
S UID PID PPID POL PRI NI RTPRIO TIME COMMAND
S 0 29103 29041 TS 19 0 - 00:00:00 su
S 0 29110 29103 TS 19 0 - 00:00:00 bash
T 0 29226 29110 TS 19 0 - 00:00:00 emacs
T 0 29227 29110 TS 19 0 - 00:00:00 man
T 0 29234 29227 TS 19 0 - 00:00:00 less
R 0 29249 29110 RR 90 - 50 00:00:00 ps
#
"-" の表示は、実時間のプロセスではない。
linux-3.12.6/include/linux/sched.h
1023: struct task_struct {
1024: volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */
...
1039: int prio, static_prio, normal_prio;
1040: unsigned int rt_priority;
1041: const struct sched_class *sched_class;
1042: struct sched_entity se;
1043: struct sched_rt_entity rt;
...
1066: unsigned int policy;
...
1414: };
966: struct sched_entity {
...
968: struct rb_node run_node;
...
970: unsigned int on_rq;
...
974: u64 vruntime;
...
995: };
struct task_struct の中に、prio 等のフィールドやstruct sched_entity が
ある。
linux-3.12.6/include/uapi/linux/sched.h 36: #define SCHED_NORMAL 0 37: #define SCHED_FIFO 1 38: #define SCHED_RR 2 39: #define SCHED_BATCH 3 40: /* SCHED_ISO: reserved but not implemented yet */ 41: #define SCHED_IDLE 5
linux-3.12.6/kernel/sys.c
227: /*
228: * Ugh. To avoid negative return values, "getpriority()" will
229: * not return the normal nice-value, but a negated value that
230: * has been offset by 20 (ie it returns 40..1 instead of -20..19)
231: * to stay compatible.
232: */
233: SYSCALL_DEFINE2(getpriority, int, which, int, who)
234: {
235: struct task_struct *g, *p;
236: struct user_struct *user;
237: const struct cred *cred = current_cred();
238: long niceval, retval = -ESRCH;
239: struct pid *pgrp;
240: kuid_t uid;
241:
242: if (which > PRIO_USER || which < PRIO_PROCESS)
243: return -EINVAL;
...
247: switch (which) {
248: case PRIO_PROCESS:
249: if (who)
250: p = find_task_by_vpid(who);
251: else
252: p = current;
253: if (p) {
254: niceval = 20 - task_nice(p);
255: if (niceval > retval)
256: retval = niceval;
257: }
258: break;
259: case PRIO_PGRP:
...
259: case PRIO_PGRP:
...
270: case PRIO_USER:
...
289: }
...
294: return retval;
295: }
linux-3.12.6/include/linux/sched/rt.h
17: #define MAX_USER_RT_PRIO 100
18: #define MAX_RT_PRIO MAX_USER_RT_PRIO
19:
20: #define MAX_PRIO (MAX_RT_PRIO + 40)
21: #define DEFAULT_PRIO (MAX_RT_PRIO + 20)
linux-3.12.6/kernel/sched/sched.h
28: #define NICE_TO_PRIO(nice) (MAX_RT_PRIO + (nice) + 20)
29: #define PRIO_TO_NICE(prio) ((prio) - MAX_RT_PRIO - 20)
30: #define TASK_NICE(p) PRIO_TO_NICE((p)->static_prio)
linux-3.12.6/kernel/sched/core.c
3200: int task_nice(const struct task_struct *p)
3201: {
3202: return TASK_NICE(p);
3203: }
glibc-2.5/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-3.12.6/kernel/sched/sched.h
912: /*
913: * Nice levels are multiplicative, with a gentle 10% change for every
914: * nice level changed. I.e. when a CPU-bound task goes from nice 0 to
915: * nice 1, it will get ~10% less CPU time than another CPU-bound task
916: * that remained on nice 0.
917: *
918: * The "10% effect" is relative and cumulative: from _any_ nice level,
919: * if you go up 1 level, it's -10% CPU usage, if you go down 1 level
920: * it's +10% CPU usage. (to achieve that we use a multiplier of 1.25.
921: * If a task goes up by ~10% and another task goes down by ~10% then
922: * the relative distance between them is ~25%.)
923: */
924: static const int prio_to_weight[40] = {
925: /* -20 */ 88761, 71755, 56483, 46273, 36291,
926: /* -15 */ 29154, 23254, 18705, 14949, 11916,
927: /* -10 */ 9548, 7620, 6100, 4904, 3906,
928: /* -5 */ 3121, 2501, 1991, 1586, 1277,
929: /* 0 */ 1024, 820, 655, 526, 423,
930: /* 5 */ 335, 272, 215, 172, 137,
931: /* 10 */ 110, 87, 70, 56, 45,
932: /* 15 */ 36, 29, 23, 18, 15,
933: };
linux-3.12.6/kernel/sched/core.c
749: static void set_load_weight(struct task_struct *p)
750: {
751: int prio = p->static_prio - MAX_RT_PRIO;
752: struct load_weight *load = &p->se.load;
...
763: load->weight = scale_load(prio_to_weight[prio]);
764: load->inv_weight = prio_to_wmult[prio];
765: }
linux-3.12.6/kernel/sched/sched.h
64: # 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-3.12.6/kernel/sched/core.c
767: static void enqueue_task(struct rq *rq, struct task_struct *p, int flags)
768: {
769: update_rq_clock(rq);
770: sched_info_queued(p);
771: p->sched_class->enqueue_task(rq, p, flags);
772: }
773:
774: static void dequeue_task(struct rq *rq, struct task_struct *p, int flags)
775: {
776: update_rq_clock(rq);
777: sched_info_dequeued(p);
778: p->sched_class->dequeue_task(rq, p, flags);
779: }
linux-3.12.6/kernel/sched/core.c
3253: static void
3254: __setscheduler(struct rq *rq, struct task_struct *p, int policy, int prio)
3255: {
3256: p->policy = policy;
3257: p->rt_priority = prio;
3258: p->normal_prio = normal_prio(p);
3259: /* we are holding p->pi_lock already */
3260: p->prio = rt_mutex_getprio(p);
3261: if (rt_prio(p->prio))
3262: p->sched_class = &rt_sched_class;
3263: else
3264: p->sched_class = &fair_sched_class;
3265: set_load_weight(p);
3266: }
linux-3.12.6/include/linux/sched/rt.h
23: static inline int rt_prio(int prio)
24: {
25: if (unlikely(prio < MAX_RT_PRIO))
26: return 1;
27: return 0;
28: }
p->prio の値に応じて
&rt_sched_class か
&fair_sched_class のどちらかを指す。
Linux CFS は、次の方法でスケジューリングを行なう。

図? runqueueの構造
linux-3.12.6/kernel/sched/sched.h
402: struct rq {
...
428: struct cfs_rq cfs;
429: struct rt_rq rt;
...
526: };
249: struct cfs_rq {
...
259: struct rb_root tasks_timeline;
260: struct rb_node *rb_leftmost;
...
327: };
linux-3.12.6/kernel/sched/core.c
113: DEFINE_PER_CPU_SHARED_ALIGNED(struct rq, runqueues);

図? runqueueの構造(red-black tree)
linux-3.12.6/kernel/sched/fair.c
505: static void __enqueue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se)
506: {
507: struct rb_node **link = &cfs_rq->tasks_timeline.rb_node;
508: struct rb_node *parent = NULL;
509: struct sched_entity *entry;
510: int leftmost = 1;
511:
512: /*
513: * Find the right place in the rbtree:
514: */
515: while (*link) {
516: parent = *link;
517: entry = rb_entry(parent, struct sched_entity, run_node);
518: /*
519: * We dont care about collisions. Nodes with
520: * the same key stay together.
521: */
522: if (entity_before(se, entry)) {
523: link = &parent->rb_left;
524: } else {
525: link = &parent->rb_right;
526: leftmost = 0;
527: }
528: }
529:
530: /*
531: * Maintain a cache of leftmost tree entries (it is frequently
532: * used):
533: */
534: if (leftmost)
535: cfs_rq->rb_leftmost = &se->run_node;
536:
537: rb_link_node(&se->run_node, parent, link);
538: rb_insert_color(&se->run_node, &cfs_rq->tasks_timeline);
539: }
470: static inline int entity_before(struct sched_entity *a,
471: struct sched_entity *b)
472: {
473: return (s64)(a->vruntime - b->vruntime) < 0;
474: }
&parent->rb_left), 大きければ右(&parent->rb_right) に進む。
cfs_rq->rb_leftmost にも保存。
linux-3.12.6/kernel/sched/core.c
2154: void scheduler_tick(void)
2155: {
2156: int cpu = smp_processor_id();
2157: struct rq *rq = cpu_rq(cpu);
2158: struct task_struct *curr = rq->curr;
...
2164: curr->sched_class->task_tick(rq, curr, 0);
...
2175: }
linux-3.12.6/kernel/sched/fair.c
5898: static void task_tick_fair(struct rq *rq, struct task_struct *curr, int queued)
5899: {
5900: struct cfs_rq *cfs_rq;
5901: struct sched_entity *se = &curr->se;
5902:
5903: for_each_sched_entity(se) {
5904: cfs_rq = cfs_rq_of(se);
5905: entity_tick(cfs_rq, se, queued);
5906: }
...
5912: }
2022: static void
2023: entity_tick(struct cfs_rq *cfs_rq, struct sched_entity *curr, int queued)
2024: {
...
2028: update_curr(cfs_rq);
...
2054: if (cfs_rq->nr_running > 1)
2055: check_preempt_tick(cfs_rq, curr);
2056: }
724: static void update_curr(struct cfs_rq *cfs_rq)
725: {
726: struct sched_entity *curr = cfs_rq->curr;
727: u64 now = rq_clock_task(rq_of(cfs_rq));
728: unsigned long delta_exec;
...
738: delta_exec = (unsigned long)(now - curr->exec_start);
...
742: __update_curr(cfs_rq, curr, delta_exec);
743: curr->exec_start = now;
...
754: }
707: static inline void
708: __update_curr(struct cfs_rq *cfs_rq, struct sched_entity *curr,
709: unsigned long delta_exec)
710: {
711: unsigned long delta_exec_weighted;
...
718: delta_exec_weighted = calc_delta_fair(delta_exec, curr);
719:
720: curr->vruntime += delta_exec_weighted;
...
722: }
void h(int a,int b, int c) {
....
}
これを実現するために、どのようなコードを書けばよいか。以下の空欄を埋め
なさい。
struct timer_list my_timer;
int my_arg_a,my_arg_b,my_arg_c;
void f(unsigned long data) {
init_timer( /*空欄(a)*/ );
my_timer.expires = /*空欄(b)*/;
my_timer.data = 0;
my_timer.function = /*空欄(c)*/;
/*空欄(d)*/;
}
void my_timer_func(unsigned long data) {
h( my_arg_a,my_arg_b,my_arg_c );
}

図? 4つの要素を持つリスト構造
このリストを表現した二分探索木を1つ作り、節と枝(矢印)を用いて図示し なさい。ただし、木はバランスをしていなくても良いものとする。注意: 正しい二分探索木は、複数存在する。