프로세스와 스레드

junu-devlog·2021년 7월 11일
0

프로세스와 스레드

익숙하면서도 헷갈리는 프로세스와 스레드의 개념에 대해 다시 한 번 정리해보자

프로그램(Program), 프로세스(Process), 쓰레드(Thread)

.

프로세스(Process)

운영체제로부터 자원을 할당받은 작업의 단위를 말하며, 쉽게 말해 실행중인 프로그램 을 하나의 프로세스로 이해할 수 있다. 일반적인 프로그램은 컴퓨터의 저장장치에 그냥 저장만 되어있는 정적인 상태이다. 다시 말해, 프로그램 자체는 그냥 가만히 있는 코드 덩어리에 불과한 것이다. 여기서 프로그램을 수행하게 되면, 운영체제가 프로그램에 CPU, 메모리 등의 자원을 할당하게 되는데, 이를 메모리에 올린다 고 표현한다.

메모리에 올라가 프로그램이 실행중인 상태를 동적인 상태 라고도 표현한다. 프로세스의 동작은 CPU가 프로세스에서 내린 명령을 실행하는 것이며, 운영체제는 프로세스의 생성과 종료를 위한 작업과 프로세스를 실행시키기 위한 스케줄링 작업을 처리한다.(상태관리)

CPU는 컴퓨터 시스템을 통제하고, 프로그램의 연산을 실행하고 처리하는 역할을 수행

.
프로세스의 상태 변화는 크게 아래의 6가지로 나뉜다.

img

  • 생성 -> 준비

    미리 정의된 정책에 따라 스케줄러에 의해 프로세스가 호출되는데, 여기서 메모리의 이용 가능성 및 어떤 장치가 요구되는 지 여부 등을 검사한다.

  • 준비 -> 실행 (디스패치)

    사전에 정의된 스케줄링 알고리즘에 따라 자원을 할당한다. 스케줄러에 의해 자원을 할당하는 과정을 디스패치(dispatch) 라고 표현한다.

  • 실행 -> 준비

    자원 할당 시간이 만료되었거나 우선순위 알고리즘에 따라 더 높은 우선순위를 가지는 프로세스를 실행해야 하는 상황이 오면, 스케줄러가 실행중이던 프로세스를 다시 준비상태로 전환하는 것이다.
    여기서 특정 시간동안 실행될 프로세스에 자원을 할당시키는 것을 타임아웃(Timeout) 이라고 한다.

  • 실행 -> 대기

    READ, WRITE, 다른 입출력 요구, 페이지 교환 등의 추가 작업을 위한 이벤트 발생시, CPU를 다른 프로세스에 할당하여 활용하기 위해 실행중이던 프로세스를 대기시키고 자원을 양도한다.(Block)

  • 대기 -> 준비

    추가 작업을 위한 조건이 만족된 경우 주로 입출력 장치 관리 신호에 의해 일어나며, 프로세스는 다시 준비 큐에 놓인다.(Wakeup)

  • 실행 -> 종료

    프로세스를 성공적으로 끝마친 경우 혹은 운영체제가 에러 발생을 감지하여 프로세스를 강제로 종료시키는 경우에 스케줄러에 의해 수행된다.

    .

프로세스의 메모리 구조

