게임이나 어떤 프로그램을 실행하다가 해당 프로그램이 멈추거나 의도대로 동작하지 않을때
Ctrl+Shift+Esc(Ctrl+Alt+Del가 더 익숙하신 분들도 있겠죠)를 눌러 작업 관리자를 통해 해당 프로그램을 종료해본 경험이 있을겁니다.
작업관리자를 통해 크롬을 강제종료 하면 실행중이던 크롬이 종료되고 이는 다른 프로그램도 마찬가지죠
여기서 작업관리자의 탭을 잘 보면 '프로세스'가 선택된 걸 볼 수 있습니다.
그렇다면 앞에서 프로그램을 강제종료했던 것은 '프로세스'를 강제종료하는 과정이라고 볼 수 있겠네요!
앞의 내용들을 토대로 프로세스가 무엇인지 알 수 있을 것 같습니다.
프로세스는 실행중인 프로그램을 의미한다.
여기서 실행중이라는건 어떤 의미 일까요?
프로그램이 실행되기 전에는 SSD, HDD등의 보조기억 장치에서 실행파일의 형태로 존재합니다.
마우스를 통해 클릭을 하거나 커맨드라인을 통해서 프로그램을 실행하면 해당 프로그램의 코드 및
정적 data가 주기억장치(Main Memory, RAM)에 적재되어 CPU가 처리할 수 있는 상태가 되면
프로그램이 실행중이라 말할 수 있고 프로세스가 생성된 것입니다.
공부에 비유한다면 필통에서 공부를 위한 필기구들을 꺼내놓은 상태라고 볼 수 있을 듯 합니다.
그럼 여기서 앞의 내용을 정리해 하나의 키워드를 추가할 수 있을 것 같습니다.
프로세스는 실행의 주체이다.
프로세스들은 주기억장치에 적재되어서 CPU가 처리를 합니다.
관점을 조금만 다르게 본다면 프로세스가 주기억장치와 CPU를 사용한다고 볼 수 있지 않을까요?
다시 말하면 프로세스는 메모리와 CPU등의 자원을 할당 받아서 사용합니다. 물론 이런 과정은 운영체제를 거쳐서 이루어집니다.
정확히 말하면 운영체제가 하는 것이 아니라 프로세스가 운영체제의 코드를 실행하여 직접 자원을 할당받습니다. 여기서 또 하나의 키워드를 추가할 수 있을 것 같습니다.
프로세스는 자원할당의 주체이다.
일반적으로 사용하는 싱글 프로세서 컴퓨터에서 여러 프로세스들이 병렬로 처리되는 것 처럼 보이지만 사실은 하나씩 아주 빠른 속도로 번갈아가며 처리되고 있습니다. 어떻게 이런 일이 가능할까요?
일반 적으로 입출력 작업을 할 때는 상대적으로 많은 대기시간이 필요합니다.
이를 기다리는 것은 비싼 자원인 CPU를 낭비하는 것이죠.
사용자의 입장에서는 컴퓨터가 잠시동안 멈춘것처럼 보일 수 있습니다.
그래서 입출력을 기다리지 않는방식이 등장했습니다. 대기시간에 다른 작업들을 수행할 수 있도록 하는 다중 프로그래밍(multi-programming)방식입니다.
CPU가 입출력이 필요한 작업을 만나서 다른 작업으로 전환할 때 아무작업을 선택해도 될까요?
물론 정해진 작업(프로세스)이 정해져 있습니다. 이런 작업을 지정하는 작업을 '스케줄링'이라 부릅니다.
스케줄링에 대해서 알아보기 전에 먼저 프로세스의 '상태'에 대해서 알아봅시다.
사람에게도 부재중, 회의중, 업무중 등 많은 상태가 있습니다.
프로세스 역시 '상태'를 가집니다. 간단하게 살펴보자면
Dispathcer는 Ready상태 프로세스중 하나를 Running상태로 바꿔줍니다. (CPU의 제어권을 넘깁니다.)
Suspend 상태를 포함한 상태도
위의 그림에서 New와 Exit을 제외한 상태에서 Queue(줄을 서는 형태와 비슷한 자료구조)를 가지며
OS나 사용자에 의해 각 상태에서 이동할 수 있습니다.
스케줄링은 많은 방법들이있고 운영체제마다 방법이 다르기 때문에 나중에 따로 설명하는 글을 적겠습니다.
Linux의 경우 부팅 절차가 끝나면 최초의 프로세스(init 프로세스)가 생성됩니다.
init 프로세스는 모든 프로세스의 조상입니다.
사용자가 프로세스를 만들어달라고 요청하면 기존의 부모 프로세스가 시스템 호출을 통하여 자식 프로세스를 만들어달라고 요청합니다.
Window의 경우 GUI를 통해 아이콘을 더블클릭하면 아이콘에 저장된 실행파일의 이름을 실행해 달라는 시스템 호출을 합니다. (이 경우 GUI가 부모, 실행된 프로세스가 자식입니다.)
프로세스의 종료는 자발적인 방식과 비자발적인 방식이 있습니다.
먼저 자발적인 방식은 exit시스템 호출입니다.
이는 운영체제에게 시스템 호출을 통해서 자발적으로 프로세스를 종료합니다.
또한 exit시스템 호출 시 전달되는 인자를 통해서 정상종료인지 비정상 종료인지 확인 할 수 있습니다.
Linux 에서 exit호출 후 완전 종료가 되기까지의 상태를 zombie process라 부릅니다.
C에서 명시적으로 exit()를 쓰는 경우도 있고 아닌 경우도 있지만 내부적으로 main()이 종료되면 exit시스템 호출을 합니다.
다음으로 비자발적인 방식이 존재하고 크게 2가지로 나뉩니다.
유닉스 계열에서 kill 명령어를 통해서 signal(메시지)를 전달합니다.
kill 명령어는 프로세스 종료 메시지를 보내는 것 주된 목적이지만 다른 메시지를 보낼때도 사용됩니다.
수 많은 프로세스들이 실행되고 있을때 이들을 어떻게 구별하고 관리할 수 있을까요?
OS는 PCB(Process Control Block, TCB라고도 불림)를 통해 관리합니다
PCB는 운영체제 내부에 있는 자료구조로 프로세스에 관한 모든 정보를 가지고 있습니다. 보통 3가지 필드로 나뉘며 세부사항은 다음과 같습니다.
Memory management: code, data, stack의 위치
File management : 루트는 어디인지(프로세스마다 루트 디렉토리를 다른곳으로 설정할 수 있습니다), 작업 디렉토리, File discriptor, UID/GID
앞서 말한 내용을 토대로 컴퓨터에서 일어나고 있는 일들을 간략하게 알아봅시다.
쉘에 실행파일 이름 입력 ->
해당 이름을 가진 실행파일 탐색 ->
해당 실행파일에서 code, data를 가져온 뒤 heap, stack 할당 ->
해당 실행 이미지를 메모리에 적재 -> 실행 -> 인터럽트 발생(time out, I/O) ->
현재 실행하던 명령어까지는 종료 ->
PC값을 stack에 push ->
Interrupt Venctor Table에서 해당 Interrupt Service Routine의 시작 주소로 분기(유저모드-> 커널모드) ->
Register 값들을 커널 stack에 push (여기서 커널 stack으로 변하는게 중요) ->
해당 인터럽트 코드 수행 ->
프로세스 스케쥴링을 통한 다음 프로세스 결정 ->
인터럽트 리턴 (문맥전환 or 복구)