Process Loadweight

태스크의 우선순위는 그 자체로 CFS스케쥴러에 의해 사용되는 개념은 아니다. 스케쥴러는 그보다는 loadweight라는 값을 통해 태스크에게 얼마큼의 CPU시간 비율을 줄것인지를 결정한다. 이 말은 우선순위는 load라는 개념으로 적절히 변환되어야 한다는 것을 의미한다.

load_weight 구조체에 의해 태스크의 loadweight이 표현된다.

struct load_weight {
        unsigned long weight;
        u32 inv_weight;
};

loadweight의 값은 아래 두개의 인자에 의존적으로 계산된다.

  • 프로세스의 스케쥴링클래스? 타입?
  • 프로세스의 static priority

nice value -> priority -> load weight로 변환되는 과정에 대한 그림???

우선순위에 걸맞는 loadweight값은 미리계산되어 sched_prio_to_weight에 저장되어 있다.

const int sched_prio_to_weight[40] = {
 /* -20 */     88761,     71755,     56483,     46273,     36291,
 /* -15 */     29154,     23254,     18705,     14949,     11916,
 /* -10 */      9548,      7620,      6100,      4904,      3906,
 /*  -5 */      3121,      2501,      1991,      1586,      1277,
 /*   0 */      1024,       820,       655,       526,       423,
 /*   5 */       335,       272,       215,       172,       137,
 /*  10 */       110,        87,        70,        56,        45,
 /*  15 */        36,        29,        23,        18,        15,
};

우선순위와 loadweight값을 매핑한 그림하나 넣을까??

120이라는 우선순위값은 loadweight 1024와 매핑되어 있다. 배열의 나머지 값들은 태스크의 우선순위가 한단계 내려갈때마다 기존보다 10%의 CPU시간을 더 갖도록 미리 계산되어져있다. 비슷한 방식으로 우선순위가 한단계 올라가면, 태스크는 10%의 CPU시간을 덜 갖게 될것이다.

실제로 그런지 자주 쓰이는 예제를 통해 알아보자. 태스크가 얻는 CPU시간의 비율은 아래와 같이 계산할 수 있다.

태스크의 얻는 CPU시간비율 
= 태스크의 loadweight/전체 태스크의 loadweight합

시스템에 2개의 태스크가 존재하고 둘 다 nice 값이 0일 경우라고 가정해보자.(== 태스크의 우선순위는 120임) loadweight는 1024가 된다. 이 때 태스크들이 얻는 CPU시간비율은 50%가 된다.

task A's loadweight 1024
------------------------      = 0.5 (50%)
1024(task A) + 1024(task B)

태스크 A의 우선순위를 한 단계 낮추면 어떻게 될까? A의 nice값은 1이 되고 loadweight 820과 매핑된다.

태스크 A가 얻게되는 CPU시간 비율은 0.45가 된다.

820(task A)
-----------------------     = about 0.45
820(task A) + 1024(task B)

반대로 태스크 B가 얻게되는 CPU시간 비율은 0.55가 된다.

1024(task A)
-----------------------     = about 0.55
820(task A) + 1024(task B)

태스크의 우선순위가 낮아지면 다른 태스크와의 CPU시간 비율이 10%차이가 나게되는걸 확인할 수 있다.

RT태스크의 이런 변환은 어떻게 진행될까?

실제 코드로 살펴보는 priority와 loadweight의 변환과정

태스크의 loadweight가 설정되는 경우는 아래와 같다.

  • 태스크가 새롭게 fork될 때
  • userspace에 의해 nice 시스템콜이 호출될 때
  • 태스크의 스케쥴링클래스가 변경될 때
  • RT쓰레드의 우선순위가 변경될 때(확실치 않음)

이때마다 set_load_weight()를 호출해서 태스크의 loadweight를 설정한다.

static void set_load_weight(struct task_struct *p)
{
        int prio = p->static_prio - MAX_RT_PRIO;
        struct load_weight *load = &p->se.load;

        /*
         * SCHED_IDLE tasks get minimal weight:
         */
        if (idle_policy(p->policy)) {
                load->weight = scale_load(WEIGHT_IDLEPRIO);
                load->inv_weight = WMULT_IDLEPRIO;
                return;
        }    

        load->weight = scale_load(sched_prio_to_weight[prio]);
        load->inv_weight = sched_prio_to_wmult[prio];
}

먼저 미리 계산되어 배열에 담긴 loadweight과 매핑된 priority를 구한다. 이과정에서 태스크의 static_prio가 쓰인다.(nice값과 1:1 매핑되어있다.)

위에서 구한 priority를 sched_prio_to_weight[] 배열의 index로 사용해서 매핑된 loadweight를 구하고 설정한다.

런큐에서의 loadweight

런큐도 loadweight를 사용한다.

struct rq {
        /* ... */
        struct load_weight load;
        /* ... */
}
struct cfs_rq {
        struct load_weight load;
        /* ... */
}

런큐의 load필드는 런큐에 속한 태스크의 loadweight의 합이 담겨져 있다. 태스크의 loadweight의 합이 곧 런큐의 loadweight이므로 태스크가 enqueue, dequeue될 때마다 런큐의 loadweight이 갱신된다.

  • 태스크를 런큐에 enqueue할 때
  • 태스크를 런큐에 dequeue할 때
  • 태스크의 weight가 변경될 때??

inv_weight는 멀까??

results matching ""

    No results matching ""