우리가 사용하는 컴퓨터 프로그램의 내부 구조는 함수들로 구성된다. 하나의 함수가 수행되는 중에 다른 함수를 호출하고, 호출된 함수의 수행이 끝나면 다시 원래 호출했던 함수의 위치로 돌아가 프로그램을 계속 실행하게 된다.
프로그램이 CPU에서 명령을 수행하려면 해당 명령을 담은 프로그램의 주소 영역이 메모리에 올라가있어야 한다. 프로그램의 주소 영역은 크게 코드(code)
, 데이터(data)
, 스택(stack)
영역으로 구분된다.
code
: 작성한 프로그램 함수들의 코드가 CPU에서 수행할 수 있는 기계어 명령 형태로 변환되어 저장되는 부분data
: 전역변수(global variable) 등 프로그램이 사용하는 데이터를 저장하는 부분stack
: 함수가 호출될때 호출된 함수의 수행을 마치고 복귀할 주소 및 데이터를 임시로 저장하는 데에 사용되는 공간
함수가 호출되면 다음에 실행할 명령(instruction)의 메모리 위치가 바뀌게 된다. 이는 CPU가 명령을 순차적으로 수행하다가 호출된 함수의 위치로 점프해서 새로운 위치의 명령을 실행하기 때문이다.
인터럽트의 동작 원리 도 함수의 호출과 비슷하다.
A 라는 프로그램이 CPU를 할당받고 명령을 수행하고 있는데,
인터럽트
가 발생하면,
➡ A는 현재 수행 중인 명령의 위치를 저장해놓는다. ✅ ➡ 운영체제 내부 코드인 인터럽트 처리루틴으로 넘어가서 인터럽트를 처리한다. ➡ 다시 돌아와서 A의 이전 작업 지점부터 수행을 계속 이어가게 된다.
✅ 인터럽트 때문에 CPU를 빼앗긴 위치는 운영체제가 관리하는
프로세스 제어블록 (Process Control Block)
에 저장된다. PCB에는 인터럽트가 발생한 시점에서 그 프로그램의 어느 부분까지 수행했는지를 저장하며, 인터럽트 처리 후 프로세스 제어블록에 저장된 주소를 복원시켜 원래 수행하던 일을 재개하게 된다.
CPU는 매 시점 메모리의 특정 주소에 존재하는 명령을 하나씩 읽어와 그대로 실행한다. CPU가 수행해야 할 메모리 주소를 담고 있는 레지스터를 프로그램 카운터(program counter: PC)
라고 부른다.
하지만, 컴퓨터 시스템의 동작에는 CPU에서 명령을 수행하는 부분뿐만 아니라 컴퓨터 외부장치와 입출력이 이루어지는 부분도 있기 때문에, 총체적인 이해를 위해서는 컴퓨터 시스템의 구성에 대한 공부가 필요하다.
메모리에는 사용자 프로그램과 운영체제가 같이 올라가 수행된다. 이 때, CPU는 프로그램 카운터(PC)가 가리키는 메모리 위치의 프로그램을 수행한다.
=> 프로그램 카운터가
i ) 운영체제가 존재하는 메모리주소를 가리킴
현재 운영체제의 코드를 수행중이며, 이 경우 CPU가 커널모드(kernel)에서 수행 중이라고 이야기한다.
ii) 사용자 프로그램이 존재하는 메모리 위치를 가리킴
해당 메모리 위치에 올라가 있는 사용자 프로그램이 수행중이며, 사용자모드(user mode)에서 CPU가 수행되고 있다고 이야기한다.
사용자 프로그램이 실행되다 보면 일반명령 외에 특권명령의 수행이 필요한 경우가 있는데, 이 때 운영체제에게 특권명령의 대행을 요청, 즉 시스템 콜
을 하게된다.
CPU가 수행하는 명령의 종류 : 일반명령과 특권명령 (3장 참고)
- 일반명령 : 모든 프로그램이 수행할 수 있는 명령 - 특권명령 : 보안이 필요한 명령으로, 운영체제만이 수행할 수 있는 명령
CPU는 프로그램 카운터(PC)가 가리키는 메모리 위치의 명령만 계속 수행하기 때문에 주변장치의 상태를 지속적으로 파악할 수 없다. 따라서 주변장치는 CPU의 도움이 필요한 경우 인터럽트를 사용해 CPU에게 서비스를 요청한다.
주변장치는 인터럽트 라인(interrupt line)
을 세팅하고, CPU는 매번 명령을 수행한 직후 인터럽트 라인을 체크해 서비스 요청이 들어왔는지 확인한다.
프로그램 실행(program execution) 되고 있다
가 갖는 중요한 의미
- 디스크에 존재하던 실행파일이 메모리에 적재된다.
- 프로그램이 CPU를 할당받고 명령을 수행하고 있는 상태이다.
실행파일이 메모리에 적재될 때, 실행파일 전체가 메모리에 한꺼번에 올라가기보다는 일부분만 메모리에 올라가고 나머지는 디스크의 특정 영역에 내려가있는 것이 일반적이다. (여러 프로그램이 함께 사용하는 메모리 공간의 효율적인 사용을 위해)
프로그램의 주소 공간 중 당장 CPU의 수행에 필요한 부분은 메모리에 올려놓고, 그렇지 않은 부분은 디스크 중 메모리의 연장 공간으로 사용되는 스왑영역(swap area) 에 내려놓는 방식으로 운영된다.
- 프로세스의 주소 공간은 code, data, stack 등으로 구성된다.
각각의 프로그램마다 이러한 주소 공간을 별도로 가지며, 우리는 이를
가상메모리(virtual memory)
,논리적 메모리(logical memory)
라고 부른다. 이는 실제 물리적 메모리의 주소와 독립적으로 각 프로그램마다 독자적인 주소 공간을 가지기 때문에 지칭하는 용어이다.
운영체제도 하나의 프로그램이므로, 운영체제 커널 역시 code, data, stack의 주소 공간 구성을 가지고 있다.
-
code
운영체제의 기능을 알고있다면 예상할 수 있듯이, 커널의 code는 CPU, 메모리 등의 자원을 관리하기 위한 부분과 사용자에게 편리한 인터페이스를 제공하기 위한 부분이 주를 이루고 있다. (+ 시스템 콜, 인터럽트 처리하기 위한 부분도 포함한다) -
data
각종 자원을 관리하기 위한 자료구조가 각각 저장된다.
➡ 하드웨어 자원(CPU, 메모리 등)을 관리하기 위한 자료구조
➡ 현재 수행 중인 프로그램(프로세스)을 관리하기 위한 자료구조 :PCB
각 프로세스의 상태, CPU 사용정보, 메모리 사용 정보 등을 유지한다. -
stack
(일반 프로그램의 스택 영역과 마찬가지로) 함수호출시의 복귀 주소를 저장한다. 하지만 커널의 스택은 일반 사용자 프로그램의 스택과 달리, 현재 수행 중인 프로세스마다 별도의 스택을 두어 관리한다.
Q. 프로그램 A의 stack VS 프로세스 A의 커널 stack ?
프로세스가 함수를 호출할 때 자기 주소 영역 내부에 정의된 함수를 호출하면 자신의 스택(전자)에 복귀 주소를 저정하지만, 프로세스가 특권 명령을 수행하려고 커널에 정의된 시스템 콜을 호출하고 시스템 콜 내부에서 다른 함수를 호출하는 경우, 그 복귀 주소는 커널 내의 주소가 되어 사용자 프로그램의 스택과는 별도의 저장공간(후자)이 필요하다.
즉, 프로그램이 자기 자신의 코드 내에서 함수호출 및 복귀 주소를 유지하기 위해서는 자기 주소 공간 내의 스택을 사용하고, 시스템 콜
이나 인터럽트
등으로 운영체제의 코드가 실행되는 중에 함수호출이 발생할 경우 커널 스택을 사용하게 되는 것이다.
➡ 정확히 알아두어야 할 부분
시스템 콜이나 인터럽트 발생으로 CPU의 수행 주체가 운영체제로 바뀌는 순간에는 직전에 수행되던 프로그램의 복귀 정보를 스택이 아닌 (data 영역의) PCB
에 저장한다!! 이 때, 커널 역시 함수 구조로 이루어져 있으므로, 커널의 코드가 수행되는 도중에 이루어지는 함수호출은 커널스택을 사용하게 되는 것이다.