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 );
}