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型进程的影响不是太大。