ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Linux] 2. 프로세스와 스레드(Process & Thread)
    CSE/Linux 2015. 8. 11. 19:46

     프로세스(Process)란 일반적으로 현재 실행 중인 프로그램을 말하며 태스크(task)라는 일반적인 용어로도 사용됩니다. 실행 중인 프로그램의 의미를 가지는 프로세스는 실행되는 동안 커널이 가진 자원(CPU, Memory, Devices, Files)을 독자적으로 차지해야 할 경우가 많습니다. 따라서 프로세스는 항상 커널의 자원을 차지하기 위해 경쟁하고 커널은 이들에게 효율적으로 자원을 스케줄링하여 할당하고 회수하는 역할을 합니다. 프로세스는 커널이 가진 여러가지 자원의 할당 및 사용을 위해 커널 함수를 호출해야 하는데, 이러한 커널 함수들을 일반적으로 시스템 호출(System call)이라 합니다. 따라서 동작 중인 컴퓨터 시스템은 커널과 프로세스 간의 관계에 의해 모든 것이 결정되는 것이라 볼 수 있습니다.

     

     프로세스나 스레드는 리눅스 커널 내부에서 모두 자원을 차지하기 위해 서로 경쟁하는 태스크들로 관리됩니다. 


     프로세스에는 커널이 시스템의 관리를 위해 생성한 시스템 프로세스와 사용자가 생성한 사용자 프로세스가 있으며 일반적으로 시스템 프로세스는 중요한 커널의 내부적 일을 담당하므로 스케줄링 우선순위가 높은 것이 보통입니다.


     사용자가 리눅스 시스템에 로그인하면 운영체제와 사용자의 대화를 위해 쉘 프로그램이 실행되는데, 쉘 또한 하나의 응용 프로세스이며 쉘을 통한 명령이나 사용자 프로그램의 실행 또한 모두 프로세스의 형태로 실행됩니다.









    2.1 프로세스의 상태

     운영체제에 따라 상세한 프로세스의 상태 구분은 약간씩 달라질 수 있지만, 개념적으로 프로세스의 상태는 아래와 같이 분류됩니다.


     - 실행(Running) 상태: 프로세스에 CPU가 할당되어 실행 중인 상태.

     - 준비(Ready) 상태: 커널에 의해 스케줄링되어 CPU가 할당되면 실행될 수 있는 상태.

     - 대기(Blocked) 상태: 프로세스가 CPU 이외의 장치를 사용하는 입출력이나 메시지 수신등을 커널에 요구하게 되면 요구가 종료될 때 까지 프로세스는 CPU가 필요 없는 장치 상태가 되는데 이를 대기 상태라 함. 대기 상태동안 장치는 이 프로세스를 위해 동작하며, 동작 완료에 의한 입출력의 종료 또는 메시지  수신 등이 발생하면 해당 프로세스는 준비(Ready) 상태로 전이 됨. 커널은 한 프로세스의 대기 상태 동안 CPU를 다른 프로세스에 할당하여 CPU와 입출력 장치가 동시에 동작하도록 하여 자신의 효율성을 높이게 된다.









     2.1.1 프로세스의 상태 전이

      






      - CPU 할당을 기다리는 프로세스들은 스케줄링 큐(Scheduling Queue)에서 대기하다가 스케줄러에 의해 할당 받게 됨.

      - 커널은 CPU를 차지하여 실행 중인 프로세스의 CPU 독점을 방지하기 위해 Time slice에 의한 시분할 시스템의 개념을 도입하는 것이 보통.

      - Time slice란 프로세스의 CPU 독점을 방지하기 위해 매 CPU 차지시마다 CPU 사용의 한계 구간으로 주어지는 것으로 커널은 타임 슬라이스를 다 사용한 프로세스에서 일단 CPU를 회수하고, 다른 프로세스의 CPU 사용이 차례로 이루어진 후에 다시 CPU를 할당함.






     2.1.2 리눅스 프로세스의 상태

      1) 리눅스 프로세스의 실행(Running) 상태: TASK_RUNNING

       프로세스가 CPU를 회수 당하거나 반납하는 경우는 2 가지 형태가 있다.

         - 주어진 Time slice 소진 or 더욱 높은 우선순위에 의한 회수

         - 실행 중에 입출력(I/O)이나 동기화(이벤트 대기)에 관계된 시스템 호출을 하여 CPU를 커널 내부에 스스로 반납하는 경우


       운영체제에 따라 차이점이 있지만 리눅스의 경우에는 일반적으로 프로세스의 실행상태는 커널 모드 실행 상태와 사용자 모드 실행 상태의 두 가지로 구분됩니다.




    실행 모드 구분 

    실행 코드 

    시스템 보호 측면 

    사용자 모드

    (User Mode)

     사용자 프로그램의 코드가 실행된다.

     커널함수 호출이 아닌 일반 라이브러리 함수의 실행도 이에 포함된다. 

     실행중인 프로세스가 다른 프로세스나 커널의 메모리 영역을 침범하지 못하도록 메모리 보호 하드웨어가 작동된다. 

    커널 모드

    (Kernel Mode)

     프로세스가 실행 중인 메모리에 상주하는 커널 함수를 호출하였거나 하드웨어 인터럽트가 발생하여 커널 안의 코드가 수행될 때 

     사용자 모드에서는 불가능한 입출력이나 인터럽트 및 시스템 제어에 관련된 특수 명령어들을 수행할 수 있고, 커널 안의 코드를 수행하면서 다른 프로세스의 영역에도 접근할 필요가 있기에 메모리 보호 하드웨어도 통과 할 수 있다. 



       위에서 커널 모드실행은 프로세스가 커널의 시스템 호출을 함으로써 이루어진 다고 설명하였습니다. 그러면 시스템 호출은 무엇일까요? 아래 설명을 보도록 하겠습니다.



     *시스템 호출(System Call)




     










      

      2) 리눅스 프로세스의 준비(Ready) 상태: TASK_RUNNING

       리눅스의 경우 프로세스에 관한 커널의 모든 정보는 태스크 구조체(task_struct)에 저장되는데, 실행상태와 준비상태의 프로세스의 태스크 구조체들은 모두 스케줄링 큐에 연결 리스트 형태로 저장됩니다.





      3) 프로세스의 대기(Blocked) 상태: TASK_INTERRUPTIBLE 또는 TASK_UNINTERRUPTIBLE

       프로세스 상태가 대기 중일때, 그 태스크 구조체는 스케줄링 큐에서 제거되어 특정 대기큐에 소속되는 것이 보통입니다. 입출력이 완료 되거나 기다리던 이벤트가 발생하면 대기 상태의 프로세스는 다시 준비(Ready) 상태가 되어 그 태스크 구조체를 스케줄링 큐로 복귀됩니다.

       리눅스 운영체제의 경우, 대기 상태는 실질적으로 두 가지로 구분되는데, 첫째는 TASK_INTERRUPTIBLE 상태이고 다른 하나는 TASK_UNINTERRUPTIBLE 상태입니다.

       

        - TASK_UNINTERRUPTIBLE: 대기 중에 해당 프로세스에 응급 이벤트의 발생을 알리는 신호가 전달되어도 원래의 기다리는 이벤트가 발생할 때 까지 대기를 유지하는 상태

        - TASK_INTERRUPTIBLE: 신호에 의한 응급 처리를 위해 기다리던 이벤트에 대한 대기를 중지하고 준비 상태로 복귀하는 경우





      4) 프레서스의 좀비(EXIT_ZOMBIE) 상태

       프로세스의 수행이 종료되면 모든 포르세스의 task 구조체와 메모리 영역의 프로그램들은 제거되어야 합니다. 그러나 리눅스 커널에서는 해당 프로세스를 생성한 후, 생성 프로세스의 종료를 기다리는 부모 프로세스에게 종료 프로세스에 대한 정보를 전달해야 하므로, 정보 전달 시까지 task 구조체를 유지합니다. 이렇게 종료는 되었지만 task 구조체는 유지하고 있는 상태를 좀비 상태라고 합니다.








      5) 프로세스의 중지(TASK_STOPPED) 상태

       디버깅 목적을 위해 프로세스가 일시 중지된 상태입니다. 시그널이나 디버깅 개시 명령으로 재개합니다.




      6) 기타

       그 이외에도 부모 프로세스가 없는 경우의 EXIT_DEAD, 태스크 삭제 직전의 TASK_DEAD 등의 상태가 존재합니다.








    2.2 프로세스의 문맥 교환(Context switch)

     2.2.1 프로세스의 문맥(context)과 태스크 구조체

      정적자원인 프로그램과 달리 프로세스는 항상 실행되며 상태 변화를 계속하는 동적인 개체입니다. 또한, 프로세스는 수행 중에 자원에 대한 할당 대기와 외부 인터럽트 처리와 같은 작업에 의해 언제든지 수행이 중지되고, 그 후에 다시 속개되는 일이 반복됩니다.

      따라서 프로세스의 중지 시점에는 프로세스의 실행에 필요한 모든 정보와 환경들이 저장되어야 하고, 속개시에는 중단 시점의 내용이 그대로 복원되어야 합니다. 이렇게 중지시에 저장되고 속개시에 복원되는 프로세스의 실행에 필요한 모든 정보를 프로세스의 문맥(context)이라 합니다.


      다음은 프로세스 문맥의 구성입니다.






      위 문맥구성에서 프로세스 공간 영역은 메모리에 할당되므로 프로세스의 사용자 공간이 유지되면 자동으로 보존됩니다. 가상 메모리 관리에 의해 메모리에서 퇴출 당하는 영역이 발생하나 필요시에는 다시 메모리로 복귀합니다. 프로세스와 관련된 모든 문맥은 PCB에 보관되는데, 반드시 보존되고 실행 개시 시점에 복원되어야 하는 것은 대부분 CPU 레지스터의 내용입니다. 즉, 레지스터의 내용은 CPU를 차지하는 프로세스가 실행되는 동안 모두 변화되기 때문에 프로세스 중지시에는 반드시 보존해 두었다가 수행 속개시 다시 복원되어야 합니다.






    스케줄링 큐










     2.2.2 프로세스 간의 문맥 교환(Context switch)

      스케줄링으로 실행 중인 한 프로세스에서 다른 프로세스로 CPU가 할당될 때, 중단되는 프로세스가 사용중인 모든 레지스터의 내용은 보존되고, CPU가 서로 할당되는 프로세스는 자신이 이전에 중단되었던 시점에서 보존된 모든 레지스터의 내용이 복원되어야 합니다.

      따라서 CPU 내의 레지스터 입장에서는 현 내용이 보존되고 새로운 내용이 로딩되므로 이를 Context Switch 라고 합니다.

     

      교최되는 레지스터에는 프로그램 카운터(PC) 레지스터도 포함되므로, 실제로 문맥교환은 레지스터 내용 교체 후에 새로운 프로세스로 분기(Jump)하는 것이 됩니다. 리눅스 커널 내부의 스케줄러에 의해 호출되는 context_switch() 함수는 메모리 관련 정보에 대한 문맥 교환을 하고, switch_to() 함수를 호출하여 레지스터의 내용에 대한 문맥 교환을 하게 됩니다. 즉, switch_to() 함수에서 새로운 프로세스로 분기하는데, 유의할 점은 switch_to() 함수에 의해 중단되는 프로세스는 언젠가는 다시 다른 문맥 교환으로 다시 이 지점에서 수행이 속개된다는 점입니다. 









    댓글

Designed by Tistory.