프로세스
실행중인 프로그램, 현대의 컴퓨팅 시스템에서 작업의 단위
실행 파일이 메모리에 적재될 때 프로그램은 프로세스가 된다. 프로세스의 현재 활동의 상태는 프로그램 카운터 값과 프로세서 레지스터의 내용으로 나타낸다. 프로세스의 메모리 배치는 일반적으로 여러 섹션으로 구분되며 아래와 같다.
- 텍스트 섹션 : 실행 코드
- 데이터 섹션 : 전역 변수
- 힙 섹션 : 프로그램 실행 중에 동적으로 할당되는 메모리
- 스택 섹션 : 함수를 호출할 때 임시 데이터 저장장소 (함수 매개변수, 복귀 주소 및 지역 변수)
텍스트 및 데이터 섹션의 크기는 고정되기 때문에 프로그램 실행 시간 동안 크기가 변하지 않는다. 그러나 스택 및 힙 섹션은 프로그램 실행 중에 동적으로 줄어들거나 커질 수 있다.
함수가 호출될 때마다 함수 매개변수, 지역 변수 및 복귀 주소를 포함하는 활성화 레코드가 스택에 푸시된다. 함수에서 제어가 되돌아오면 스택에서 활성화 레코드가 팝된다.
마찬가지로 메모리가 동적으로 할당됨에 따라 힙이 커지고 메모리가 시스템에 반환되면 축소된다. 스택 및 힙 섹션이 서로의 방향으로 커지더라도 운영체제는 서로 겹치지 않도록 해야한다.
스레드
프로세스 내에서 실행되는 여러 흐름의 단위
종종 가벼운 프로세스라 불리는 스레드는 CPU 활용의 기본단위이며, 스레드 아이디, 프로그램 카운터, 레지스터 세트 그리고 스택으로 이루어진다.
웹 서버가 다중 스레드화 되면, 서버는 클라이언트의 요청을 listen 하는 별도의 스레드를 운영한다.
요청이 들어오면 다른 프로세스를 생성하는 것이 아니라, 요청을 서비스할 새로운 스레드를 생성하고 추가적인 요청을 listen하기 위한 작업을 재개한다.
다중 스레드
대부분의 운영체제 커널도 일반적으로 다중 스레드이다.
장점
- 응답성
- 대화형 응용 프로그램을 다중 스레드화 하면 일부분이 봉쇄 또는 긴 작업을 수행하더라도 프로그램의 수행이 계속되는 것을 허용함으로써 사용자에 대한 응답성을 증가시킴
- 특히 사용자 인터페이스를 설계하는데 특히 유용함
- 자원 공유
- 스레드는 자동으로 자신이 속한 프로세스의 자원, 메모리를 공유한다.
- 위의 이점은 한 응용 프로그램이 같은 주소 공간 내에 여러개의 다른 작업을 하는 스레드를 가질 수 있다는 점
- 경제성
- 스레드는 자신이 속한 프로세스의 자원들을 공유하기 때문에, 스레드를 생성하고 문맥 교환하는 것이 더욱 경제적임
- 일반적으로 스레드 생성은 프로세스 생성보다 시간과 메모리를 덜 소비함
- Context switching(문맥 교환) 또한 일반적으로 프로세스 사이보다 스레드 사이에서 더 빠름
- 규모 적응성
- 다중 스레드 이점은 다중 처리기 구조에서 더욱 증가함
- 다중 처리기 구조에서는 각각의 스레드가 다른 처리기에서 병렬로 수행될 수 있음
문맥 교환 (Context Switch)
인터럽트가 발생하면 시스템은 인터럽트 처리가 끝난 후에 문맥을 복구할 수 있도록 현재 실행 중인 프로세스의 현재 문맥을 저장할 필요가 있다. 문맥은 프로세스의 PCB에 표현된다. 문맥은 CPU 레지스터의 값, 프로세스 상태, 메모리 관리 정보 등을 포함한다.
일반적으로 커널 모드이건 사용자 모드이건 CPU의 현재 상태를 저장하는 작업을 수행하고 나중에 연산을 재개하기 위하여 상태 복구 작업을 수행한다.
이때 Context Switching이 필요하다. 문맥 교환은 CPU 코어를 다른 프로세스로 교환하려면 이전의 프로세스의 상태를 보관하고 새로운 프로세스의 보관된 상태를 복구하는 작업이다.
context switch가 일어나면, 커널은 과거 프로세스의 문맥을 PCB에 저장하고, 실행이 스케줄된 새로운 프로세스의 저장된 문맥을 복구해야한다. 이러한 context switch가 일어나는 동안 시스템은 아무런 유용한 일을 못하기 때문에 context switch 시간은 순수한 오버헤드이다. 교환 속도는 메모리의 속도, 레지스터의 수(반드시 복사되어야 할), 특수 명령어 등에 의해 크게 좌우된다.
스레드와 프로세스의 동작방식은 상이한데, 스레드는 캐시메모리나 PCB에 저장해야되는 내용, 비워야하는 내용이 적기 때문에 상대적으로 더 빠른 context switch이 일어날 수 있고 자연스레 프로그램의 응답 시간이 단축된다.
❓면접 질문
Q1. 프로세스와 스레드의 차이는 무엇인가?
프로세스는 실행되고 있는 프로그램의 인스턴스라고 생각할 수 있다. 프로세스는 CPU 시간이나 메모리 등의 시스템 자원이 할당되는 독립적인 개체이다. 각 프로세스는 별도의 주소 공간에서 실행되며, 한 프로세스는 다른 프로세스의 변수나 자료구조에 접근할 수 없다. 한 프로세스가 다른 프로세스의 자원에 접근하려면 프로세스 간 통신을 사용해야 한다. 프로세스 간 통신 방법으로는 파이프, 파일, 소켓 등을 이용한 방법이 있다.
스레드는 프로세스 안에 존재하며 프로세스의 자원 (힙 공간 등)을 공유한다. 같은 프로세스 안에 있는 여러 스레드들은 같은 힙 공간을 공유한다. 반면에 프로세스는 다른 프로세스의 메모리에 직접 접근할 수 없다. 각각의 스레드는 별도의 레지스터와 스택을 갖고 있지만, 힙 메모리는 서로 읽고 쓸 수 있다. 스레드는 프로세스의 특정한 수행 경로와 같다. 한 스레드가 프로세스 자원을 변경하면, 다른 이웃 스레드도 그 변경 결과를 즉시 볼 수 있다.
Q2. 스레드가 생성될 때 어떤 자원이 사용되나요? 프로세스가 생성될 때와는 무엇이 다른가?
스레드가 생성될 때 스레드는 새로운 자원을 필요로 하지 않는다. 스레드를 실행하기 위해 자신이 속한 프로세스의 메모리와 같은 자원을 공유한다. 코드를 공유하는 장점은 같은 주소 공간 내에서 여러 개의 서로 다른 활성 스레드를 가질 수 있는 응용 프로그램을 허용한다는 것이다. 반면 새로운 프로세스를 생성하는 것은 매우 무거운 작업이다. 항상 새로운 주소 공간을 생성해야 하고 메모리를 공유한다 해도 내부 프로세스 간의 통신은 스레드 간의 통신에 비하면 비용이 매우 비싸다.
Q3. 프로그램과 프로세스의 차이는 무엇인가?
프로그램은 명령어 리스트를 내용으로 가진 실행파일과 같은 수동적인 존재이다.
이와 대조적으로 프로세스는 다음에 실행할 명령어를 지정하는 프로그램 카운터와 관련 자원의 집합을 가진 능동적인 존재이다.
Q4. 멀티 스레드와 멀티 프로세스?
멀티 프로세스 : 하나의 컴퓨터에 여러 CPU 장착 → 하나 이상의 프로세스들을 동시에 처리함 (병렬)
- 장점 : 안전성 (메모리 침범 문제를 OS 차원에서 해결함)
- 단점 : 각각 독립된 메모리 영역을 갖고 있어서, 작업량이 많을 수록 오버헤드가 발생한다. Context Switching으로 인해 성능 저하가 일어난다.
멀티 스레드 : 하나의 응용 프로그램에서 여러 스레드를 구성해 각 스레드가 하나의 작업을 처리함
- 장점 : 독립적인 프로세스에 비해 공유 메모리만큼의 시간, 자원 손실이 감소 전역 변수와 정적 변수에 대한 자료 공유가 가능함
- 단점 : 안전성 (하나의 스레드가 데이터 공간을 망가뜨리면, 모든 스레드가 작동 불능 상태가 된다. ), 동기화 문제 (다른 스레드에서 사용중인 변수나 자료구조에 접근하여 엉뚱한 값을 읽어오고나 수정될 수 있음 → 동기화 작업을 통해 작업 처리 순서를 컨트롤하고 공유 자원에 대한 접근을 컨트롤 한다. → 병목 현상이 발생하여 성능 저하 문제가 일어날 수 있기때문에 과도한 락으로 인한 병목현상을 줄여야함)
적용해야 하는 시스템에 따라 적합/부적합이 나뉜다!! 따라서 대상 시스템이 어떤 특징이냐에 따라 적합한 방식을 선택하자.
Q5. Context Switching이 뭔가요?
CPU가 프로세스를 처리하다가 다른 프로세스의 처리를 시작하기 위해 필요한 작업
Q6. 스택을 스레드마다 독립적으로 할당하는 이유는 뭔가요?
스택은 함수 호출 시 전달되는 인자, 되돌아갈 주소값 및 함수 내에서 선언하는 변수 등을 저장하기 위해 사용되는 메모리 공간이므로 스택 메모리 공간이 독립적이라는 것은 독립적인 함수 호출이 가능하다는 것이고, 이는 독립적인 실행 흐름이 추가되는 것이다. 따라서, 스레드의 정의에 따라 독립적인 실행 흐름을 추가하기 위한 최소 조건으로 독립된 스택을 할당한다.
Q7. PC 레지스터를 스레드마다 독립적으로 할당하는 이유는 뭔가요?
PC 값은 스레드가 명령어의 어디까지 수행하였는지를 나타낸다. 스레드는 CPU를 할당받았다가 스케줄러에 의해 다시 선점당한다. 그렇기 때문에 명령어가 연속적으로 수행되지 못하고 어느 부분까지 수행했는지 기억할 필요가 있다. 따라서 PC 레지스터를 독립적으로 할당한다.
•─────⋅참고 자료⋅─────•
아브라함 실버스카츠, [운영체제 10th Edition], 퍼스트북 (2020)
Narasimha Karumanchi, [코딩인터뷰퀘스천], 영진닷컴 (2012)
게일 라크만 맥도웰, [코딩인터뷰 완전분석], 프로그래밍인사이트 (2017)