ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [xv6] Process
    프로젝트/운영체제 만들기 2023. 7. 24. 13:25

    글의 참고

    -

     


    글의 전제

    - 밑줄로 작성된 글은 강조 표시를 의미한다.

    - 그림 출처는 항시 그림 아래에 표시했다.


    글의 내용

    - `xv6`에서는 `프로세스`라는 파트는 없고, 대신에 `스케줄링` 파트가 존재한다. 이 파트에서 프로세스에 대해 다루면서 스케줄링까지 한꺼번에 다룬다. 그런데, 생각보다 소스양이 많아서 `프로세스`와 `스케줄링`으로 나눠서 분석해본다. 이 글은 `스케줄링`에 대해 먼저 다룰 것이다. 

     

    - 이 글에서는 `proc.c` 파일의 `sleep, wakeup, wakeup1, yield, sched, scheduler, forkret`

     

    - 현재 동작하고 있는 프로세스

    - `xv6`에서 현재 동작하고 있는 프로세스가 바뀌는 시점이 언제일까? `xv6`에서 현재 동작하고 있는 프로세스를 나타내는 값은 `mycpu()->proc`과 `myproc()` 함수다. 그런데, `myproc()` 함수는 프로세스를 반환만 한다. 즉, 반환된 프로세스의 속성을 바꿀 수 는 있어도, 현재 동작하고 있는 프로세스를 다른 프로세스로 바꿀 수는 없다. 현재 동작하고 있는 프로세스를 바꾸는 코드는 결국 `mycpu()->proc` 밖에 없다. 확인 결과, 현재 동작하고 있는 프로세스가 바뀌는 부분은 `scheduler` 함수에서만 가능하다. 즉, `컨택스트 스위칭`을 통해서만 가능하다는 것이다. 이게 왜 중요할까?

    //PAGEBREAK: 42
    // Per-CPU process scheduler.
    // Each CPU calls scheduler() after setting itself up.
    // Scheduler never returns.  It loops, doing:
    //  - choose a process to run
    //  - swtch to start running that process
    //  - eventually that process transfers control
    //      via swtch back to the scheduler.
    void scheduler(void)
    {
      struct proc *p;
      struct cpu *c = mycpu();
      c->proc = 0;
      
      for(;;){
        // Enable interrupts on this processor.
        sti();
    
        // Loop over process table looking for process to run.
        acquire(&ptable.lock);
        for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){
          if(p->state != RUNNABLE)
            continue;
    
          // Switch to chosen process.  It is the process's job
          // to release ptable.lock and then reacquire it
          // before jumping back to us.
          c->proc = p;
          switchuvm(p);
          p->state = RUNNING;
    
          swtch(&(c->scheduler), p->context);
          switchkvm();
    
          // Process is done running for now.
          // It should have changed its p->state before coming back.
          c->proc = 0;
        }
        release(&ptable.lock);
    
      }
    }

    : 위의 `scheduler` 함수에서는 실제 `컨택스트 스위칭`을 하기 전에, 현재 동작하고 있는 프로세서의 프로세스를 변경하고 있다(`c->proc = p`). 그리고 `switchuvm` 함수를 통해서 이제 곧 동작할 프로세스 페이지 테이블로 교체하고, 현재 프로세서의 TSS의 ESP0을 이제 곧 동작할 프로세스의 커널 스택 TOP으로 설정한다. `switchuvm` 함수에서 진행하는 2가지 작업은 좀 더 구체적인 설명이 필요한 부분이다.

    0" 페이지 테이블 교체 : 컨택스트 스위칭이 발생하는 시점에 동작하고 있는 프로세스는 `스케줄러`다. `xv6` 문서에서는 `컨택스트 스위칭` 절차에 대해 아래와 같이 설명하고 있다. 아래 문서에서도 볼 수 있다시피, `컨택스트 스위칭`이 일어나려면, 반드시 스케줄러가 먼저 동작하고 있어야 한다. 그런데, 스케줄러는 커널 영역에 대한 가상 주소만 할당받은 페이지 테이블을 사용하고 있다(`kpgdir`). `xv6`의 프로세스들은 자신만의 별도의 페이지 테이블을 가지고 있다(유저 레벨과 커널 레벨이 하나의 페이지 테이블에 함께 관리된다). 사실, 프로세스 보다도 페이지 테이블을 변경함으로써, 완전히 새로운 프로그램이 될 수 있다. 예를 들어, `exec` 프로그램에 대한 바이너리 정보를 갖고 있는 페이지 테이블 A가 있고, `fork` 프로그램에 대한 바이너리 정보를 갖고 있는 페이지 테이블 B가 있다고 치자. 이 때, 프로세스가 페이지 테이블 A를 사용하면 `exec`와 같은 프로그램이 되는 것이고, 페이지 테이블 B를 사용하면, `fork`와 같은 프로그램이 되는 것이다.

    1" TSS 설정 : 컨택스트 스위칭으로 새로운 프로세스가 시작되면, 왜 프로세서 당 하나밖에 없는 TSS에 새롭게 시작되는 프로세스의 스택 TOP을 설정할까? 현대의 32비트 x86에서 TSS는 `권한이 낮은 수준에서 높은 수준으로 바뀔 때, 스택을 변경한다` 의 용도로만 사용한다. 권한이 변경되는 구간은 `유저 레벨 -> 커널 레벨` 밖에 없다. 즉, 시스템 콜을 사용하지 않는다면, 거의 사용되지 않는 구조라는 뜻이다. 그런데, `xv6`에서는 시스템 콜 호출한다고 해서, 무조건적으로 컨택스트 스위칭이 발생하지는 않는다. 즉, `scheduler` 함수가 무조건으로 호출되지는 않는다는 것이다. 이 말은, 유저 레벨에서 동작하고 있던 프로세스가 `sbrk`와 같은 시스템 콜을 호출하는 코드를 만나서 커널 레벨에 진입하더라도, 프로세스는 동일하다는 뜻이 된다. 그래서, 컨택스트 스위칭시에 `TSS의 ESP0에 제 곧 동작할 프로세스의 커널 스택 TOP으로 설정` 하는 것이다. 현재 프로세서의 TSS에 설정된 ESP 값은 현재 동작하고 프로세스의 스택 TOP을 의미한다는 것이다. 그래서 유저 레벨에서 시스템 콜을 호출하더라도, 프로세스는 변하지 않았기 때문에, 자신의 유저 레벨 스택에서 자신의 커널 레벨 스택으로 변경된 것 뿐이다. 위 그림에서도 보면, 유저 레벨에서 커널 레벨로 온다고 해서 `컨택스트 스위칭`이 무조건 일어나는 것이 아님을 확인할 수 있다.

    : 근데, 이전 페이지 테이블은 뭐였을까? 스케줄러의 페이지 테이블이다.

     

    : 그리고, 프로세스의 상태를 `동작중`으로 변경한다. 이제 진짜, `스케줄러`에서 `프로세스`로 컨택스트 스위칭이 일어날 차례다(`swtch`).

     

     : `시스템 콜, 인터럽트 및 익셉션`이 발생했을 때 프로세스가 바뀔까? 예를 들어,유저 레벨에서 프로세스 A가 열심히 뭔가 일을하고 있다고 치자. 그런데, 이 때 `sbrk`시스템 콜이 호출하는 코드를 만났다. 그렇면, OS는 유저 레벨에서 커널 레벨로의 전환을 위해 TSS에서 ESP0을 읽어서 스택을 새로 로드할 것이다.   

     

     

    : 프로세스가 바뀐다는 기준이 뭘까? 각 프로세스를 구분할 수 있는 기준이 뭘까? 단순한 구분이라면, `PID`로 가능할 것이다. 그러나, 기능적으로 A 프로세스와 B 프로세스가 다르다는 것을 어떻게 정의할 수 있을까? 내가 생각하는 기준에서는 `IP, SP, 페이지 테이블` 정도가 될 것이다.

    '프로젝트 > 운영체제 만들기' 카테고리의 다른 글

    [xv6] Buffer Cache  (0) 2023.07.24
    [xv6] inode  (0) 2023.07.24
    [xv6] Sleeplock  (0) 2023.07.23
    [멀티 프로세서] I/O APIC  (0) 2023.07.23
    [xv6] Application Processor  (0) 2023.07.23
Designed by Tistory.