💬 프로세스
프로세스의 사전적 의미로는 "컴퓨터에서 연속적으로 실행되고 있는 컴퓨터 프로그램"이라는 의미로 프로그램의 실행 상태이자, 메모리 공간에 할당된 실체이다. 각 프로세스는 생성되면서 고유한 메모리 공간을 할당 받게 되고 때문에 프로세스는 다른 프로세스와 독립적으로 존재하여 하나의 프로세스에 충돌이 발생해도 다른 프로세스에는 영향을 주지 않는다.
즉, 이해하기 쉽게 말하자면 정적인 프로그램을 실행시켜 동적으로 변환되면서 실행된 상태를 말하는 것이다.
위에서 말한 프로그램과 혼동이 올 수 있다. 두 개의 차이를 명확히 알아보자
프로그램 | 프로세스 |
어떤 작업을 하기 위해 실행할 수 있는 파일 | 실행되어 작업중인 컴퓨터 프로그램 |
파일이 저장 장치에 있지만 메모리에는 올라가 있지 않은 정적인 상태 | 메모리에 적재되고 CPU 자원을 할당 받아 프로그램이 실행되고 있는 상태 |
코드 덩어리 | 실행하고 있는 코드 덩어리 |
특징
- 프로세스는 각각의 독립된 메모리 영역을 할당받는다. (Code, Data, Stack, Heap)
- 기본적으로 프로세스당 최소 1개의 스레드를 가지고 있다
- 각 프로세스는 별도의 주소 공간에서 실행되며, 한 프로세스는 다른 프로세스의 변수나 자료구조에 접근할 수 없다.
- 한 프로세스가 다른 프로세스의 자원에 접근하려면 프로세스 간의 통신(IPC)을 사용해야한다.
프로세스의 자원 구조
위에서 말한 특징 중 굉장히 중요한 것은 바로 4가지의 메모리 영역으로 구성된 프로세스의 자원 구조이다.
- 코드영역(Code) : 프로그래머가 작성한 프로그램 함수들의 코드가 CPU가 해석 가능한 기계어 형태로 저장되어 있다.
- 데이터 영역(Data) : 코드가 실행되면서 사용하는 전역 변수나 각종 데이터들이 모여있다. (.data, .rodata, .bss)
- .data : 전역 변수 또는 static 변수 등 프로그램이 사용하는 데이터를 저장
- .bss : 초기값이 없는 전역 변수, static 변수가 저장
- .rodata : const같은 상수 키워드 선언 된 변수나 문자열 상수가 저장
- 스택 영역(Stack) : 지역 변수와 같은 호출한 함수가 종료되면 되돌아올 임시적인 자료를 저장하는 독립적인 공간이다. Stack은 함수의 호출과 함께 할당되며, 함수의 호출이 완료되면 소멸한다. 만일 stack영역을 초과하면 stack overflow 에러가 발생한다.
- 힙 영역(Heap) : 생성자, 인스턴스와 같은 동적으로 할당되는 데이터들을 위해 존재하는 공간이다. 사용자에 의해 메모리 공간이 동적으로 할당되고 해제된다.
프로세스의 한계
오늘날의 컴퓨터는 멀티 작업이 굉장히 당연한 일이다. 하지만 과거에는 프로그램을 실행할 때 프로세스 하나만을 사용해서 이용했었다. 하지만 기술이 발전됨에 따라 프로그램이 복잡해지고 다채로워짐으로써 프로세스 작업 하나만을 사용해서 프로그램을 실행하기에는 한계가 있었다. 때문에 파일을 다운 받으면 실행 시작부터 실행 끝까지 프로세스 하나만을 사용하기 때문에 다운이 완료 될때까지 하루종일 기다려야 했다. 그렇다고 동일한 프로그램을 여러 개의 프로세스로 만들게 되면 각각의 프로세스는 메모리를 할당 받게 되고 CPU에서 할당받는 자원이 중복되는 현상이 발생할 수 있다.
이러한 프로세스의 한계로 인해 등장한 것이 바로 스레드 이다.
💬 스레드(Thread)
스레드의 사전적 의미는 "프로세스 내에서 실행되는 여러 흐름의 단위"이다. 위에서 말한 것처럼 한 개의 프로세스의 내부에서는 여러 개의 스레드(멀티 스레드)가 실행된다. 프로세스가 점유한 메모리 공간에서 각 스레드는 공간을 공유한다. 스레드는 서로의 메모리에 Access 할 수 있다. 이 방식은 더 빠른 통신과 효율을 제공하지만, 하나의 스레드에 오류가 발생해도 전체 프로세스와 해당 프로세스 내의 다른 스레드에 영향을 미치는 위험이 있다.
특징
- 스레드는 프로세스 내에서 각각 stack만 따로 할당받고 Code, Data, Heap영역은 공유하게 된다
- 스레드는 한 프로세스 내에서 동작되는 여러 실행의 흐름으로, 프로세스 내의 주소 공간이나 자원들을 같은 프로세스 내에 스레드끼리 공유하면서 실행된다.
- 같은 프로세스 안에 있는 여러 스레드들은 같은 힙 공간을 공유한다. 반면에 프로세스는 다른 프로세스의 메모리에 직접 접근할 수 없다.
- 각각의 스레드는 별도의 레지스터와 스택을 갖고 있지만 힙 메모리는 서로 읽고 쓸 수 있다.
- 한 스레드가 프로세스 자원을 변경하면, 다른 이웃 스레드도 그 변경 결과를 즉시 볼 수 있다.
스레드의 자원 공유
스레드는 프로세스가 할당 받은 자원을 이용하는 실행의 단위로써 스레드가 여러개 있다면 우리가 파일을 다운 받으며 동시에 웹 서핑을 할 수 있게 해준다. 스레드끼리 프로세스의 자원을 공유하면서 프로세스 실행 흐름의 일부가 되기 때문에 동시 작업이 가능한 것이다. 즉, 스레드는 밑에 보이는 사진 처럼 Stack 영역만 할당 받아 복사하고 Code, Data, Heap은 다른 스레드들과 공유된다. 따라서 각각의 스레드는 heap메모리가 고유하기 떄문에 서로 다른 스레드에서 가져와 읽고 쓸 수 있게 된다.
이렇게 구성되어 있는 이유는 하나의 프로세스를 다수의 실행 단위인 스레드로 구분하여 자원을 공유하고, 자원의 생성과 관리의 중복성을 최소화하여 수행 능력을 올리기 위해서이다.
💬 프로세스 & 스레드의 동시 실행 원리
우리가 컴퓨터를 키면 한가지의 일만 하지 않는다. 굉장히 많은 프로그램을 동시에 실행 시켜서 작동하는 것을 알 수 있는데 이는 컴퓨터의 내부적으로 프로세스와 스레드를 동시에 처리하는 멀티 테스킹의 기술 때문이다. 이러한 원리가 운영체제의 핵심 원리가 될 것이다.
조립 컴퓨터가 유명해지면서 다들 부품을 따로 장바구니에 담아 구매하는 추세가 된 것을 알 수 있다. 그중 우리가 CPU를 구매할 때를 생각해보자 우리는 무엇을 사던지 부품을 살 때 성능을 따지게 되는데 CPU도 성능을 가장 쉽게 확인 할 수 있는 것이 코어와 스레드가 적힌 부분이다.
예를 들어 우리가 고른 CPU가 4코어 8쓰레드 라고 치자. 그럼 해당 CPU는 명령어를 메모리에서 뽑아 해석하고 실행하는 반도체 유닛이 4개가 있다는 것이다. 4코어가 물리적 코어 갯수라면 8쓰레드는 논리적 코어 갯수를 뜻한다. 이 경우 물리적 코어 하나가 쓰레드 두 개 이상을 동시에 실행 가능하다는 의미가 된다
즉, 해당 CPU는 운영체제가 8개의 작업을 동시에 처리할 수 있다는 뜻이 되는 것이다.
중요한 것은 우리가 위에서 말한 스레드와 CPU의 쓰레드는 다르다.CPU의 쓰레드는 하드웨어적인 쓰레드이고 프로그램의 스레드는 소프트웨어적 스레드로 구분되는 것이다
근데 뭔가 이상하다. 물론 8개 이하의 프로그램을 키고 컴퓨터를 하는 사람도 있지만 수십, 수백개의 프로그램을 켜놓고 일하는 경우도 굉장히 많다. 그럼 그걸 8개의 쓰레드가 어떻게 처리하는 것일까?
이는 병렬성과 동시성이라는 개념을 알고 있어야 한다.
병렬성(Parallelism)
병렬성은 직관적으로 명령어를 메모리에서 뽑아 해석하고 실행하는 반도체의 여러개의 코어에 맞춰 여러개의 프로세스, 스레드를 돌려 병렬로 작업들을 동시 수행하는 것을 말한다.
동시성(Concurrency)
동시성은 둘 이상의 작업이 동시에 실행되는 것을 의미한다. 이 '동시'라는 의미에서 병렬성과 동시성의 한글의미가 헷갈릴수 있다. Parallelism가 물리적으로 정말로 동시에 실행하는것이라고 한다면, Concurrency는 동시에 실행하는 것처럼 보이게 하는 것으로 이해하면 되는 것이다.
즉, 한개의 코어가 다양한 프로세스들을 번갈아가면서 작업을 처리하면서 마치 프로그램이 동시에 실행되는 것 처럼 보이는 것이다. 이때 번갈아가면서 방문하여 처리하는 속도가 굉장히 빠르기 때문에 동시에 처리하는 것처럼 보일 수 있다.
이때 앞서 말한 번갈아가면서 방문하여 처리하는것을 Context Switching이라고 부른다.
동시성이 필요한 이유
- 하드웨어적 한계
- CPU의 발열로 인해 클럭으로 성능을 올리기에는 한계에 봉착했다. 때문에 코어의 성능을 올리는 대신 코어를 여러 개 탑재하여 쿼드 코어, 옥타 코어 CPU들을 출시하고 있다. 하지만 아무리 코어를 많이 널어도 수십개의 코어를 넣을순 없으니 결국 하드웨어적 제한으로 인해 동시성이 필요하다
- 논리적인 효율
- 4코어 8쓰레드의 CPU 환경에서 현재 총 16개의 작업이 있다면 그중8개는 오래 걸리는 작업, 나머지는 짧은 시간이 걸리는 작업이라 치고 만약 앞에서 부터 작업을 처리하면 긴 작업이 걸리는 8개의 작업 때문에 짧은 시간이 걸리는 뒤에 작업도 같이 늦어지는 비효율적인 면이 나타나게 된다. 이를 해결하기 위해 작업을 잘게 나누어 번갈아 가며 처리하는 동시성 개념이 채택된 것이다.
💬 프로세스와 스레드의 컨텍스트 스위칭
프로세스 컨텍스트 스위칭
컨텍스트 스위칭은 CPU가 한 프로세스에서 다른 프로세스로 전환할 때 발생하는 일련의 과정을 말한다. 위의 동시성 파트에서 다뤘듯 CPU는 한 번에 하나의 프로세스만 실행할 수 있다.때문에 여러개의 프로세스를 번갈아가며 실행하여 CPU활용률을 높이기위해 컨텍스트 스위칭이 필요한 것이다.
좀 더 구체적으로 동작 중인 프로세스가 대기를 하면서 해당 프로세스의 상태를 보관하고 대기하고 있던 다음 순서의 프로세스가 동작하면서 이전에 보관했던 프로세스의 상태를 복구하는 작업을 진행한다. 이러한 컨텍스트 스위칭이 일어날 때 다음번 프로세스는 스케줄러가 결정하게 된다.
PCB
PCB는 운영체제에서 프로세스를 관리하기 위해 해당 프로세스의 상태 정보를 담고 있는 자료구조이다.
프로세스를 컨텍스트 스위칭이 발생할때 기존의 작업의 상태를 저장하고 다음에 다시 불러왔을때 다시 이어서 진행을 할 수 있다. 여기서 PCB가 프로세스의 모든 정보를 저장해두는 임시 저장소인것이다.
따라서 PCB에 담긴 프로세스 고유 정보를 통해 프로세스를 관리하며, 프로세스의 실행 상태를 파악하고, 우선순위를 조정하고 스케줄링을 수행하며 다른 프로세스와 동기화를 제어한다.
PCB의 정보
- 포인터(Pointer) : 프로세스의 현재 위치를 저장하는 포인터 정보
- 프로세스의 상태 : 프로세스의 상태를 저장해둔다.
- 프로세스의 아이디
- 프로그램 카운터 : 프로세스를 위해 실행될 다음 명령어의 주소를 포함하는 카운터를 저장
- 레지스터 : 누산기, 베이스, 레지스터 및 범용 레지스터를 포함하는 CPU 레지스터에 있는 정보
- 메모리 제한 : 운영 체제에서 사용하는 메모리 관리 시스템에 대한 정보
- 열린 파일 목록 : 프로세스를 위해 열린 파일 목록
프로세스 컨텍스트 스위칭 과정
- CPU는 프로세스A를 실행한다
- 일정 시간이 지나 Interrupt 또는 system call이 발생한다.
- 현재 실행 중인 프로세스A의 상태를 PCB1에 저장한다.
- 다음으로 실행할 프로세스B를 선택한다
- 프로세스B의 상태를 PCB2에서 불러온다
- 일정 시간이 지나 Interrupt 또는 system call이 발생한다.
- 현재 실행 중인 프로세스B의 상태를 PCB2에 저장한다
- 다시 프로세스A를 실행할 차례가 된다
- 프로세스A의 상태를 PCB1에서 불러온다
- CPU는 프로세스A를 중간 지점 부터 실행한다.
스레드 컨텍스트 스위칭
스레드 컨텍스트 스위칭은 멀티 스레딩 환경에서 스레드 간의 실행을 전환하는 기술이다. 프로세스 컨텍스트 스위칭과 다른 점은 스레드 컨텍스트 스위칭은 하나의 프로세스 내의 스레드들을 교환한다
TCB
PCB처럼 TCB는 각 스레드마다 운영 체제에서 유지하는 스레드에 대한 정보를 담고 있는 자료구조이다. TCB는 PCB안에 들어있다. 스레드가 프로세스 안에 있는 것 처럼 말이다.
역시 스레드의 상태 정보, 스레드 ID, 스레드 우선순위, 스케줄링 정보 등 다양한 정보를 저장한다. TCB도 스레드가 생성될 때 운영 체제에 의해 생성되며, 스레드가 실행을 마치고 소멸될 때 함께 소멸된다.
또한 스레드 간의 자원 공유와 동기화도 TCB를 사용해서 관리된다. 예를 들어, 뮤텍스나 세마포어 같은 동기화 기법을 사용할 때, TCB에서 해당 스레드의 뮤텍스나 세마포어 정보를 관리하고, 스레드가 해당 자원에 대한 접근 권한을 획득하거나 반납할 때 TCB의 정보를 업데이트하게 된다.
프로세스 컨텍스트 스위칭 vs 스레드 컨텍스트 스위칭
- TCB는 PCB보다 가볍다
- 어찌보면 당연한 말이다. 프로세스 내부의 존재인 스레드를 다루는 TCB가 당연히 가벼울수 밖에 없다.
TCB는 stack 및 간단한 register 포인터 정보만을 저장하기때문에 가벼워 더 빨리 읽고 쓸 수 있다.
- 어찌보면 당연한 말이다. 프로세스 내부의 존재인 스레드를 다루는 TCB가 당연히 가벼울수 밖에 없다.
- 캐시 메모리 초기화 여부
- 프로세스 컨텍스트 스위칭
- CPU 캐시 메모리는 CPU와 메인 메모리 사이에 위치하며 CPU에서 한 번 이상 읽어들인 메모리의 데이터를 저장하고 있다가, CPU가 다시 그 메모리에 저장된 데이터를 요구할 때 메인 메모리를 통하지 않고 곧바로 데이터를 전달해주는 용도이다. 하지만 컨텍스트 스위칭이 일어나게 되면 CPU는 새로운 명령어와 데이터를 로드하기 때문에 CPU 캐시 메모리가 초기화 된다. 이는 굉장히 부담이 되는 요소이다
- 스레드 컨텍스트 스위칭
- 프로세스 내 스레드 간에 Stack과 register 값등 일부 컨텍스트 정보만 변경되므로 CPU 캐시 메모리는 초기화되지 않는다. 다만 스레드가 다른 CPU 코어에서 실행될 때는 해당 코어의 캐시 메모리에 스레드 컨텍스트 정보가 로드되어야 하므로 초기화 될 수 있다.
- 프로세스 컨텍스트 스위칭
- 자원 동기화
- 스레드 컨텍스트 스위칭
- 컨텍스트 스위칭이 발생하여 다른 스레드가 heap 영역의 공유 데이터에 접근할 때, 이전 스레드가 이미 공유 자원을 사용하고 있는 경우 동기화 문제가 발생할 수 있다. 예를 들어, 두 개의 스레드가 동시에 하나의 변수를 수정하려고 할 때, 스레드 컨텍스트 스위칭이 발생하면 변수의 값을 잘못된 값으로 업데이트 할 수 있는 것이다. 이것을 스레드 간에 경쟁 조건이라고 한다.
- 프로세스 컨텍스트 스위칭
- 기본적으로 독립된 공간이지만, IPC와 같은 공유 자원을 사용하는 경우에 똑같이 경쟁 조건이 발생 할 수 있다. 예를 들어, 여러 개의 프로세스가 동시에 파일 시스템에 접근하여 파일을 수정하려고 할 때, 컨텍스트 스위칭이 발생할 때 다른 프로세스가 그 파일에 접근할 수 있기 때문에 파일 내용이 손상 될 수 있다.
- 스레드 컨텍스트 스위칭
이를 해결하기 위해 각 상황에 적잘한 공유 자원에 대한 동기화 메커니증이 필요하다. 이것이 바로 멀티 프로세스와 멀티 스레드이다.
https://latewalk.tistory.com/175
💻 멀티 프로세스와 멀티 스레드
들어가기 앞서... 멀티 프로세스와 멀티 스레드에 대하여 공부하기 전 프로세스와 스레드를 알고 가는 것이 중요하다. 만약 궁금하다면 해당 블로그를 먼저 보고 오자 https://latewalk.tistory.com/173
latewalk.tistory.com
'CS > 💻 운영체제' 카테고리의 다른 글
💻 커널이란 (0) | 2024.05.29 |
---|---|
동기와 비동기 그리고 Blocking 과 Non - Blocking (0) | 2023.10.19 |