
23.12.07 최초 작성
고정된 시간 간격에서 시스템의 스레드가 적어도 1번은 실행되는 스케줄링 기법
현재 대부분의 리눅스 시스템에서 채택하고 있음
nice값을 바탕으로 우선도를 결정하며 낮을수록 높은 우선도를 가짐
Non-real-time-priority : 100 ~ 139의 범위를 가짐 (default = 120)Real-time priority : 0 ~ 99의 범위를 가짐/include/linux/sched.hsched_entity task_struct에서 관리되며 해당 자료구조를 통해 스케줄러에 스케줄링 됨struct load_weight는 우선순위를 나타내며 nice값에 의해 결정u64 vruntime은 최근 실행된 시간을 저장함struct sched_entity {
/* For load-balancing: */
struct load_weight load;
struct rb_node run_node;
struct list_head group_node;
unsigned int on_rq;
u64 exec_start;
u64 sum_exec_runtime;
u64 vruntime;
u64 prev_sum_exec_runtime;
u64 nr_migrations;
struct sched_statistics statistics;
...
};
task_structthread_info에서 스레드의 레지스터, sp, pc 등을 저장state에서 프로세스 상태를 나타냄sched_entity se에서 스케줄링에 필요한 정보 저장struct task_struct {
#ifdef CONFIG_THREAD_INFO_IN_TASK
/*
* For reasons of header soup (see current_thread_info()), this
* must be the first element of task_struct.
*/
struct thread_info thread_info;
#endif
/* -1 unrunnable, 0 runnable, >0 stopped: */
volatile long state;
/*
* This begins the randomizable portion of task_struct. Only
* scheduling-critical items should be added above here.
*/
randomized_struct_fields_start
void *stack;
refcount_t usage;
/* Per task flags (PF_*), defined further below: */
unsigned int flags;
unsigned int ptrace;
#ifdef CONFIG_SMP
int on_cpu;
struct __call_single_node wake_entry;
#ifdef CONFIG_THREAD_INFO_IN_TASK
/* Current CPU: */
unsigned int cpu;
#endif
unsigned int wakee_flips;
unsigned long wakee_flip_decay_ts;
struct task_struct *last_wakee;
/*
* recent_used_cpu is initially set as the last CPU used by a task
* that wakes affine another task. Waker/wakee relationships can
* push tasks around a CPU where each wakeup moves to the next one.
* Tracking a recently used CPU allows a quick search for a recently
* used CPU that may be idle.
*/
int recent_used_cpu;
int wake_cpu;
#endif
int on_rq;
int prio;
int static_prio;
int normal_prio;
unsigned int rt_priority;
const struct sched_class *sched_class;
struct sched_entity se;
struct sched_rt_entity rt;
...
};
/kernel/sched/sched.hstruct rqcfs_rq cfs, rt_rq rt, dl_rq dl를 관리함struct rq {
/* runqueue lock: */
raw_spinlock_t __lock;
/*
* nr_running and cpu_load should be in the same cacheline because
* remote CPUs use both these fields when doing load calculation.
*/
unsigned int nr_running;
#ifdef CONFIG_NUMA_BALANCING
unsigned int nr_numa_running;
unsigned int nr_preferred_running;
unsigned int numa_migrate_on;
#endif
#ifdef CONFIG_NO_HZ_COMMON
#ifdef CONFIG_SMP
unsigned long last_blocked_load_update_tick;
unsigned int has_blocked_load;
call_single_data_t nohz_csd;
#endif /* CONFIG_SMP */
unsigned int nohz_tick_stopped;
atomic_t nohz_flags;
#endif /* CONFIG_NO_HZ_COMMON */
#ifdef CONFIG_SMP
unsigned int ttwu_pending;
#endif
u64 nr_switches;
#ifdef CONFIG_UCLAMP_TASK
/* Utilization clamp values based on CPU's RUNNABLE tasks */
struct uclamp_rq uclamp[UCLAMP_CNT] ____cacheline_aligned;
unsigned int uclamp_flags;
#define UCLAMP_FLAG_IDLE 0x01
#endif
struct cfs_rq cfs;
struct rt_rq rt;
struct dl_rq dl;
...
};
cfs_rq unsigned int nr_running : 해당 런큐의 스레드 갯수load_weight load : 해당 런큐의 weight 총 합u64 min_vruntime : 최소 실행 시간, red-black트리(rb_root_cached tasks_timeline)로 프로세스를 관리하며 이 때 정렬 기준은 vruntimesched_entity *curr : 현재 실행 중인 프로세스의 정보 저장struct sched_entity *next : 교체할 프로세스의 정보 저장struct cfs_rq {
struct load_weight load;
unsigned int nr_running;
unsigned int h_nr_running; /* SCHED_{NORMAL,BATCH,IDLE} */
unsigned int idle_nr_running; /* SCHED_IDLE */
unsigned int idle_h_nr_running; /* SCHED_IDLE */
s64 avg_vruntime;
u64 avg_load;
u64 exec_clock;
u64 min_vruntime;
#ifdef CONFIG_SCHED_CORE
unsigned int forceidle_seq;
u64 min_vruntime_fi;
#endif
#ifndef CONFIG_64BIT
u64 min_vruntime_copy;
#endif
struct rb_root_cached tasks_timeline;
/*
* 'curr' points to currently running entity on this cfs_rq.
* It is set to NULL otherwise (i.e when none are currently running).
*/
struct sched_entity *curr;
struct sched_entity *next;
struct {
raw_spinlock_t lock ____cacheline_aligned;
int nr;
unsigned long load_avg;
unsigned long util_avg;
unsigned long runnable_avg;
} removed;
...
};
kernel/sched/fair.c
sched_slice() : timeslice를 설정하는 함수
__sched_period() : 만약 프로세스의 스레드 수가 일정 수 보다 크다면 period를 조정 __calc_delta() : 각 프로세스의 weight에 따라 timeslice를 계산pick_next_task_fair() : context switching할 프로세스 선택 (red-black tree)
update_curr() : __schedule()로부터 호출되어 curr에 저장된 sched_entity정보 수정
task_tick_fair() : curr에 저장된 sched_entity와 timeslice를 업데이트
__sched_period()에서 period 조정

