이것이 점프 투 공작소

커널에서의 문맥교환 본문

리눅스

커널에서의 문맥교환

겅겅겅 2023. 2. 14. 21:40

출처 : https://byjus.com/gate/context-switching-in-os-notes/

문맥교환(context-switching)이란?

문맥교환은 커널이 프로세스간 스위칭 할 때, 현재 실행 중인 프로세스의 상태를 저장하고, 다음 실행할 프로세스의 상태를 불러오는 과정입니다, 이를 통해 커널에서 여러 프로세스가 동시에 실행(멀티테스킹)하는 것 처럼 보여집니다.

문맥 교환은 멀티테스킹, 인터럽트 , 커널<->사용자모드 전환 시 발생됩니다.

 

프로세스의 문맥이란?

1. 하드웨어 문맥 (Hardware Context)

  • CPU가 프로세스를 실행하기 위해 필요한 하드웨어 레지스터의 상태입니다.
  • 하드웨어 문맥에는 아래와 같은 데이터가 존재합니다.
    1. 프로그램 카운터(Program Counter, PC): 다음에 실행할 명령어의 메모리 주소를 저장하는 레지스터입니다.
    2. 프로세서 레지스터: CPU에서 연산에 사용되는 레지스터로, 데이터 레지스터, 주소 레지스터 등이 있습니다.
    3. 상태 레지스터: CPU가 연산을 수행하는 동안에 발생하는 상태 정보를 저장하는 레지스터로, 연산 결과에 대한 정보, 예외 상황에 대한 정보 등이 저장됩니다.
    4. 스택 포인터(Stack Pointer, SP): 스택 메모리의 시작 주소를 저장하는 레지스터입니다.
    5. 프로세서 모드(Mode): CPU가 실행하는 모드를 나타내는 레지스터로, 사용자 모드와 커널 모드가 있습니다.

2. 프로세스 주소공간(Process Adress Space) - 코드,데이터,스택영역으로 구분됩니다

  • 프로세스가 메모리를 사용하는 논리적인 공간, 프로세스마다 자신만의 독립된 주소 공간을 가지고 있습니다.
  • 이를 통해 프로세스는 다른 메모리와 자원 등을 공유하지 않고 독립적으로 실행됩니다.
    1. 코드(Code) 영역: 프로세스가 실행될 때, 실행 파일에 포함된 프로그램 코드가 메모리에 로드됩니다. 이 코드는 읽기 전용(Read-only)으로 메모리에 저장되며, 여러 프로세스에서 동시에 사용할 수 있습니다.
    2. 데이터(Data) 영역: 프로그램에서 사용하는 전역 변수, 정적 변수 등이 저장되는 영역입니다. 데이터 영역은 읽기 쓰기(Read-write)가 가능합니다.
    3. 스택(Stack) 영역: 프로그램에서 사용하는 지역 변수와 함수 호출 시 생성되는 지역 변수, 매개변수 등이 저장되는 영역입니다. 스택은 아래에서 위로 자라는 구조로 되어 있으며, 메모리의 높은 주소부터 할당됩니다.

3. 커널상의 문맥(Kernel Context) - 커널이 실행되는 동안 필요한 상태정보를 나타냅니다

  • 즉 프로세스를 관리를 위한 자료구조인 PCB(Process Control Block)와 Kernel stack(커널내의 주소)가 필요합니다.

커널에서 문맥교환은 언제 일어날까?

커널 코드 상에서는 schedule() 함수가 실행되면 문맥교환이 일어나게됩니다.

shcedule() 함수에서는 context_switch()를 호출하여 문맥교환을 시작합니다.

schedule() 함수는 need_resched 플래그가 변경되면 실행됩니다.

문맥교환이 실행되기 전까지 과정

need_resched 플래그 변경 -> (인터럽트, 시스템콜, 프로세스 생성, 프로세스 종료 등 재스캐줄 이벤트) -> schedule() 함수 실행 -> context_switch() 순서로 문맥교환이 실행되게됩니다.

need_resched 플래그란?

말 그대로 언제 schedule()함수를 실행해야하는지 알려주는 플래그입니다.

task_struct 구조체 안에 존재하는 변수입니다.

커널은 스케줄링 이벤트가 발생하면 need_resched 값을 체크하고 재스케줄링이 필요한 상태라면 스케줄링을 실행합니다.

 

커널이 need_resched 플래그를 변경하는 시점은 다음과 같습니다.

  • 현재 실행 중인 프로세스의 time slice가 끝나서 다음 프로세스로 전환해야 할 때
  • 현재 실행 중인 프로세스가 wait queue에 대기 중인 이벤트가 발생하기 전에 sleep 상태가 되었을 때
  • 현재 실행 중인 프로세스가 semaphore나 mutex와 같은 동기화 객체에 대해 대기 중이었는데, 이를 확보할 수 있는 다른 프로세스가 생겼을 때
  • 깨어난 프로세스가 현재 실행 중인 프로세스 보다 우선순위가 높을 때
  • 프로세스를 선점할 필요가 있을 때

context_switch 작업 (kernel 2.6)

아래  context_switch() 함수는 현재 실행 중인 스레드의 레지스터 상태와 메모리 매핑 정보를 새로운 스레드의 정보로 교체하는 작업을 수행합니다.

/*
 * 함수는 두 개의 task_struct 포인터를 인자로 받아서, 이전 스레드와 새로운 스레드를 전환합니다.
 */
static inline void
context_switch(struct rq *rq, struct task_struct *prev,
               struct task_struct *next)
{
        # 페이지 테이블 정보, 가상 메모리 주소와 실제 메모리 주소 매핑 역할, 포인터를 이용하여 메모리 매핑 정보를 교체
        struct mm_struct *mm, *oldmm; 

        prepare_task_switch(rq, prev, next  # 스레드 전환 준비
        trace_sched_switch(rq, prev, next); # 함수를 호출하여 스케줄링 변경에 대한 추적 정보를 기록합니다
        mm = next->mm;
        oldmm = prev->active_mm;

        arch_start_context_switch(prev); # 스레드 전환에 필요한 아키텍처별 동작을 수행합니다.

        if (likely(!mm)) {
                next->active_mm = oldmm;
                atomic_inc(&oldmm->mm_count);
                enter_lazy_tlb(oldmm, next);
        } else
                switch_mm(oldmm, mm, next);

        if (likely(!prev->mm)) {
                prev->active_mm = NULL;
                rq->prev_mm = oldmm;
        }

#ifndef __ARCH_WANT_UNLOCKED_CTXSW
        spin_release(&rq->lock.dep_map, 1, _THIS_IP_);
#endif

        switch_to(prev, next, prev); # 함수를 호출하여 이전 스레드와 새로운 스레드의 레지스터 값을 교체

        barrier();

        finish_task_switch(this_rq(), prev);
}

 

references

https://byjus.com/gate/context-switching-in-os-notes/

https://lxr.linux.no/linux+v2.6.34/kernel/sched.c