img

  • 스택(Stack) 영역

    자료구조에서의 Stack은 후입선출(Last In First Out)으로 마지막에 추가된 요소부터 처음으로 제거되는 특징이 있다.

    데이터가 일시적으로 저장되는 영역으로 지역변수와 매개변수, 리턴값 등이 저장된다. 함수가 호출될 경우 생성되며, 메모리에 올라갈 때 사이즈가 고정되기 때문에 런타임 상태에서는 스택의 사이즈를 바꿀 수 없다. 다시 말해, 컴파일 시점에서 사이즈가 결정되는 것이다.

    명령 실행시(=함수 호출시) 자동으로 증가 혹은 감소하며 보통 메모리의 마지막 부분에 해당하는 주소값을 할당한다. 제일 높은 부분에서 아래 방향으로 데이터를 쌓다가, LIFO 에 따라 Heap 영역과 가까운 제일 아래에 쌓인 데이터부터 제거된다.

    여기서 보통 재귀함수가 무한으로 호출되는 경우에 데이터가 너무 많아져 스택 영역을 침범하게 될 경우에는 stack overflow 예외가 발생한다.

    .

  • 힙(Heap) 영역

    메모리의 할당 및 해제와 관련 있는 영역으로 메모리 주소 값에 의해서만 참조되고 사용되는 영역이다. C에서의 malloc() 나, java의 new 와 같이 메모리 할당 및 해제 키워드와 관련이 있다.

    메모리의 누수를 방지하기 위해 사용된 메모리는 사용하고 난 후에 반드시 해제되야 하는데, 아래에서 위로 데이터가 쌓이다가 Stack 영역을 침범하게 되면 heap overflow 가 발생한다.

    결국 Stack과 Heap 영역은 사실상 같은 공간을 공유한다고 봐도 된다.

  • 데이터(Data 영역) - BSS, GVAR

    • 프로그램이 실행될 때 생성되고, 종료될 때 반환되는 영역으로 전역변수, 정적(Static)변수, 배열, 구조체 등이 저장됨

      • 전역변수와 정적변수를 참조하는 코드는 컴파일되면 데이터 영역의 해당 변수값에 해당하는 주소값을 가리키도록 바뀜
      • 프로그램 실행 도중에 전역변수가 변경되는 경우가 발생할 수 있기 때문에, Read-Write 형식으로 지정
    • 데이터 영역을 저장 후 초기화 여부에 따라 GVAR(Data)와 BSS(Block Stated Symbol) 로 나눔

      • 위에서 언급한 변경될 수 있는 전역변수나 함수 내부에 선언된 정적 변수는 프로그램 실행시 공간만 할당하여 BSS에 저장하고, 변경되거나 함수 실행시 초기화됨
      • 이렇게 하는 이유는, 초기화되지 않은 데이터까지 ROM 에 저장하는 것은 비용상 비효율적이기 때문에 초기화되지 않은 데이터를 RAM 에 저장하여 구분하고자 Data와 BSS로 나누는 것
  • 코드(Code) 영역

    • 프로세스가 실행할 명령을 포함한 코드들이 저장되는 영역
    • 컴파일된 프로세스가 기계어의 형태로 저장되며, 읽기전용(READ) 이기 때문에 변경 불가능

>>> 프로세스 제어 블록(PCB)

프로세스 제어 블록 (PCB: Process Control Block) 알아보기

  • 운영체제가 프로세스를 제어하기 위해 프로세스의 상태 정보를 저장해놓은 자료구조

  • 프로세스가 진행됨에 따라 PCB는 변경되기도 하며, 프로세스가 종료되면 PCB도 사라짐

  • 운영체제가 PCB에 빠르게 접근하기 위해 Process Table 을 사용하여 PID를 통해 원하는 PCB 위치에 접근할 수 있음(인덱싱 같은 것)

  • 포인터, 프로세스 상태, 프로세스 번호(PID), 프로그램 카운터, 레지스터, 메모리, 우선순위 등의 정보가 저장됨

    • Pointer : 프로세스의 현재 위치

    • Process State : 생성, 준비, 실행, 대기, 종료의 프로세스의 상태

    • Process Number(PID) : 프로세스를 식별하기 위한 고유한 ID

    • Program Counter : 실행될 다음 명령어의 주소

    • Register : CPU 레지스터 정보

    • Memory Limit : 운영체제의 메모리 관리 정보를 포함하며, 페이지 및 세그먼트 테이블 등을 저장

    • Open File Lists : 프로세스 실행을 위해 열린 파일 목록

>>> Context Switching

  • 문맥 교환 이라고도 하며, 하나의 프로세스가 CPU를 비롯한 자원을 사용중인 상태에서 다른 프로세스가 자원을 사용해야 할 경우에 프로세스의 상태(문맥 / Context)를 보관하고 새로운 프로세스의 상태를 적재하는 것을 말함
    • 문맥 교환이 불가능하다면 특정 작업이 끝날 때까지 무조건 기다려야 하기 때문에 매우 불편하고 반응속도 또한 느리게 될 것
  • 문맥에 대한 정보는 위에서 언급한 PCB 에 저장되고 관리되며, 문맥 교환이 일어날 때 다음에 실행될 작업의 PCB 정보를 읽어 레지스터에 적재함으로써 CPU는 연속적으로 프로세스를 수행할 수 있는 것

