2012年02月21日 情報科学類 オペレーティングシステム II 筑波大学 システム情報工学研究科 コンピュータサイエンス専攻, 電子・情報工学系 新城 靖 <yas@is.tsukuba.ac.jp>
このページは、次の URL にあります。
http://www.coins.tsukuba.ac.jp/~yas/coins/os2-2011/2012-02-21
あるいは、次のページから手繰っていくこともできます。
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.1.3/kernel/timer.c 53: u64 jiffies_64 __cacheline_aligned_in_smp = INITIAL_JIFFIES; linux-3.1.3/include/linux/jiffies.h 81: extern u64 __jiffy_data jiffies_64; 82: extern unsigned long volatile __jiffy_data jiffies;
linux-3.1.3/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.1.3/kernel/timer.c 1286: void update_process_times(int user_tick) 1287: { 1288: struct task_struct *p = current; 1289: int cpu = smp_processor_id(); ... 1292: account_process_tick(p, user_tick); 1293: run_local_timers(); ... 1300: scheduler_tick(); 1301: run_posix_cpu_timers(p); 1302: } 1320: void run_local_timers(void) 1321: { 1322: hrtimer_run_queues(); 1323: raise_softirq(TIMER_SOFTIRQ); 1324: }
linux-3.1.3/kernel/timer.c 53: u64 jiffies_64 __cacheline_aligned_in_smp = INITIAL_JIFFIES; linux-3.1.3/kernel/time/timekeeping.c 1102: void do_timer(unsigned long ticks) 1103: { 1104: jiffies_64 += ticks; 1105: update_wall_time(); ... 1107: }
linux-3.1.3/kernel/time/timekeeping.c 160: static struct timespec xtime __attribute__ ((aligned (16)));
linux-3.1.3/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.1.3/kernel/time/timekeeping.c 340: void do_gettimeofday(struct timeval *tv) 341: { 342: struct timespec now; 343: 344: getnstimeofday(&now); 345: tv->tv_sec = now.tv_sec; 346: tv->tv_usec = now.tv_nsec/1000; 347: } 217: void getnstimeofday(struct timespec *ts) 218: { ... 227: *ts = xtime; 228: nsecs = timekeeping_get_ns(); 231: nsecs += arch_gettimeoffset(); ... 235: timespec_add_ns(ts, nsecs); 236: }
linux-3.1.3/kernel/time/timekeeping.c 885: static void update_wall_time(void) 886: { .... 953: xtime.tv_nsec = ((s64) timekeeper.xtime_nsec >> timekeeper.shift) + 1; ... 962: if (unlikely(xtime.tv_nsec >= NSEC_PER_SEC)) { 963: xtime.tv_nsec -= NSEC_PER_SEC; 964: xtime.tv_sec++; ... 966: }
linux-3.1.3/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.1.3/kernel/sched.c 3964: void account_process_tick(struct task_struct *p, int user_tick) 3965: { 3966: cputime_t one_jiffy_scaled = cputime_to_scaled(cputime_one_jiffy); 3967: struct rq *rq = this_rq(); ... 3977: if (user_tick) 3978: account_user_time(p, cputime_one_jiffy, one_jiffy_scaled); 3979: else if ((p != rq->idle) || (irq_count() != HARDIRQ_OFFSET)) 3980: account_system_time(p, HARDIRQ_OFFSET, cputime_one_jiffy, 3981: one_jiffy_scaled); 3982: else 3983: account_idle_time(cputime_one_jiffy); 3984: } 3733: void account_user_time(struct task_struct *p, cputime_t cputime, 3734: cputime_t cputime_scaled) 3735: { ... 3740: p->utime = cputime_add(p->utime, cputime); 3741: p->utimescaled = cputime_add(p->utimescaled, cputime_scaled); 3742: account_group_user_time(p, cputime); ... 3754: }
例: 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.1.3/include/linux/jiffies.h 106: #define time_after(a,b) \ 107: (typecheck(unsigned long, a) && \ 108: typecheck(unsigned long, b) && \ 109: ((long)(b) - (long)(a) < 0)) 110: #define time_before(a,b) time_after(b,a) 111: 112: #define time_after_eq(a,b) \ 113: (typecheck(unsigned long, a) && \ 114: typecheck(unsigned long, b) && \ 115: ((long)(a) - (long)(b) >= 0)) 116: #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 ); }