2013年02月12日
情報科学類 オペレーティングシステム II
筑波大学 システム情報工学研究科
コンピュータサイエンス専攻, 電子・情報工学系
新城 靖
<yas@cs.tsukuba.ac.jp>
このページは、次の URL にあります。
http://www.coins.tsukuba.ac.jp/~yas/coins/os2-2012/2013-02-12
あるいは、次のページから手繰っていくこともできます。
http://www.coins.tsukuba.ac.jp/~yas/
http://www.cs.tsukuba.ac.jp/~yas/
試験について
struct timeval {
long tv_sec; /* seconds since Jan. 1, 1970 */
long tv_usec; /* and microseconds */
};
int gettimeofday(struct timeval *tp, struct timezone *tzp);
int settimeofday(const struct timeval *tp, const struct timezone *tzp);
使い方
struct timeval tv;
gettimeofday(&tv, NULL);
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);
カレンダ時刻は、変更できる。逆走させることも可能。
順方向のジャンプや逆走を避けて、カレンダ時刻を合わせるには、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.6.8/kernel/timer.c 53: u64 jiffies_64 __cacheline_aligned_in_smp = INITIAL_JIFFIES; linux-3.6.8/include/linux/jiffies.h 90: extern u64 __jiffy_data jiffies_64; 91: extern unsigned long volatile __jiffy_data jiffies;
linux-3.6.8/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: }
...
75: update_process_times(user_mode(get_irq_regs()));
...
77: }
linux-3.6.8/kernel/timer.c
1359: void update_process_times(int user_tick)
1360: {
1361: struct task_struct *p = current;
1362: int cpu = smp_processor_id();
...
1365: account_process_tick(p, user_tick);
...
1373: scheduler_tick();
...
1375: }
linux-3.6.8/kernel/timer.c
53: u64 jiffies_64 __cacheline_aligned_in_smp = INITIAL_JIFFIES;
linux-3.6.8/kernel/time/timekeeping.c
1353: void do_timer(unsigned long ticks)
1354: {
1355: jiffies_64 += ticks;
1356: update_wall_time();
...
1358: }
xtime_nsec >> shift でナノ秒を表す。
linux-3.6.8/kernel/time/timekeeping.c
25: struct timekeeper {
...
30: /* The shift value of the current clocksource. */
31: u32 shift;
...
41: /* Current CLOCK_REALTIME time in seconds */
42: u64 xtime_sec;
43: /* Clock shifted nano seconds */
44: u64 xtime_nsec;
...
78: };
79:
80: static struct timekeeper timekeeper;
99: static struct timespec tk_xtime(struct timekeeper *tk)
100: {
101: struct timespec ts;
102:
103: ts.tv_sec = tk->xtime_sec;
104: ts.tv_nsec = (long)(tk->xtime_nsec >> tk->shift);
105: return ts;
106: }
linux-3.6.8/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.6.8/kernel/time/timekeeping.c
412: void do_gettimeofday(struct timeval *tv)
413: {
414: struct timespec now;
415:
416: getnstimeofday(&now);
417: tv->tv_sec = now.tv_sec;
418: tv->tv_usec = now.tv_nsec/1000;
419: }
294: void getnstimeofday(struct timespec *ts)
295: {
296: struct timekeeper *tk = &timekeeper;
297: unsigned long seq;
298: s64 nsecs = 0;
...
305: ts->tv_sec = tk->xtime_sec;
306: nsecs = timekeeping_get_ns(tk);
...
310: ts->tv_nsec = 0;
311: timespec_add_ns(ts, nsecs);
312: }
linux-3.6.8/kernel/time/timekeeping.c
1135: static void update_wall_time(void)
1136: {
1137: struct clocksource *clock;
1138: struct timekeeper *tk = &timekeeper;
...
1194: remainder = tk->xtime_nsec & ((1ULL << tk->shift) - 1);
1195: tk->xtime_nsec -= remainder;
1196: tk->xtime_nsec += 1ULL << tk->shift;
1197: tk->ntp_error += remainder << tk->ntp_error_shift;
...
1203: accumulate_nsecs_to_secs(tk);
...
1210: }
tk->xtime_nsec を
1 << tk->shift
だけ増やす。
1 << tk->shift以下の部分は、tk->ntp_error に集めておく。
tk->xtime_nsec が秒を超えたら
accumulate_nsecs_to_secs() で補正する。
linux-3.6.8/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;
}
linux-3.6.8/kernel/sched/core.c
3214: void scheduler_tick(void)
3215: {
3216: int cpu = smp_processor_id();
3217: struct rq *rq = cpu_rq(cpu);
3218: struct task_struct *curr = rq->curr;
...
3225: curr->sched_class->task_tick(rq, curr, 0);
...
3234: }
linux-3.6.8/kernel/sched/fair.c
4981: static void task_tick_fair(struct rq *rq, struct task_struct *curr, int queued)
4982: {
4983: struct cfs_rq *cfs_rq;
4984: struct sched_entity *se = &curr->se;
4985:
4986: for_each_sched_entity(se) {
4987: cfs_rq = cfs_rq_of(se);
4988: entity_tick(cfs_rq, se, queued);
4989: }
4990: }
1347: static void
1348: entity_tick(struct cfs_rq *cfs_rq, struct sched_entity *curr, int queued)
1349: {
...
1353: update_curr(cfs_rq);
...
1377: if (cfs_rq->nr_running > 1)
1378: check_preempt_tick(cfs_rq, curr);
1379: }
684: static void update_curr(struct cfs_rq *cfs_rq)
685: {
686: struct sched_entity *curr = cfs_rq->curr;
687: u64 now = rq_of(cfs_rq)->clock_task;
688: unsigned long delta_exec;
...
698: delta_exec = (unsigned long)(now - curr->exec_start);
...
702: __update_curr(cfs_rq, curr, delta_exec);
703: curr->exec_start = now;
...
714: }
663: static inline void
664: __update_curr(struct cfs_rq *cfs_rq, struct sched_entity *curr,
665: unsigned long delta_exec)
666: {
667: unsigned long delta_exec_weighted;
...
674: delta_exec_weighted = calc_delta_fair(delta_exec, curr);
...
676: curr->vruntime += delta_exec_weighted;
...
682: }
linux-3.6.8/kernel/sched/core.c
3071: void account_process_tick(struct task_struct *p, int user_tick)
3072: {
3073: cputime_t one_jiffy_scaled = cputime_to_scaled(cputime_one_jiffy);
3074: struct rq *rq = this_rq();
...
3084: if (user_tick)
3085: account_user_time(p, cputime_one_jiffy, one_jiffy_scaled);
3086: else if ((p != rq->idle) || (irq_count() != HARDIRQ_OFFSET))
3087: account_system_time(p, HARDIRQ_OFFSET, cputime_one_jiffy,
3088: one_jiffy_scaled);
3089: else
3090: account_idle_time(cputime_one_jiffy);
3091: }
2854: void account_user_time(struct task_struct *p, cputime_t cputime,
2855: cputime_t cputime_scaled)
2856: {
...
2860: p->utime += cputime;
2861: p->utimescaled += cputime_scaled;
...
2871: }
例: 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.6.8/include/linux/jiffies.h 115: #define time_after(a,b) \ 116: (typecheck(unsigned long, a) && \ 117: typecheck(unsigned long, b) && \ 118: ((long)(b) - (long)(a) < 0)) 119: #define time_before(a,b) time_after(b,a) 120: 121: #define time_after_eq(a,b) \ 122: (typecheck(unsigned long, a) && \ 123: typecheck(unsigned long, b) && \ 124: ((long)(a) - (long)(b) >= 0)) 125: #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 が使われている。
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 );
}