실행중인 프로그램의 동의어이다. 우리가 더블 클릭이던 어떤 방식으로 프로그램을 실행했을 때 프로그램이 메모리 위에 올라가고 프로그램이 실행된다. 프로그램은 실행중인 프로그램(프로세스)상태가 된다.
프로세스는 포그라운드 프로세스
(foreground process : 사용자가 볼 수 있는 공간에서의 프로세스), 백그라운드 프로세스
(background process : 볼수 없는 공간에서..)로 나뉜다.
백그라운드 프로세스는 또 다시 직접 상호작용 가능한 백그라운드 프로세스와 그렇지 않고 정해진 일만 수행하는 프로세스(데몬
, 서비스
)로 나뉜다.
모든 프로세스는 실행을 위해 CPU가 필요하지만 CPU의 자원은 한정되어있다. 프로세스는 한정된 시간 만큼만 CPU를 이용하고 타이머 인터럽트(자신의 차례가 끝)가 발생하면 차례를 양보한다.
여기서 언급된 시간에 대한 관리는 운영체제의 CPU스케줄링을 통해 관리된다.
빠르게 번갈아 수행되는 프로세스들을 관리하기 위해 사용되는 자료구조를 프로세스 제어블록(PCB)
라고 하며 프로세스 관련 정보들을 담고 있고, 프로세스 생성 시 커널 영역에 생성되고 종료시 폐기된다.
즉 하나의 제어블록이 아니라 프로세스에 달려있는 신분증같은 것이라고 생각할 수 있다.
PCB에는 다음과 같은 정보들이 담긴다.
프로세스 ID(PID)
: 프로세스 식별을 위한 Unique value레지스터 값
: 프로세스들은 CPU를 이용해야하므로 CPU내부의 여러 레지스터들을 활용한다. 활용하다가 타이머 인터럽트에 의해 자신의 차례가 끝나게 되면 다시 CPU를 이용하기 위해 레지스터값들을 기억하다가 차례가 오면 이를 활용하기 위함이다.프로세스 상태
: CPU를 사용하기 위해 기다리는 상태, CPU를 이용 중인 상태, 입출력 장치의 사용을 기다리는 상태... 여러 상태 값들이 담긴다.CPU 스케줄링 정보
: 프로세스가 언제, 어떤 순서로 CPU를 할당 받을지에 대한 정보메모리 정보
: 프로세스가 메모리의 어느 주소에 저장되어 있는지에 대한 정보, 페이지 테이블 정보사용한 파일과 I/O장치 정보
: 할당된 입출력장치, 사용중인 파일 정보프로세스A 에서 다음 프로세스인 프로세스B로 타이머 인터럽트에 의해 실행 순서가 넘어간다면, 프로세스A는 지금까지의 중간 정보를 백업한다. 이 중간 정보를 문맥(context)
라고 한다. 이는 다음 실행을 재개하기 위한 정보이며 실행 문맥을 백업해두면 언제든 해당 프로세스의 실행을 재개할 수 있다.
기존의 실행 중이던 프로세스의 문맥을 백업하고, 새로운 프로세스 실행을 위해 새로운 프로세스의 문맥을 복구하는 과정을 context switch 문맥교환
이라고 한다. 여러 프로세스가 이 과정을 반복하며 CPU자원 아래에서 빠르게 번갈아 실행된다.
PCB는 프로세스의 신분증 같은 역할을 하며 여러 정보를 담고 있고 "커널 영역"에 존재한다고 했다. 그렇다면 사용자 영역에서는 어떤 일이 발생하고 있을까.
사용자영역은 스택영역, 힙 영역, 데이터 영역, 코드 영역으로 나뉜다. JVM의 메모리에서의 움직임을 공부했다면 단어들이 매우 익숙하다.
코드 영역(텍스트 영역)은 실행 가능한 코드 즉 기계어로 이루어진 명령어들이 저장된다. CPU가 실행할 명령어가 담기는 공간으로, 프로그램을 이루는 명령어가 갑자기 바뀔 일은 없어야 하므로 쓰기가 금지된 영역이다(read-only).
잠깐 썼다가 없앨 데이터가 아닌 프로그램이 실행되는 동안 유지할 데이터가 저장된다. JVM의 static함수, 전역변수가 담기는 클래스 영역과 굉장히 비슷한 개념으로 들린다.
프로그램을 만드는 사용자가 직접 할당할 수 있는 저장공간이다. 힙 영역에 메모리 공간을 할당했다면 언젠가는 이를 반환해야한다. JVM에서도 객체가 담기는 영역으로, 객체의 사용이 끝났다면 GC에 의해 폐기된다. 반환하지 못할 경우 메모리 누수(memory leak)이 발생가능하다. 실제로 C의 경우 GC의 기능을 가지지 못해 프로그래머가 직접 자원을 해제해야한다.
데이터가 일시적으로 저장되는 공간으로 매개 변수, 지역 변수에 해당한다. 스택 영역과 힙 영역은 프로그래머에 의해 언제든지 동적으로 크기가 변할 수 있기에 "동적 할당 영역"이라고도 한다.
동적 할당 영역에 해당하는 힙, 스택 영역은 메모리에서 각각 오름차순, 내림차순의 주소 할당을 받는다. 양쪽 끝에서 늘어나서 주소 겹침이 최소화되도록 하는 것이 목적이다.
생성 상태
: 이제 막 메모리에 적재되어 PCB를 할당받은 상태준비 상태
: 당장 실행 가능한 상태이지만, 자신의 차례가 아니므로 기다리는 상태, 차례가 오면 실행상태로(dispatch)실행 상태
: CPU를 할당 받아 실행 중인 상태, 타이머 인터럽트 발생시 준비 상태로, 실행 도중 입출력장치 사용시 입출력 작업이 끝날 때 까지 대기상태로 머문다. 대기 상태
: 프로세스가 실행 도중 입출력 장치를 사용할 경우 대기상태가 되며 작업이 끝나면 준비 상태로종료 상태
: 프로세스 종료, PCB, 메모리 영역 정리이때 입출력 요청은 실행 상태의 프로세스가 수행하는 요청이다. 그렇다면 다음과 같은 질문이 있을 수 있다. 실행상태에 들어가자마자 입출력 요청에 의해 대기상태로 가버린다면 해당 프로세스는 다른 프로세스들이 줄 서있는 뒤에 다시 줄을 서야하는데 이는 좀 불공평하지 않나?
실제로 입출력 요청에의해 대기-준비로 돌아온 프로세스는 여러 운영체제의 CPU스케줄링 알고리즘에 의해 상황마다 다르게 다시 실행상태로 들어간다. 우선순위 알고리즘이 작용하고, 해당 프로세스가 우선순위가 높아 빠르게 다시 실행상태로 진입할 수도 있을 것이다.
프로세스는 실행 도중 시스템 호출을 통해 다른 프로세스 생성이 가능하다. 새 프로세스를 생성한 프로세스는 부모 프로세스
가 될 것이며 부모에 의해 생성된 프로세스는 자식 프로세스
가 된다.
이러한 프로세스가 프로세스를 생성하는 방식에는 System call에 해당하는 fork
와 exec
이 활용된다.
프로세스가 프로세스를 낳는 구체적 방식은 다음과 같다. 기존의 프로세스(부모 프로세스)는 fork 시스템 콜을 통해 자신과 프로그램 내용이 동일한(PCB는 다르다) 프로세스를 메모리에 생성한다.(복제) 그리고 복제된 프로세스는 exec 시스템 콜을 통해 실행시킬 자식 프로세스를 자신의 메모리 공간에 그대로 입힌다.
쉘 프로그램(bash)은 사용자로부터 명령어를 입력받고, 해당 명령어를 실행하기 위해 새로운 자식 프로세스를 생성한다. 쉘에서 ls 명령어를 수행하는 과정을 생각해보자. 우리는 이전에 ls는 결국 프로그램이라고 배웠다. bash에서의 명령어를 통해 ls를 실행하므로 bash는 부모 프로세스가 될 것이며 ls는 자식 프로세스가 된다.
실제로 이러한 구체적 방식은 window에는 해당사항이 없다. Unix계열의 경우에는 따르므로 mac이나 linux를 사용이 잦은 프로그래머들은 잘 알아두어도 좋을 것 같다.
JobLauncher
(스프링 배치(Spring Batch) 프레임워크의 핵심 인터페이스)를 통해 배치 작업이 실행된다.ProcessBuilder
(외부 명령어 사용 - 운영체제 명령어)를 사용하여 zip 명령어(압축 프로그램 실행)를 실행하거나 python ~ 로 하여금 python 스크립트를 실행할 수도 있다.