Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3934568
  • 博文数量: 93
  • 博客积分: 3189
  • 博客等级: 中校
  • 技术积分: 4229
  • 用 户 组: 普通用户
  • 注册时间: 2009-02-02 13:29
个人简介

出没于杭州和青岛的程序猿一枚,对内核略懂一二

文章分类

全部博文(93)

文章存档

2016年(2)

2015年(3)

2014年(11)

2013年(29)

2012年(16)

2011年(5)

2010年(5)

2009年(22)

分类: LINUX

2013-01-11 17:11:47

Why IO bond process increase cpu load

Author: Tony  

Date:2013-01-11

About  /proc/loadavg

下面是从网上流行的解释:

前三个数字表示的是1515分钟内的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 immediatelywhat to do?

当进程等待IO数据的时候,最终会到block层的get_request_wait(或类似的函数)

看一下这个函数的实现:(/block/blk-core.c)


点击(此处)折叠或打开

  1. static struct request *get_request_wait(struct request_queue *q, int rw_flags,

  2.                                                struct bio *bio)

  3. {

  4.          const bool is_sync = rw_is_sync(rw_flags) != 0;

  5.          struct request *rq;

  6.          rq = get_request(q, rw_flags, bio, GFP_NOIO);

  7.          while (!rq) {

  8.                    DEFINE_WAIT(wait);

  9.                    struct io_context *ioc;

  10.                    struct request_list *rl = &q->rq;

  11.                    /*修改当前进程的状态为TASK_UNINTERRUPTIBLE*/

  12.                    prepare_to_wait_exclusive(&rl->wait[is_sync], &wait,

  13.                                      TASK_UNINTERRUPTIBLE);

  14.                    trace_block_sleeprq(q, bio, rw_flags & 1);

  15.                    __generic_unplug_device(q);

  16.                    spin_unlock_irq(q->queue_lock);

  17.                   /*调用函数进行调度*/

  18.                    io_schedule();

  19.                    /*

  20.                     * After sleeping, we become a "batching" process and

  21.                     * will be able to allocate at least one request, and

  22.                     * up to a big batch of them for a small period time.

  23.                     * See ioc_batching, ioc_set_batching

  24.                     */

  25.                    ioc = current_io_context(GFP_NOIO, q->node);

  26.                    ioc_set_batching(q, ioc);

  27.                    spin_lock_irq(q->queue_lock);

  28.                    finish_wait(&rl->wait[is_sync], &wait);

  29.                    rq = get_request(q, rw_flags, bio, GFP_NOIO);

  30.          };

  31.          return rq;

  32. }


函数调用了io_schedule(有些会调用io_schedule_timeout,这个函数的不同是,至少sleep多长时间)。我们看一下io_schedule的代码:(kernel/sched.c)


点击(此处)折叠或打开

  1. /*

  2.  * This task is about to go to sleep on IO. Increment rq->nr_iowait so

  3.  * that process accounting knows that this is a task in IO wait state.

  4.  */

  5. void __sched io_schedule(void)

  6. {

  7.          struct rq *rq = raw_rq();

  8.          delayacct_blkio_start();

  9.          atomic_inc(&rq->nr_iowait);

  10.          current->in_iowait = 1;

  11.          /*调用schedule函数,让出的CPU*/

  12.          schedule();

  13.          current->in_iowait = 0;

  14. /*增加当前CPU的等待IO的进程数*/

  15.          atomic_dec(&rq->nr_iowait);

  16.          delayacct_blkio_end();

  17. }


从这个函数可以看出,当进程IO操作的时候,会从当前CPUrunning队列移除,并让出CPU。那按照网上的解释,不应该增加load值啊。看来需要看看load的真正含义了。

How the kernel calculate load?

/proc/loadavg对应的代码是fs/proc/loadavg.c


点击(此处)折叠或打开

  1. static int loadavg_proc_show(struct seq_file *m, void *v)

  2. {

  3.          unsigned long avnrun[3];

  4.          get_avenrun(avnrun, FIXED_1/200, 0);

  5.          seq_printf(m, "%lu.%02lu %lu.%02lu %lu.%02lu %ld/%d %d\n",

  6.                    LOAD_INT(avnrun[0]), LOAD_FRAC(avnrun[0]),

  7.                    LOAD_INT(avnrun[1]), LOAD_FRAC(avnrun[1]),

  8.                    LOAD_INT(avnrun[2]), LOAD_FRAC(avnrun[2]),

  9.                    nr_running(), nr_threads,

  10.                    task_active_pid_ns(current)->last_pid);

  11.          return 0;

  12. }


这里通过get_avenrun函数获取(kernel/sched.c


点击(此处)折叠或打开

  1. void get_avenrun(unsigned long *loads, unsigned long offset, int shift)

  2. {

  3.          loads[0] = (avenrun[0] + offset) << shift;

  4.          loads[1] = (avenrun[1] + offset) << shift;

  5.          loads[2] = (avenrun[2] + offset) << shift;

  6. }


这里实际读取的全局变量avenrunAvenrun的更新在函数calc_global_load


点击(此处)折叠或打开

  1. void calc_global_load(void)

  2. {

  3.          unsigned long upd = calc_load_update + 10;

  4.          long active;

  5.          if (time_before(jiffies, upd))

  6.                    return;

  7.          active = atomic_long_read(&calc_load_tasks);

  8.          active = active > 0 ? active * FIXED_1 : 0;

  9.          avenrun[0] = calc_load(avenrun[0], EXP_1, active);

  10.          avenrun[1] = calc_load(avenrun[1], EXP_5, active);

  11.          avenrun[2] = calc_load(avenrun[2], EXP_15, active);

  12.          calc_load_update += LOAD_FREQ;

  13. }


这里实际读取的calc_load_tasks变量,这个变量在函数calc_load_account_active中被更新。(这个函数可能在每个tick的时候通过update_cpu_load调用,也可能在cpu选择idle进程的时候调用)


点击(此处)折叠或打开

  1. static void calc_load_account_active(struct rq *this_rq)

  2. {

  3.          long nr_active, delta;

  4.          nr_active = this_rq->nr_running;

  5.          /*这里不仅包含的running的进程,也包含了uninterruptible的进程*/

  6.          nr_active += (long) this_rq->nr_uninterruptible;

  7.          if (nr_active != this_rq->calc_load_active) {

  8.                    delta = nr_active - this_rq->calc_load_active;

  9.                    this_rq->calc_load_active = nr_active;

  10.                    atomic_long_add(delta, &calc_load_tasks);

  11.          }

  12. }


Conclusion

内核统计load的含义的是,所有cpu上的处于running状态和uninterruptible状态的进程,由于IO操作的时候,进程会进入uninterruptible状态,所有会增大系统的负载。这个时候负载虽然高,但是CPU仍然是比较空闲的,也就说IO导致的负载过高,对cpu bound型进程的影响不是太大。

阅读(4894) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~