__calc_delta()에서 weight에 따라 timeslice 계산

task_tick_fair()에서 timeslice를 업데이트

/kernel/sched/rt.ctask_tick_rt() : 프로세스의 sched_rt_entity정보에 따라 프로세스를 context switching 하는 함수dequeue_task_rt() : 프로세스의 sched_rt_entity정보에 따라 런큐에서 제거할 프로세스 제거task_tick_rt()
dequeue_task_rt()
fork, exec : 프로세스 생성 시 분산 (가장 한가한 cpu)wake up : 가장 한가한 cpu가 프로세스를 깨우고 해당 cpu가 작업 실행 idle : 적당한 작업을 현재 cpu로 할당periodic : 주기적으로 작업 분산global queue
Per-cpu queue
task_group : 관련 자료구조// linux/sched/sched.h
struct task_group {
struct cgroup_subsys_state css;
...
#ifdef CONFIG_RT_GROUP_SCHED
struct sched_rt_entity **rt_se;
struct rt_rq **rt_rq;
struct rt_bandwidth rt_bandwidth;
#endif
struct rcu_head rcu;
struct list_head list;
struct task_group *parent;
struct list_head siblings;
struct list_head children;
...
}
cpu.cfs_period_us : cpu 주기(ms)cpu.cfs_quota_us : 프로세스를 사용할 시간(ms) ( < cpu.cfs_period_us)// cgroup을 이용하여 CPU 25% 제한
sudo apt install -y stress
sudo su
cd /sys/fs/cgroup/cpu
mkdir kdt
cat cpu.cfs_period_us
$100000
echo 25000 > cpu.cfs_quota_us
// 100000/4 = 25000
// 현재 쉘 프로세스 등록해 결과 확인
echo $$ > tasks
$ stress --cpu 1
$ htop
kernel/sched/fair.c
enqueue_task_fair() : 스레드를 wake up 하기 위해 런큐에 sched_entitiy를 넣는 함수task_tick_fair() : curr에 저장된 sched_entity와 timeslice를 업데이트check_preempt_tick() : entity_tick()에 의해 호출되어 선점이 필요한지 체크load_balance() : 실제 로드밸런스를 실행하는 함수익셉션 핸들러를 종료하고 스레드 wake up을 위해 enqueue_task_fair() 호출하는 모습

task_tick_fair()

check_preempt_tick()

load_balance()가 실행되는 모습
