# scheduler tick: 주기적으로 실행 시간을 계상하고 재선택을 요청하기

v6.18.37 / kernel/sched/core.c

타이머 tick이 왔다고 매번 다른 task로 교체하는 것은 아닙니다. 먼저 경과 시간과 CPU 상태를 갱신하고 현재 스케줄링 정책에 이번 tick을 알려 줍니다. Linux 6.18.37에서 이 공통 함수의 실제 이름은 sched_tick이며, donor를 통해 계상 대상을 구분합니다.

## sched_tick

```c

void sched_tick(void)
{
	int cpu = smp_processor_id();
	struct rq *rq = cpu_rq(cpu);
	/* accounting goes to the donor task */
	struct task_struct *donor;
	struct rq_flags rf;
	unsigned long hw_pressure;
	u64 resched_latency;

	if (housekeeping_cpu(cpu, HK_TYPE_KERNEL_NOISE))
		arch_scale_freq_tick();

	sched_clock_tick();

	rq_lock(rq, &rf);
	donor = rq->donor;

	psi_account_irqtime(rq, donor, NULL);

	update_rq_clock(rq);
	hw_pressure = arch_scale_hw_pressure(cpu_of(rq));
	update_hw_load_avg(rq_clock_task(rq), rq, hw_pressure);

	if (dynamic_preempt_lazy() && tif_test_bit(TIF_NEED_RESCHED_LAZY))
		resched_curr(rq);

	donor->sched_class->task_tick(rq, donor, 0);
	if (sched_feat(LATENCY_WARN))
		resched_latency = cpu_resched_latency(rq);
	calc_global_load_tick(rq);
	sched_core_tick(rq);
	task_tick_mm_cid(rq, donor);
	scx_tick(rq);

	rq_unlock(rq, &rf);

	if (sched_feat(LATENCY_WARN) && resched_latency)
		resched_latency_warn(cpu, resched_latency);

	perf_event_task_tick();

	if (donor->flags & PF_WQ_WORKER)
		wq_worker_tick(donor);

	if (!scx_switched_all()) {
		rq->idle_balance = idle_cpu(cpu);
		sched_balance_trigger(rq);
	}
}

```

### 5579행

```c

void sched_tick(void)

```

이 버전의 공통 스케줄러 tick 함수입니다. 문서에서 개념적으로 scheduler tick이라고 부르더라도 원문 함수명은 sched_tick입니다.

### 5581행

```c

	int cpu = smp_processor_id();

```

지금 실행하는 CPU 번호를 얻습니다. 다른 CPU의 rq를 임의로 갱신하는 경로가 아닙니다.

### 5582행

```c

	struct rq *rq = cpu_rq(cpu);

```

해당 CPU의 runqueue를 찾습니다.

### 5584행

```c

	struct task_struct *donor;

```

시간 계상 대상 donor를 담을 포인터를 준비합니다.

### 5585행

```c

	struct rq_flags rf;

```

rq 잠금과 관련된 상태를 보관할 자료구조입니다.

### 5586행

```c

	unsigned long hw_pressure;

```

하드웨어 때문에 줄어든 CPU 처리 용량 정보를 저장합니다.

### 5587행

```c

	u64 resched_latency;

```

재스케줄 지연 경고를 위한 시간을 담습니다. 기능 검사에 따라 사용됩니다.

### 5589행

```c

	if (housekeeping_cpu(cpu, HK_TYPE_KERNEL_NOISE))

```

커널 잡음을 담당하는 housekeeping CPU인지 확인합니다. 격리된 CPU의 부담을 구분하는 조건입니다.

### 5590행

```c

		arch_scale_freq_tick();

```

해당 CPU라면 주파수 기반 용량 계상 상태를 갱신합니다.

### 5592행

```c

	sched_clock_tick();

```

스케줄러 시계의 tick 처리를 수행합니다. 뒤의 실행 시간 계산에 일관된 시각 기준이 필요합니다.

### 5594행

```c

	rq_lock(rq, &rf);

```

rq 상태를 일관되게 갱신하기 위해 잠금을 잡고 관련 상태를 rf에 보관합니다.

### 5595행

```c

	donor = rq->donor;

```

