Why IO bond process increase cpu load
Author: Tony
Date:2013-01-11
About /proc/loadavg
下面是从网上流行的解释:
前三个数字表示的是1、5、15分钟内的CPU平均处于运行状态的进程个数,后面的两个,一个的分子是正在运行的进程数,分母是进程总数;另一个是最近运行的进程PID
Copy file will
increase cpu load
如果在linux系统上运行大文件的copy操作或者大量的读写磁盘操作,系统负载会显著增大。Why?
进行磁盘操作的时候,应该是IO操作,CPU大部分时间应该是等待IO操作完成,不应该占用CPU时间的,为什么load会增大呢?难道linux内核在等待IO的时候,仍然霸占CPU,不能被其他的进程使用?(内核应该不会这么傻,呵呵)
If process can’t get data from disk immediately,what to do?
当进程等待IO数据的时候,最终会到block层的get_request_wait(或类似的函数)
看一下这个函数的实现:(/block/blk-core.c)
-
static struct request *get_request_wait(struct request_queue *q, int rw_flags,
-
-
struct bio *bio)
-
-
{
-
-
const bool is_sync = rw_is_sync(rw_flags) != 0;
-
-
struct request *rq;
-
-
rq = get_request(q, rw_flags, bio, GFP_NOIO);
-
-
while (!rq) {
-
-
DEFINE_WAIT(wait);
-
-
struct io_context *ioc;
-
-
struct request_list *rl = &q->rq;
-
-
/*修改当前进程的状态为TASK_UNINTERRUPTIBLE*/
-
-
prepare_to_wait_exclusive(&rl->wait[is_sync], &wait,
-
-
TASK_UNINTERRUPTIBLE);
-
-
trace_block_sleeprq(q, bio, rw_flags & 1);
-
-
__generic_unplug_device(q);
-
-
spin_unlock_irq(q->queue_lock);
-
-
/*调用函数进行调度*/
-
-
io_schedule();
-
-
/*
-
-
* After sleeping, we become a "batching" process and
-
-
* will be able to allocate at least one request, and
-
-
* up to a big batch of them for a small period time.
-
-
* See ioc_batching, ioc_set_batching
-
-
*/
-
-
ioc = current_io_context(GFP_NOIO, q->node);
-
-
ioc_set_batching(q, ioc);
-
-
spin_lock_irq(q->queue_lock);
-
-
finish_wait(&rl->wait[is_sync], &wait);
-
-
rq = get_request(q, rw_flags, bio, GFP_NOIO);
-
-
};
-
-
return rq;
-
-
}
函数调用了io_schedule(有些会调用io_schedule_timeout,这个函数的不同是,至少sleep多长时间)。我们看一下io_schedule的代码:(kernel/sched.c)
-
/*
-
-
* This task is about to go to sleep on IO. Increment rq->nr_iowait so
-
-
* that process accounting knows that this is a task in IO wait state.
-
-
*/
-
-
void __sched io_schedule(void)
-
-
{
-
-
struct rq *rq = raw_rq();
-
-
delayacct_blkio_start();
-
-
atomic_inc(&rq->nr_iowait);
-
-
current->in_iowait = 1;
-
-
/*调用schedule函数,让出的CPU*/
-
-
schedule();
-
-
current->in_iowait = 0;
-
-
/*增加当前CPU的等待IO的进程数*/
-
-
atomic_dec(&rq->nr_iowait);
-
-
delayacct_blkio_end();
-
-
}
从这个函数可以看出,当进程IO操作的时候,会从当前CPU的running队列移除,并让出CPU。那按照网上的解释,不应该增加load值啊。看来需要看看load的真正含义了。
How the kernel calculate load?
/proc/loadavg对应的代码是fs/proc/loadavg.c
-
static int loadavg_proc_show(struct seq_file *m, void *v)
-
-
{
-
-
unsigned long avnrun[3];
-
-
get_avenrun(avnrun, FIXED_1/200, 0);
-
-
seq_printf(m, "%lu.%02lu %lu.%02lu %lu.%02lu %ld/%d %d\n",
-
-
LOAD_INT(avnrun[0]), LOAD_FRAC(avnrun[0]),
-
-
LOAD_INT(avnrun[1]), LOAD_FRAC(avnrun[1]),
-
-
LOAD_INT(avnrun[2]), LOAD_FRAC(avnrun[2]),
-
-
nr_running(), nr_threads,
-
-
task_active_pid_ns(current)->last_pid);
-
-
return 0;
-
-
}
这里通过get_avenrun函数获取(kernel/sched.c)
-
void get_avenrun(unsigned long *loads, unsigned long offset, int shift)
-
-
{
-
-
loads[0] = (avenrun[0] + offset) << shift;
-
-
loads[1] = (avenrun[1] + offset) << shift;
-
-
loads[2] = (avenrun[2] + offset) << shift;
-
-
}
这里实际读取的全局变量avenrun。Avenrun的更新在函数calc_global_load
-
void calc_global_load(void)
-
-
{
-
-
unsigned long upd = calc_load_update + 10;
-
-
long active;
-
-
if (time_before(jiffies, upd))
-
-
return;
-
-
active = atomic_long_read(&calc_load_tasks);
-
-
active = active > 0 ? active * FIXED_1 : 0;
-
-
avenrun[0] = calc_load(avenrun[0], EXP_1, active);
-
-
avenrun[1] = calc_load(avenrun[1], EXP_5, active);
-
-
avenrun[2] = calc_load(avenrun[2], EXP_15, active);
-
-
calc_load_update += LOAD_FREQ;
-
-
}
这里实际读取的calc_load_tasks变量,这个变量在函数calc_load_account_active中被更新。(这个函数可能在每个tick的时候通过update_cpu_load调用,也可能在cpu选择idle进程的时候调用)
-
static void calc_load_account_active(struct rq *this_rq)
-
-
{
-
-
long nr_active, delta;
-
-
nr_active = this_rq->nr_running;
-
-
/*这里不仅包含的running的进程,也包含了uninterruptible的进程*/
-
-
nr_active += (long) this_rq->nr_uninterruptible;
-
-
if (nr_active != this_rq->calc_load_active) {
-
-
delta = nr_active - this_rq->calc_load_active;
-
-
this_rq->calc_load_active = nr_active;
-
-
atomic_long_add(delta, &calc_load_tasks);
-
-
}
-
-
}
Conclusion
内核统计load的含义的是,所有cpu上的处于running状态和uninterruptible状态的进程,由于IO操作的时候,进程会进入uninterruptible状态,所有会增大系统的负载。这个时候负载虽然高,但是CPU仍然是比较空闲的,也就说IO导致的负载过高,对cpu bound型进程的影响不是太大。
阅读(4894) | 评论(0) | 转发(0) |