loadavg_read_proc

ロードアベレージの算出方法

  • runqueue
  • TASK_RUNNING, TASK_UNINTERRUPTIBLE
  • procfs
  • タイマ割り込み
    • ticks , HZ
  63 /*
  64  * These are the constant used to fake the fixed-point load-average
  65  * counting. Some notes:
  // faction .. 分数、割合
  66  *  - 11 bit fractions expand to 22 bits by the multiplies: this gives
  67  *    a load-average precision of 10 bits integer + 11 bits fractional
  68  *  - if you want to count load-averages more often, you need more
  69  *    precision, or rounding will get you. With 2-second counting freq,
  70  *    the EXP_n values would be 1981, 2034 and 2043 if still using only
  71  *    11 bit fractions.
  72  */
  73 extern unsigned long avenrun[];         /* Load averages */
  74 
  75 #define FSHIFT          11              /* nr of bits of precision */
  76 #define FIXED_1         (1<<FSHIFT)     /* 1.0 as fixed-point */
  77 #define LOAD_FREQ       (5*HZ)          /* 5 sec intervals */
  78 #define EXP_1           1884            /* 1/exp(5sec/1min) as fixed-point */
  79 #define EXP_5           2014            /* 1/exp(5sec/5min) */
  80 #define EXP_15          2037            /* 1/exp(5sec/15min) */
  81 
  82 #define CALC_LOAD(load,exp,n) \
  83         load *= exp; \
  84         load += n*(FIXED_1-exp); \
  85         load >>= FSHIFT;


// FSHIFT ... 11?
/* #define LOAD_INT(x) ((x) >> FSHIFT) */
/* #define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100) */

static int loadavg_read_proc(char *page, char **start, off_t off,
				 int count, int *eof, void *data)
{
	int a, b, c;
	int len;
        // avenrun ... kernel/timer.cのグローバル変数
	a = avenrun[0] + (FIXED_1/200);
	b = avenrun[1] + (FIXED_1/200);
	c = avenrun[2] + (FIXED_1/200);
	len = sprintf(page,"%d.%02d %d.%02d %d.%02d %ld/%d %d\n",
		LOAD_INT(a), LOAD_FRAC(a),
		LOAD_INT(b), LOAD_FRAC(b),
		LOAD_INT(c), LOAD_FRAC(c),
                      // TASK_RUNNINGの数 .. kernel/sched.c
                      // スレッドの数
                      // 最後のプロセスのpid
		nr_running(), nr_threads, last_pid);
	return proc_calc_metrics(page, start, off, count, eof, len);
}

/*
 * nr_running, nr_uninterruptible and nr_context_switches:
 *
 * externally visible scheduler statistics: current number of runnable
 * threads, current number of uninterruptible-sleeping threads, total
 * number of context switches performed since bootup.
 */

// TASK_RUNNING と TASK_UNINTERRUPTIBLE
unsigned long nr_running(void)
{
	unsigned long i, sum = 0;

        // CPUごとのrunqueueを見て、nr_runningを足す
	for_each_online_cpu(i)
          sum += cpu_rq(i)->nr_running; // TASK_RUNNING

	return sum;
}

unsigned long nr_uninterruptible(void)
{
	unsigned long i, sum = 0;

        // CPUごとのrunqueue, TASK_UNINTERRUPTIBLEの数を総計する
	for_each_cpu(i)
          sum += cpu_rq(i)->nr_uninterruptible; // TASK_UNINTERRUPTIBLE

	return sum;
}

/*
 * Nr of active tasks - counted in fixed-point numbers
 */
static unsigned long count_active_tasks(void)
{
    // 全てのCPUのTASK_RUNNING + TASK_UNINTERRUPTIBLE
	return (nr_running() + nr_uninterruptible()) * FIXED_1;
}

/*
 * calc_load - given tick count, update the avenrun load estimates.
 * This is called while holding a write_lock on xtime_lock.
 */
static inline void calc_load(unsigned long ticks)
{
	unsigned long active_tasks; /* fixed-point */
	static int count = LOAD_FREQ;

	count -= ticks;
	if (count < 0) {
		count += LOAD_FREQ;
		active_tasks = count_active_tasks(); // TASK_RUNNING + TASK_UNINTERRUPTIBLE
		CALC_LOAD(avenrun[0], EXP_1, active_tasks);
		CALC_LOAD(avenrun[1], EXP_5, active_tasks);
		CALC_LOAD(avenrun[2], EXP_15, active_tasks);
	}
}

/*
 * Called by the timer interrupt. xtime_lock must already be taken
 * by the timer IRQ!
 */
// タイマ割り込み 1/1000 とか 1/100 秒とかで
static inline void update_times(void)
{
	unsigned long ticks;

	ticks = jiffies - wall_jiffies;
	if (ticks) {
		wall_jiffies += ticks;
		update_wall_time(ticks);
	}
	// ここで計算
	calc_load(ticks);
}