티스토리 뷰
6.2.6 태스크 깨우기: try_to_wake_up (ttwu)
잠들어있던 task 가 깨우기에 적절한 실행 상태인지 체크하고 실행할 cpu 를 결정
6-13 kernel/sched/core.c try_to_wake_up
- 태스크가 이전 실행되던 cpu 에서 wakeup
- 새로운 cpu 에서 wakeup
try_to_wake_up {
ttwu_remote 먼저 시도 6-14
ttwu_queue 6-15
}
6-14 kernel/sched/core.c
task 가 runqueue 에 이미 들어가있는 상태라면, 현재 task 가 선점할 수 있는지 확인후 스케줄링 요청
static int ttwu_remote(struct task_struct *p, int wake_flags)
{
if (task_on_rq_queued(p)) { // 큐에 있는지 확인
update_rq_clock(rq); // rq clock 갱신
ttwu_do_wakeup(rq, p, wake_flags); // 코드 6-18
}
}
6-15 kernel/sched/core.c ttwu_queue
잠들어 있던 task 를 지정된 cpu 에서 실행 될 수 있도록 enqueue 한다.
/*
* Queue remote wakeups on the target CPU and process them
* using the scheduler IPI. Reduces rq->lock contention/bounces.
*/
// 이 매크로로 TTWU_QUEUE 피쳐를 사용할지 정함
// rq 에 직접 lock 을 잡지 않고, wake up 할 task 를 큐에 넣어둘 수 있는 피쳐
SCHED_FEAT(TTWU_QUEUE, true)
static void ttwu_queue(struct task_struct *p, int cpu)
{
struct rq *rq = cpu_rq(cpu);
#if defined(CONFIG_SMP)
// 이전 cpu 와 현재 cpu 가 캐시를 공유하지 않을때만 사용 가능
// 같은 cpu 를 사용하거나 캐시를 공유하면 activate 를 바로 호출하도록 하는 것 같다.
if (sched_feat(TTWU_QUEUE) && !cpus_share_cache(smp_processor_id(), cpu)) {
sched_clock_cpu(cpu); /* sync clocks x-cpu */
ttwu_queue_remote(p, cpu); // remote cpu 에서 실행되도록 설정 6-16
return;
}
#endif
ttwu_do_activate(rq, p, 0); // 6-16
}
u64 sched_clock_cpu(int cpu)
{
struct sched_clock_data *scd;
u64 clock;
if (sched_clock_stable()) // 이미 안정적이라면 sched_clock 을 반환
return sched_clock();
if (unlikely(!sched_clock_running)) // 아직 sched clock 이 돌지 않은 경우
return 0ull;
preempt_disable_notrace();
scd = cpu_sdc(cpu); // per-cpu sched_clock_data
if (cpu != smp_processor_id())
clock = sched_clock_remote(scd); // 나와 상대 cpu 의 clock 을 동일하게 변경
else
clock = sched_clock_local(scd); // 내 clock 갱신
preempt_enable_notrace();
return clock;
}
6-16 kernel/sched/core.c ttwu_queue_remote
remote cpu 에서 task 가 실행될 수 있도록 설정하기
if (llist_add(&p->wake_entry, &cpu_rq(cpu)->wake_list)) { // remote cpu 의 wake_list 에 추가, 리스트가 비어있었다면 아래코드 실행
if (!set_nr_if_polling(rq->idle))
smp_send_reschedule(cpu); // 폴링하지 않으니 직접 인터럽트
else
// ipi: inter processor interrupt
trace_sched_wake_idle_without_ipi(cpu); // 인터럽트 없이 트레이싱
}
6-17 kernel/sched/core.c ttwu_do_activate
태스크를 실행 가능하도록 만들기. runqueue 에 넣고,
선점 가능하다면 스케줄링 요청 6-18
static void
ttwu_do_activate(struct rq *rq, struct task_struct *p, int wake_flags)
{
#ifdef CONFIG_SMP
if (p->sched_contributes_to_load)
rq->nr_uninterruptible--; // 이제 큐로 들어오기때문에 interrupt 가 가능한 상태라는 듯
// queue 에 들어갈때 감소 시키고, dequeue 할때 증가시킨다 (deactivate_task)
// 근데 activate_task 에서 또 감소시키는데?????? -> 최신 버전에서는 activate, deactivate 에서 값을 수정하지 않음
#endif
ttwu_activate(rq, p, ENQUEUE_WAKEUP | ENQUEUE_WAKING);
ttwu_do_wakeup(rq, p, wake_flags);
}
static inline void ttwu_activate(struct rq *rq, struct task_struct *p, int en_flags)
{
activate_task(rq, p, en_flags); // 큐에 넣기
p->on_rq = TASK_ON_RQ_QUEUED; // rq 에 들어감 플래그 설정
/* if a worker is waking up, notify workqueue */
#define PF_WQ_WORKER 0x00000020 /* I'm a workqueue worker */
if (p->flags & PF_WQ_WORKER)
wq_worker_waking_up(p, cpu_of(rq));
}
void activate_task(struct rq *rq, struct task_struct *p, int flags)
{
if (task_contributes_to_load(p))
rq->nr_uninterruptible--; // 이제 큐로 들어오기때문에 interrupt 가 가능한 상태라는 듯
enqueue_task(rq, p, flags);
}
6-18 kernel/sched/core.c ttwu_do_wakeup
current task 가 선점가능하다면 스케줄링 요청
static void ttwu_do_wakeup(struct rq *rq, struct task_struct *p, int wake_flags)
{
check_preempt_curr(rq, p, wake_flags); // 6-19 선점 가능하면 TIF_NEED_RESCHED 설정
p->state = TASK_RUNNING; // enqueue 됐으니
#ifdef CONFIG_SMP
if (p->sched_class->task_woken) {
p->sched_class->task_woken(rq, p); // 콜백
}
if (rq->idle_stamp) { // fair 스케줄링용, idle 로 있던 시간을 평균으로 갖고 있는다
...
}
#endif
}
6-19 kernel/sched/core.c check_preempt_curr
선점 가능하면 TIF_NEED_RESCHED 설정
#define sched_class_highest (&stop_sched_class)
#define for_each_class(class) \
for (class = sched_class_highest; class; class = class->next)
extern const struct sched_class stop_sched_class;
extern const struct sched_class dl_sched_class;
extern const struct sched_class rt_sched_class;
extern const struct sched_class fair_sched_class;
extern const struct sched_class idle_sched_class;
void check_preempt_curr(struct rq *rq, struct task_struct *p, int flags)
{
const struct sched_class *class;
if (p->sched_class == rq->curr->sched_class) {
rq->curr->sched_class->check_preempt_curr(rq, p, flags); // rq 의 스케줄러 클래스와 동일하면 사용
} else {
for_each_class(class) { // 우선순위가 높은 class 부터 돌게됨
if (class == rq->curr->sched_class) // task 의 스케줄링이 rq 보다 낮은경우
break; // 스케줄 요청하지 않음
if (class == p->sched_class) { // task 의 스케줄링이 rq 보다 높은 경우
resched_curr(rq); // 스케줄링 플래그 설정 TIF_NEED_RESCHED
break;
}
}
}
/*
* A queue event has occurred, and we're going to schedule. In
* this case, we can save a useless back to back clock update.
*/
// 이미 clock 갱신했으니 뒤에는 스킵 가능하다 플래그 설정
if (task_on_rq_queued(rq->curr) && test_tsk_need_resched(rq->curr))
rq_clock_skip_update(rq, true);
}
6-20 kernel/sched/fair.c
fair 의 check preempt_curr 은 check_preempt_wakeup 을 호출, 각 스케줄러에도 정의되어 있음
const struct sched_class fair_sched_class = {
.check_preempt_curr = check_preempt_wakeup,
다음 스케줄시 우선적으로 next 가 될 수 있도록 설정
static void check_preempt_wakeup(struct rq *rq, struct task_struct *p, int wake_flags)
{
struct task_struct *curr = rq->curr;
struct sched_entity *se = &curr->se, *pse = &p->se;
struct cfs_rq *cfs_rq = task_cfs_rq(curr);
int scale = cfs_rq->nr_running >= sched_nr_latency; // enqueue 된 태스크 수가 latency 보다 큰 경우 (너무 많음)
int next_buddy_marked = 0;
if (unlikely(se == pse)) // 같은 스케줄 엔티티인 경우
return;
if (unlikely(throttled_hierarchy(cfs_rq_of(pse)))) // 요청의 개수를 제한
return;
if (sched_feat(NEXT_BUDDY) && scale && !(wake_flags & WF_FORK)) { // 다음 스케줄링때 새로 생성된 태스크를 우선 선점
set_next_buddy(pse);
next_buddy_marked = 1;
}
if (test_tsk_need_resched(curr)) // 이미 RESCHED 플래그 설정된 경우
return;
if (unlikely(curr->policy == SCHED_IDLE) && // 현재 돌고있던건 IDLE 인데
likely(p->policy != SCHED_IDLE)) // 새로 오는 task 는 IDLE 이 아니면 선점
goto preempt;
if (unlikely(p->policy != SCHED_NORMAL) || !sched_feat(WAKEUP_PREEMPTION)) // SCHED 가 IDLE,BATCH 거나 옵션이 꺼져있으면 선점 불가
return;
// 양쪽 sched entity 를 부모를 찾아가면서 같은 그룹일때까지 변경
// vruntime 을 같은 그룹에서 하고싶어서 그런가?
find_matching_se(&se, &pse);
update_curr(cfs_rq_of(se)); // vruntime 등 돌아간 시간을 계산
BUG_ON(!pse);
if (wakeup_preempt_entity(se, pse) == 1) { // pse 가 더 돌아야 하는지. 예를들어 이전 task 인 se 가 예상보다 많이 돌았다면 스케줄링 필요
if (!next_buddy_marked)
set_next_buddy(pse);
goto preempt;
}
return;
preempt:
resched_curr(rq); // TIF_NEED_RESCHED 설정
// last buddy 가 될수있는지 체크. last buddy 는 next buddy 이후에 우선 선점될지 여부
// idle 스레드인 경우나 deque 되어있을때는 우선적으로 실행할 필요 없음
if (unlikely(!se->on_rq || curr == rq->idle))
return;
// 이전 그룹을 last task 로 등록
if (sched_feat(LAST_BUDDY) && scale && entity_is_task(se))
set_last_buddy(se);
}
6-21 kernel/sched/core.c set_task_cpu
cpu 에서 task 가 실행될 수 있도록 설정
void set_task_cpu(struct task_struct *p, unsigned int new_cpu)
{
if (task_cpu(p) != new_cpu) { // cpu 가 달라진다면
if (p->sched_class->migrate_task_rq) // 콜백 등록된거 있는지 보고 실행한다음에
p->sched_class->migrate_task_rq(p);
p->se.nr_migrations++; // sched entity 에 마이그레이션 수 올려주고
}
__set_task_cpu(p, new_cpu); // 6-22
}
6-22 kernel/sched/sched.h __set_task_cpu
static inline void __set_task_cpu(struct task_struct *p, unsigned int cpu)
{
set_task_rq(p, cpu);
#ifdef CONFIG_SMP
/*
* After ->cpu is set up to a new value, task_rq_lock(p, ...) can be
* successfuly executed on another CPU. We must ensure that updates of
* per-task data have been completed by this moment.
*/
smp_wmb(); // smp 에 쓰기연산 순서 보장
task_thread_info(p)->cpu = cpu; // ti 에 cpu 등록
p->wake_cpu = cpu; // wake_cpu 등록
#endif
}
task 에 cpu 에 맞는 rq 설정
static inline void set_task_rq(struct task_struct *p, unsigned int cpu)
{
#if defined(CONFIG_FAIR_GROUP_SCHED) || defined(CONFIG_RT_GROUP_SCHED)
struct task_group *tg = task_group(p);
#endif
#ifdef CONFIG_FAIR_GROUP_SCHED
set_task_rq_fair(&p->se, p->se.cfs_rq, tg->cfs_rq[cpu]);
p->se.cfs_rq = tg->cfs_rq[cpu];
p->se.parent = tg->se[cpu];
#endif
#ifdef CONFIG_RT_GROUP_SCHED
p->rt.rt_rq = tg->rt_rq[cpu];
p->rt.parent = tg->rt_se[cpu];
#endif
}
'개발 > 코드로 알아보는 ARM 리눅스 커널 TIL' 카테고리의 다른 글
250308 스케줄링 엔티티의 실행시간 관리하기 & 스케줄러 초기화 (6.3~6.4) (2) | 2025.03.08 |
---|---|
250301 CFS 태스크 선택, 제거, 삽입 (6.3.1~6.3.4) (0) | 2025.03.01 |
250208 6.2.4 스케줄링 요청하기 (0) | 2025.02.15 |
250201 5.7 IDLE 쓰레드 초기화 ~ 6.2 스케줄링 (1) | 2025.02.08 |
20250111 5.5 테스크 생성하기 ~ 5.7 idle 쓰레드 (swapper) (0) | 2025.01.18 |
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
TAG
- 에러 위치 찾기
- boost
- mysql
- ad skip
- Reciprocal n-body Collision Avoidance
- 우리는 vr핏이라고 부릅니다
- 카카오
- 면접
- hole-punching
- vrpit
- Golang
- vr핏
- 영상 픽셀화 하기
- Obstacle Avoidance
- SuffixArray
- 클래스 맴버 변수 출력하기
- Visual Studio
- C++
- set value
- Quest2
- 코어 남기기
- 봄날에 스케치
- RVO
- shared_from_this
- 잘못된 빨간줄
- red underline
- chrome-extension
- cockroach db
- print shared_ptr class member variable
- it's called a vrpit
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 |
글 보관함