프로세스는 실행 중인 프로그램에 대한 운영체제의 추상화다. 프로세스는 동시에(concurrently) 실행될 수 있으며, 한 프로세스의 인스트럭션들이 다른 프로세스의 인스트럭션들과 섞일 수 있다.
이는 한 개의 CPU가 다수의 프로세스를 동시에 실행하는 것처럼 보이게 해준다.
운영체제는 문맥 전환(context switching)이라는 방법을 사용해서 이러한 교차실행을 수행한다.
운영체제는 프로세스가 실행하는 데 필요한 모든 상태정보의 변화를 추적한다. 이 컨텍스트라고 부르는 상태정보는 PC, 레지스터 파일, 메인 메모리의 현재 값을 포함하고 있다.
운영체제는 현재 프로세스에서 다른 새로운 프로세스로 제어를 옮기려고 할 때 현재 프로세스의 컨텍스트를 저장하고 새 프로세스의 컨텍스트를 복원시키는 문맥전환을 실행하여 제어권을 새 프로세스로 넘겨준다.
위 그림은 리눅스 환경에서 쉘(bash.sh)이 실행되고 있을 때, 응용 프로그램(hello.bin)이 실행됐을 때의 예시다. 응용 프로그램이 운영체제에 의한 어떤 작업을 요청하면, 컴퓨터는 파일 읽기나 쓰기와 같은 특정 시스템 콜(system call)을 실행해서 커널에 제어를 넘겨준다. 그러면 커널은 요청된 작업을 수행하고 응용프로그램으로 리턴한다.
단, 커널은 별도의 프로세스가 아니다. 모든 프로세스를 관리하기 위해 시스템이 이용하는 코드와 자료구조의 집합이다. 이처럼 하나의 프로세스에서 다른 프로세스로의 전환은 커널(Kernel)에 의해 관리된다. 커널은 운영체제 코드의 일부분으로 메모리에 상주한다.
쓰레드(Thread)는 다수의 실행 유닛으로 구성되어 있으며, 각각의 쓰레드는 해당 프로세스의 컨텍스트에서 실행되며 동일한 코드와 전역 데이터를 공유한다. 다수의 프로세스들에서보다 데이터의 공유가 더 쉽고, 프로세스보다 더 효율적이라는 장점을 가지고 있다.
가상 메모리는 각 프로세스들이 메인 메모리 전체를 독점적으로 사용하고 있는 것 같은 환상을 제공하는 추상화이다. 각 프로세스는 가상주소 공간이라고 하는 균일한 메모리의 모습을 갖게 된다. 아래 그림을 보자.
<그림 1.13> 리눅스 프로세스 가상주소공간(각 영역은 크기에 맞춰서 그려진 것이 아님)
주소공간의 최상위 영역은 모든 프로세스들이 공통으로 사용하는 운영체제의 코드와 데이터를 위한 공간이다. 주소공간의 하위 영역은 사용자의 프로세스의 코드와 데이터를 저장한다. (참고: 그림에서 위쪽으로 갈수록 주소가 증가한다.)
프로그램 코드와 데이터 : 코드는 모든 프로세스들이 같은 고정 주소에서 시작하며, 다음에 C 전역변수에 대응되는 데이터 위치들이 따라온다.
힙(Heap) : 힙은 프로세스가 실행되면서 C 표준함수인 malloc
이나 free
를 호출하면서 런타임에 동적으로 그 크기가 늘었다 줄었다 한다.
공유 라이브러리 : 주소공간의 중간 부근에 위치하며 공유 라이브러리의 코드와 데이터를 저장하는 영역이다.
스택(Stack) : 사용자가 가상메모리 공간의 맨 위에 컴파일러 함수 호출을 구현하기 위해 사용하며, 힙처럼 프로그램이 실행되는 동안에 동적으로 늘어났다 줄어들었다 한다. 특히, 함수를 호출할 때마다 스택이 커지며, 함수에서 리턴될 때는 줄어든다.
커널 가상메모리 : 주소공간의 맨 윗부분은 커널을 위해 예약되어 있다. 이 영역의 내용을 읽거나 쓰는 것이 금지되어 있다. 대신, 커널을 호출하면 된다.
파일(file)은 그저 연속된 바이트들이다. 시스템의 모든 입출력은 유닉스 I/O라는 시스템 콜들을 이용하여 파일을 읽고 쓰는 형태로 이루어진다.
출처 : Computer Systems A Programmer's Perspective(3rd Edition)