PintOS PJT2 - do_iret ()

김수환·2024년 11월 17일
0

PintOS

목록 보기
11/15

from __do_fork ()

/* Use iretq to launch the thread *** 실제로 context switching을 하는 함수 *** */
void 
do_iret (struct intr_frame *tf) {
    /* Structure -> CPU Register로 데이터 이동 (Load) */
    __asm __volatile (             // 입력한 그대로 사용
        "movq %0, %%rsp\n"        // 인자 *tf의 주소를 Register Stack Pointer RSP에 저장
        "movq 0(%%rsp),%%r15\n"   // rsp위치의 값(stack 시작)을 레지스터 r15에 저장
        "movq 8(%%rsp),%%r14\n"   // rsp+8위치의 값을 레지스터 r14에 저장
        "movq 16(%%rsp),%%r13\n"  // rsp+16위치의 값을 레지스터 r16에 저장
        "movq 24(%%rsp),%%r12\n"  // rsp+24 위치의 값을 레지스터 r12에 저장
        "movq 32(%%rsp),%%r11\n"
        "movq 40(%%rsp),%%r10\n"
        "movq 48(%%rsp),%%r9\n"
        "movq 56(%%rsp),%%r8\n"
        "movq 64(%%rsp),%%rsi\n"  
        "movq 72(%%rsp),%%rdi\n"
        "movq 80(%%rsp),%%rbp\n"
        "movq 88(%%rsp),%%rdx\n"
        "movq 96(%%rsp),%%rcx\n"   // rsp+96 위치의 값을 레지스터 rcx에 저장
        "movq 104(%%rsp),%%rbx\n"  // rsp+104 위치의 값을 레지스터 rbx에 저장
        "movq 112(%%rsp),%%rax\n"  // rsp+112 위치의 값을 레지스터 rax에 저장

        "addq $120,%%rsp\n"     // rsp 위치를 정수 레지스터 다음으로 이동-> rsp->es
        "movw 8(%%rsp),%%ds\n"  // rsp+8위치의 값을 레지스터 ds(data segment)에 저장
        "movw (%%rsp),%%es\n"   // rsp 위치의 값을 레지스터 es(extra segment)에 저장
        "addq $32, %%rsp\n"  // rsp 위치를 rsp+32로 이동. rsp->rip
        "iretq"              // rip 이하(cs, eflags, rsp, ss) 인터럽트 프레임에서 CPU로 복원. (직접 ACCESS 불가능)
        :                    // 인터럽트 프레임의 rip 값을 복원함으로서 기존에 수행하던 스레드의 다음 명령 실행 ... ?
        : "g"((uint64_t)tf)  // g=인자. 0번 인자로 tf를 받음
        : "memory");
}

do_iret 함수 해석

이 함수는 iretq 명령어를 사용하여 저장된 인터럽트 프레임 (intr_frame)의 상태를 CPU 레지스터에 복원하고, 중단된 스레드(또는 프로세스)의 실행을 재개합니다. 주로 문맥 교환(context switching) 시에 사용됩니다.


코드 상세 분석

1. 함수의 목적

  • struct intr_frame *tf에 저장된 상태 정보를 CPU 레지스터에 복원하여, 스레드의 실행을 이어갑니다.
  • iretq 명령어를 사용하여 인터럽트 이전 상태로 복귀합니다.

2. 어셈블리 코드 동작

(1) movq %0, %%rsp

  • rsp (Stack Pointer)를 tf로 설정합니다.
  • %0: 입력으로 받은 tf의 주소를 사용.
  • %%rsp: CPU의 스택 포인터 레지스터.

(2) 레지스터 복원 (movq ...)

  • rsp가 가리키는 메모리 주소에서 정수 레지스터 값을 읽어 CPU 레지스터에 복원합니다.
  • 순서:
    1. r15, r14, ..., rax: 정수 레지스터 복원.
    2. 레지스터의 저장 순서intr_frame 구조체의 정수 레지스터 배열과 일치합니다.

(3) 스택 포인터 이동 (addq $120, %%rsp)

  • 스택 포인터(rsp)를 데이터 세그먼트(ds)와 추가 세그먼트(es) 영역으로 이동.
  • 이전 레지스터 값을 복원했으므로 다음 데이터(es, ds)를 복원할 준비를 합니다.

(4) 세그먼트 레지스터 복원 (movw ...)

  • 세그먼트 레지스터 dses를 복원.
    • ds (Data Segment): 데이터가 저장되는 메모리 세그먼트.
    • es (Extra Segment): 추가 데이터 영역.

(5) 스택 포인터 추가 이동 (addq $32, %%rsp)

  • 스택 포인터를 rip(Instruction Pointer) 이하로 이동.
  • rip, cs, eflags, rsp, ss를 복원하기 위한 준비.

(6) iretq 명령어

  • iretq는 CPU의 인터럽트 프레임에 저장된 정보를 사용하여 다음 실행 상태를 복원합니다.
  • 복원되는 레지스터:
    1. rip (Instruction Pointer): 실행할 명령의 주소.
    2. cs (Code Segment): 명령어의 세그먼트.
    3. eflags (Flags Register): CPU 상태 플래그.
    4. rsp (Stack Pointer): 스택의 현재 위치.
    5. ss (Stack Segment): 스택 세그먼트.

3. 함수의 입력 및 출력

  • 입력: struct intr_frame *tf
    • 이전 인터럽트 발생 시 저장된 상태를 담고 있는 구조체.
  • 출력: 없음
    • CPU 상태가 복원되고, 중단된 실행이 재개됩니다.

4. 레지스터 및 스택 활용

  • CPU 레지스터(r15~rax, ds, es)와 플래그(eflags) 값을 스택에서 읽어 복원.
  • 스택 포인터(rsp)는 intr_frame의 메모리 구조에 따라 조정됩니다.

iretq 명령어의 핵심 역할

  • 스택에서 상태 복원:
    • CPU는 iretq를 통해 rip, cs, eflags, rsp, ss를 복원.
    • 복원된 rip 값을 사용해 인터럽트가 발생하기 전 실행 중이던 명령을 재개.

결론

do_iret는 저장된 인터럽트 프레임(intr_frame)의 상태를 복원하여 중단된 스레드 또는 프로세스의 실행을 재개하는 역할을 합니다. 어셈블리 코드로 구현되어 있으며, 문맥 교환에서 중요한 함수입니다. CPU 레지스터와 스택을 직접 다루므로 매우 저수준에서 실행됩니다.

profile
juniorDev

0개의 댓글