잠금 안에서 이번 계상의 대상 task를 읽습니다.

### 5597행

```c

	psi_account_irqtime(rq, donor, NULL);

```

donor의 PSI 관련 IRQ 시간 계상을 반영합니다. 작업이 CPU를 얻지 못하는 압력 관찰과 연결됩니다.

### 5599행

```c

	update_rq_clock(rq);

```

실행 큐의 현재 시계를 갱신합니다. 오래된 시각으로 정책의 경과 시간을 계산하지 않도록 합니다.

### 5600행

```c

	hw_pressure = arch_scale_hw_pressure(cpu_of(rq));

```

CPU가 실제 제공하지 못하는 처리 용량에 대한 하드웨어 pressure 값을 읽습니다.

### 5601행

```c

	update_hw_load_avg(rq_clock_task(rq), rq, hw_pressure);

```

갱신한 rq task 시각과 pressure로 하드웨어 부하 평균을 갱신합니다.

### 5603행

```c

	if (dynamic_preempt_lazy() && tif_test_bit(TIF_NEED_RESCHED_LAZY))

```

lazy preemption이 동작하고 지연된 재스케줄 요청이 있는지 확인합니다.

### 5604행

```c

		resched_curr(rq);

```

그렇다면 현재 rq에 재스케줄이 필요함을 반영합니다. 이 호출을 곧바로 context switch라고 해석하지 않습니다.

### 5606행

```c

	donor->sched_class->task_tick(rq, donor, 0);

```

donor가 속한 스케줄링 정책의 task_tick을 실행합니다. 마지막 0은 이 호출에서 queued 인자로 전달되는 값입니다.

### 5607행

```c

	if (sched_feat(LATENCY_WARN))

```

재스케줄 지연 경고 기능이 켜져 있는지 확인합니다.

### 5608행

```c

		resched_latency = cpu_resched_latency(rq);

```

켜져 있으면 요청 이후의 지연 시간을 계산하여 잠금 밖에서 경고할 수 있게 합니다.

### 5609행

```c

	calc_global_load_tick(rq);

```

시스템 load 계상에 이번 tick을 반영합니다.

### 5610행

```c

	sched_core_tick(rq);

```

core scheduling의 tick 관련 상태를 처리합니다. 설정에 따라 실체가 달라질 수 있습니다.

### 5611행

```c

	task_tick_mm_cid(rq, donor);

```

메모리 문맥의 concurrency ID 관리에 tick을 반영합니다.

### 5612행

```c

	scx_tick(rq);

```

sched_ext 쪽의 tick 처리도 수행합니다. 전체 task가 해당 클래스로 전환되었는지는 아래에서 별도 확인합니다.

### 5614행

```c

	rq_unlock(rq, &rf);

```

공통 rq 상태 갱신을 끝내고 잠금을 풉니다.

### 5616행

```c

	if (sched_feat(LATENCY_WARN) && resched_latency)

```

경고 기능이 켜져 있고 실제 지연이 감지되었는지 확인합니다. 앞의 조건과 맞물려 resched_latency를 읽습니다.

### 5617행

```c

		resched_latency_warn(cpu, resched_latency);

```

잠금 밖에서 지연 경고를 출력합니다.

### 5619행

```c

	perf_event_task_tick();

```

성능 계측 이벤트의 task tick 처리를 수행합니다.

### 5621행

```c

	if (donor->flags & PF_WQ_WORKER)

```

계상 대상이 workqueue worker인지 flags 비트로 확인합니다.

### 5622행

```c

		wq_worker_tick(donor);

```

worker라면 workqueue의 tick 관련 관리도 진행합니다.

### 5624행

```c

	if (!scx_switched_all()) {

```

모든 task를 sched_ext가 맡는 상황이 아닌지 확인합니다. 기존 균형 조정과의 역할을 구분합니다.

### 5625행

```c

		rq->idle_balance = idle_cpu(cpu);

```

이 CPU가 idle인지 기록하여 balancing 판단에 제공합니다.

### 5626행

```c

		sched_balance_trigger(rq);

```

필요한 스케줄러 부하 균형 조정 절차를 촉발합니다. 이 줄 하나가 모든 이동을 완료한다는 뜻은 아닙니다.