.

스레드(Thread)

img

  • 프로그램에 복잡해질수록 단순히 하나의 프로세스만 가지고 프로그램을 실행하는 것은 사실상 힘듦

  • 이 경우, 단순히 하나의 프로그램 실행을 위해 여러 개의 프로세스를 생성하는 하는 방법을 생각해볼 수 있지만 보통 프로세스 하나에 할당된 프로세스 제어블록에 많은 양의 상태정보가 포함될 뿐만 아니라 프로세스는 자신에게 할당된 메모리에만 접근할 수 있기 때문에, 이러한 병렬처리는 사실상 불가능하다고 할 수 있음

  • 결국 단일 프로세스 내부에서의 다중 처리를 위해 프로세스보다 한단계 더 낮은 실행 단위 개념이 필요한데, 이를 스레드(Thread) 라고 하는 것

    • 스레드의 개수에 따라 싱글 스레드(Single Thread)멀티 스레드(Multi Thread) 로 나눌 수 있음
    • 프로세스를 보통 자원 소유의 단위(unit of resource ownership) 과, 디스패칭의 단위(unit of dispatching) 의 두 가지측면으로 볼 수 있는데, 스레드는 여기서 디스패칭 단위에 해당하는 개념임
    • 다시 말해, 여러 개의 스레드가 전체 자원을 소유한 하나의 프로세스 내에서 각각 사용할 자원을 분배받는 것을 의미함
  • 컴퓨터에 여러 개의 CPU가 있거나, CPU가 여러 개의 코어로 된 경우라면 다중 스레드를 병렬로 처리할 수 있는데, 이를 통해 속도가 느린 작업을 다른 작업들이 불필요하게 기다려야 하는 상황을 극복할 수 있는 것

  • 스레드는 프로세스에서 실행의 개념 만을 분리한 것이기 때문에 실행에 필요한 최소한의 정보만을 가지고, 프로그램 실행시 프로세스의 실행 환경을 공유함

    • 멀티스레딩일 경우에는 프로세스에 1개의 메인 스레드와 별도의 여러 스레드로 이루어짐

    • 스레드들은 Stack 영역 만 개별적으로 할당받고, Code, Data, Heap 영역은 서로 공유함

    • 따라서, 한 스레드가 프로세스의 특정 자원을 변경할 경우에는 다른 스레드 역시 이러한 변경의 영향을 받을 수 있음

      • 더 나아가, 동기화(Synchronization) 개념이 필요한 이유가 여기에 있는 것!

        ..

    >>> 사용자 수준 스레드와 커널 수준 스레드의 차이

    • 스레드가 사용자 모드와 커널 모드 중 어디서 동작하냐에 차이(생성 주체의 차이)
      • 커널 모드 : 운영체제 커널이 동작하여 하드웨어를 직접 제어하는 CPU 명령어를 사용할 수 있는 모드
      • 사용자 모드 : CPU 명령어를 직접 사용하지 못하고 응용 프로그램만이 동작되는 모드
      • 보호모드에서 하드웨어 제어가 필요한 경우에는 운영 체제에 커널 모드로의 전환을 요청하게 되는데, 이를 시스템 호출 이라고 함
    • 커널 수준의 스레드 는 운영체제 시스템 내에서 생성되어 동작하며, 커널이 직접 관리함
      • 응용프로그램과 하드웨어 사이의 가교 역할을 수행한다고 보면 됨
      • 커널이 직접 제공하기 때문에 안정성이 높지만, 사용자 모드에서 커널 모드로의 전환이 빈번할 경우에는 오히려 성능 저하가 일어날 가능성이 높음
    • 사용자 수준의 스레드 는 말 그대로 사용자 수준에서 생성 및 관리되며, 커널이 이를 따로 관리하지 않음
      • 커널은 사용자 수준의 스레드에 대해 알지 못함
      • 따라서 사용자 <-> 커널 모드 간 전환이 없기 때문에 잦은 전환으로 인한 성능 저하가 일어날 가능성이 적음
      • 하지만, 프로세스가 블로킹될 경우에는 이러한 상황에 대한 예측이 그만큼 어려움
profile
배운 것들을 꾸준히 기록하자

0개의 댓글