운영체제에서 프로세스는 하나의 작업 단위(Task)
폰노이만 구조에서 프로그램이 실행된다는 것은 메모리에 올라와서 작업이 진행되는 것. 이 때 프로그램
이란 저장장치에 있는 정적인 상태이고 프로세스
는 메모리에 올라온 동적인 상태임
운영체제는 프로그램을 메모리의 적당한 위치로 가져옴. 그와 동시에 PCB(Process Controll Block) 이라는 작업 지시서를 만드는데 PCB가 없으면 프로그램이 프로세스로 전환되지 못함
경계 레지스터
와 한계 레지스터
도 포함되어 있음PCB는 프로세스를 관리하는 데이터 구조로 운영체제 영역에 만들어짐
※ 포인터의 역할
시스템 내에는 다양한 종류의 입출력 장치가 있기 때문에 대기 상태로 모이는 프로세스도 다양함. 예를 들어 하드디스크로부터 인터럽트가 들어왔는데 프로세스가 한 곳에 모여있으면 해당 프로세스를 찾기위해서 모든 프로세스를 뒤져야함. 이러한 불편함을 해결하기 위해서 같은 입출력을 요구한 프로세스끼리 모아놓음
(* 준비상태 -> 실행상태)
준비 상태에 있던 프로세스 중 다음에 실행할 프로세스를 선정하는 일은 CPU 스케줄러
가 담당함. CPU 스케줄러는 준비 상태의 맨 앞에서 기다리고 있는 PCB를 CPU에 전달하여 작업이 이루어지도록 함. 이 때 준비 상태의 프로세스 중 하나를 골라 실행 상태로 바꾸는 작업을 디스패치
라고 함
(* 준비상태 -> 실행상태)
오늘날의 운영체제는 효율성을 고려하여 한 가지 상태를 더 만들었음. 프로세스가 입출력을 요구하면 CPU가 직접 데이터를 가져오지 않고 입출력 관리자에게 명령을 내리게 되는데 입출력 요청은 컴퓨터의 연산속도에 비하면 굉장한 시간을 낭비하는 것이기 때문에 프로세스의 입출력이 완료될 때까지 기다리는 대기 상태
가 추가됨.
대기 상태의 프로세스는 요청한 입출력이 완료되면 입출력 관리자로부터 인터럽트를 받음. 이 때 대기 상태가 종료된 프로세스는 실행 상태로 가지 않고 준비 상태로 돌아감
(* 준비상태 -> 실행상태)
보류 상태
는 프로세스가 메모리에서 잠시 쫓겨난 상태로 대부분 컴퓨터의 성능을 저하시키거나 실행을 미루어도 큰 지장이 없는 프로세스임. 보류 상태는 보류 대기 상태 와 보류 준비 상태 로 구분되며 보류 상태에 들어간 프로세스는 메모리 밖으로 쫓겨나 스왑 영역
에 보관됨. 추가적으로 메모리밖으로 쫓겨나지 않고 멈춘 상태를 휴식 상태
라고 함.
문맥 교환은
CPU를 차지하던 프로세스가 나가고 새로운 프로세스를 받아들이는 작업을 말함
프로세스는 코드 영역, 데이터 영역, 스택 영역 으로 구성됨.
데이터 영역은 추가로 일반 데이터 영역과 힙 영역으로 나뉜다.
코드 영역
코드 영역은 프로그램의 본문이 기술된 곳으로 텍스트 영역이라고도 함. 우리가 작성하는 코드가 코드 영역에 읽기 전용으로 처리됨
데이터 영역
데이터 영역은 코드가 실행되면서 사용하는 변수 , 파일 등 각종 데이터를 모아놓은 곳. 데이터는 변하는 값이기 때문에 해당 영역은 기본적으로 읽기와 쓰기가 가능함 (상수 영역은 읽기만 가능하다)
스택 영역
스택 영역은 운영체제가 프로세스를 실행하기 위해 부수적으로 필요한 데이터를 모아놓은 곳. 예를 들어 함수를 호출하면 원래 프로그램으로 돌아올 위치를 해당 영역에 저장함
프로세스를 생성할 때, 매번 새로 생성하는 방법뿐 아니라 기존의 프로세스를 복사하는 방법도 있음
fork()
는 실행중인 프로세스로 부터 새로운 프로세스를 복사하는 함수임. 만약 인터넷 브라우저를 켜 놓은 상태로 Ctrl + N
단축키를 실행하면 현재의 브라우저 프로그램을 fork로 복사하여 실행됨
fork를 통하여 프로세스를 복사하면 원래 프로세스는 부모 프로세스, 새로 생긴 프로세스는 자식 프로세스가 된다.
fork를 호출하면 PCB를 포함한 부모 프로세스 영역이 대부분 자식 프로세스에 복사되어 같은 프로세스가 만들어진다. 다만 일부 내용은 부모 프로세스와 다르게 변경된다.
PID가 바뀜 위 그림에서 부모 프로세스는 PID가 326 , 자식 프로세스는 PID가 368 인 것을 확인 가능
메모리 관련 정보가 바뀜. 부모 프로세스와 자식 프로세스가 존재하는 메모리 위치가 다르기 때문
PPID와 CPID가 바뀜. 부모 프로세스에서는 자식 프로세스를 가리키는 CPID가 368로 , 자식 프로세스에서는 부모 프로세스를 가리키는 PPID가 326으로 바뀌며 추가적으로 자식 프로세스의 CPID는 -1임.
exec()
시스템 호출은 기존의 프로세스를 새로운 프로세스로 전환하는 함수.
exec를 사용하는 목적은 프로세스의 구조체를 재활용하기 위함임. 새로운 프로세스를 만들려면 PCB를 만들고 메모리의 자리를 확보해야 하는데 exec를 사용하면 새로운 코드 영역만 가져오면 되기 때문에 기존 프로세스의 PCB , 메모리 영역 , 부모-자식 관계를 그대로 사용 가능함
exec를 호출하면 코드 영역에 있는 기존의 내용을 지우고 새로운 코드로 바꾼다. 추가적으로 PCB에서 PID , PPID , CPID , 메모리 관련 사항은 변하지 않지만 프로그램 카운터, 각종 레지스터 , 파일 정보가 전부 리셋됨
유닉스에서 커널이 처음 메모리에 올라와 부팅되면 커널 관련 프로세스를 여러개 만드는데 그 중 init
프로세스는 모든 프로세스의 출발점이 됨.
init 프로세스는 일반 프로세스 맨 위에 위치하며, fork와 exec를 이용하여 자식 프로세스를 만든다.
계층 구조는 동시에 여러 작업을 처리하고 종료된 프로세스의 자원을 회수하는데 유용함
여러 작업 동시 처리
login 프로세스는 인증을 거쳐 컴퓨터에 접속하는 과정을 처리함. 예를 들어 사용자 3명이 동시에 컴퓨터에 접속한다면 동시에 3명을 처리해야 하는데, login 프로세스는 1명만 처리 가능함. 이 때 fork를 활용하여 login 프로세스를 여러 개 만들어 사용자에게 나누어 주면 새로운 사용자가 들어올 때마다 작업을 동시에 처리 가능
login 프로세스를 통과하고 나면 shell 프로세스가 필요하다. shell이 있어야만 사용자가 운영체제에 명령을 내리고 결과를 얻을 수 있다. login 프로세스가 작업을 마치면 메모리 공간이 비워지고 PCB가 제거되며, shell 프로세스를 위한 메모리 공간이 확보되고 PCB도 새로 생성됨 하지만. 이러한 작업은 굉장히 비효율적이므로 exec 호출을 사용하여 login 프로세스를 shell 프로세스로 전환시킴
용이한 자원 회수
프로세스가 독립적으로 만들어지면 프로세스가 종료될 때마다 자원을 회수하러 가야하는데, 모든 프로세스를 부모-자식 관계로 만들면 자식프로세스가 작업을 마쳤을 때 사용하던 자원을 부모가 회수하는 방식으로 간결화 할 수 있다.
고아 프로세스
부모 프로세스는 자원을 회수하기 위해 자식 프로세스가 끝날 때 까지 기다려야 함. 그런데 부모 프로세스가 먼저 종료되거나 자식 프로세스가 비 정상적으로 종료되면 종료 후에도 사용하던 자원이 그대로 남아 있는데 이러한 프로세스를 고아 프로세스라고 함
운영체제는 코드와 데이터를 메모리에 가져와 PCB를 생성하고 프로세스를 준비 큐에 대기시킴. 프로세스가 생성되면 CPU 스케줄러는 프로세스가 해야 할 일을 CPU에 전달하고 CPU는 작업을 수행함. 이 때 CPU 스케줄러가 CPU에 전달하는 일 하나를 스레드라고 함.
스레드
프로세스의 코드에 정의된 절차에 따라 CPU에 작업 요청을 하는 실행 단위
멀티 스레드
멀티스레드는 프로세스 내 작업을 여러 개의 스레드로 분할함으로써 작업의 부담을 줄임
멀티태스킹
멀티태스킹은 운영체제가 CPU에 작업을 줄 때 시간을 잘게 나누어 배분하는 기법(시분할 시스템)
멀티프로세싱
멀티프로세싱은 CPU를 여러개 사용하여 여러 개의 스레드를 동시에 처리하는 작업 환경. 하나의 컴퓨터에 여러 개의 CPU 혹은 하나의 CPU에 여러 개의 코어에 스레드를 배정하는 방식
CPU 멀티스레드
한번에 하나씩 처리해야 하는 스레드를 파이프라인 기법을 이용하여 여러 스레드를 처리하도록 만든 병렬 처리 기법
멀티스레드 vs CPU 멀티스레드
멀티스레드: 운영체제가 소프트웨어적으로 프로세스를 작은 단위의 스레드로 분할하여 운영
CPU 멀티스레드 : 하드웨어적인 설계를 통해 CPU에서 여러 스레드를 동시에 처리하는 병렬 처리 기법
보통의 프로그래밍 언어는 순차적으로 실행되기 때문에 여러 개의 작업을 동시에 처리하기 위하여 fork나 exec를 사용하였음. 그러나 fork는 낭비적인 요소가 많은데 코드 영역과 데이터 영역의 일부가 메모리에 중복되어 존재함.
스레드는 이러한 멀티태스킹의 낭비 요소를 제거하기 위해 사용함. 비슷한 일을 하는 2개의 프로세스를 만들어 메모리 공간을 낭비하는 대신 코드 , 데이터를 공유하면서 여러 개의 일을 프로세스 내에서 처리함
왼쪽 그림처럼 fork를 호출하여 여러 개의 프로세스를 만들면 중복되는 정적 영역이 많아짐. 이처럼 한 프로세스내에서 여러 스레드를 생성하는 멀티스레드는 자원의 낭비를 막는 효과가 있음
비디오 플레이어 프로세스를 예로 들어서 이해해보자. 일반적으로 비디오 플레이어는 재생할 파일을 저장장치로부터 가져오는 입출력 부분과 데이터를 화면에 렌더링하는 부분으로 나뉨. 이러한 기능을 단일 스레드로 구현하면 입출력을 요청한 프로세스는 입출력이 끝날 때까지 대기 상태로 전환됨. 하지만 멀티스레드로 구현한다면 입출력 스레드가 대기상태에 있더라도 재생 스레드는 실행가능함.
멀티스레드의 장점은 다음과 같이 요약할 수 있음
응답성 향상
한 스레드가 다른 작업을 수행중이더라도 다른 스레드가 사용자의 작업 요구에 응답가능
자원 공유
프로세스가 가진 자원을 공유하게 되어 작업을 원할하게 진행 가능
효율성 향상
멀티스레드는 불필요한 자원의 중복을 막음으로써 시스템 효율 향상
다중 CPU 지원
2개 이상의 CPU를 가진 컴퓨터에서 멀티스레드를 사용하면 다중 CPU가 멀티스레드를 동시에 처리하여 프로세스의 처리 시간이 단축됨
멀티스레드에는 단점도 있는데 자원을 공유하기 때문에 하나의 스레드에 문제가 생기면 전체 프로세스에 영향을 미칠 수 있기 때문임. 스레드를 공부하며 대표적으로 많이 듣는 예시인 브라우저를 예로 들어 설명하면 멀티스레드로 구현된 IE에서는 하나의 탭에서 문제를 일으키면 프로세스 전체가 종료되는 경우가 많음. 크롬의 경우 하나의 탭을 독립적인 프로세스로 설계하여 다른 프로세스에 미치는 영향이 상대적으로 적음. 과거와 달리 요즘은 메모리가 넉넉하고 멀티코어 CPU가 대중화되어 여러 개의 프로세스를 CPU에서 동시에 실행하는데 큰 무리가 없기 때문
프로세스는 커널 프로세스
와 사용자 프로세스
로 나뉘며 스레드에도 커널 스레드
와 사용자 스레드
가 있음
사용자 스레드가 커널 스레드를 사용하려면 시스템 호출로 커널 기능을 이용해야함. 이떄 커널 스레드와 사용자 스레드의 대응 방식에 따라 다음과 같이 분류함
사용자 스레드는 운영체제가 멀티스레드를 지원하지 않을 때 사용하는 방법임. 이 스레드는 사용자 레벨에서 스레드를 구현하기 때문에 관련 라이브러리를 사용하여 구현하며, 라이브러리가 커널이 지원하는 스케줄링이나 동기화 같은 기능을 대신 구현해줌(커널 입장에서 해당 스레드는 하나의 프로세스처럼 보임). 사용자 프로세스 내에서 여러 개의 스레드가 존재하지만 커널의 스레드 하나와 연결되기 때문에 1 to N 모델이라고 부름
사용자 스레드는 라이브러리가 직접 스케줄링하고 처리하기 때문에 문맥 교환이 필요 없음 사용자 스레드는 같은 브라우저에서 탭을 나누는 수준의 작업이기 때문에 문맥 교환 없이 적절한 값만 저장하고 복귀시키면 되기 때문임
사용자 스레드의 단점은 여러 개의 스레드가 하나의 커널 스레드와 연결되기 때문에 커널 스레드가 대기 상태에 들어가면 모든 사용자 스레드가 같이 대기하게 됨 ( 커널 스레드 입장에서는 하나의 프로세스이기 때문에 개별 관리가 불가능) 또한 타임 슬라이스를 여러 스레드가 공유하기 때문에 여러 CPU를 동시에 사용할 수 없음. 마지막으로 보안에 취약한데 커널 레벨에서는 공유 변수를 보호하는 장치가 있으나 이러한 서비스를 제공받지 못하기 때문
커널 스레드는 커널이 멀티스레드를 지원하는 방식으로 하나의 사용자 스레드가 하나의 커널 스레드와 연결되기 때문에 1 to 1 모델 이라고 부름
장단점은 사용자 스레드와 정반대임. 커널 스레드는 커널 레벨에서 모든 작업을 지원하기 때문에 멀티 CPU를 지원하고 하나의 스레드가 대기 상태에 있어도 다른 스레드는 작업을 계속할 수 있다. 또한 커널의 기능을 사용하므로 보안에 강하고 안정적으로 작동함. 하지만 문맥 교환시 오버헤드 때문에 느리게 작동함
사용자 스레드와 커널 스레드를 혼합한 방식으로 M to N 모델 이라고 부름
멀티레벨 스레드는 사용자 스레드와 커널 스레드의 장단점을 모두 갖고 있음. 하나의 커널 스레드가 대기 상태에 들어가면 다른 커널 스레드가 유연하게 작업을 처리 가능함. 하지만 마찬가지로 문맥 교환시 오버헤드가 있어 사용자 스레드만큼 빠르지는 않음. 따라서 빠르게 움직여야 하는 스레드는 사용자 스레드로, 안정적으로 움직여야 하는 스레드는 커널 스레드로 작동함