본문 바로가기

🌈 CS Requirements/Operating System

운영체제에서 프로세스 작동 원리

프로세스의 작동 원리를 자세하게 알아보기 위해서는 전체적인 OS의 구조를 먼저 알아보겠습니다. 

🌟 운영체제의 구조 

 

운영체제의 구조

운영체제는 사용자가 권한을 가지고 있는 USER MODE와 사용자가 권한을 가지고 있지 않은 KERNAL MODE로 나뉘어 있습니다. 

 

User 프로그램이 동작하기 위해 사용되는 메모리 공간 (code, data, heap, stack) 영역 
응용 프로그램이 사용하는 공간 
Shell 사용자와 컴퓨터 간의 커뮤니케이션을 중개하는 역할  (종류 : GUI / CLI)      ex. Mac - terminal 
응용 프로그램으로 운영체제의 기능과 서비스를 조작할수 있도록 인터페이스 제공 
API/ 라이버리 응용 프로그램을 위한 인터페이스으로 응용 프로그램에 내장된 프로그래밍 코드 형태로 존재함. 
많은 종류의 함수들을 묶어 라이버리 형태로 제공 
System Call 운영체제의 기능을 응용 프로그램에서 사용할 수 있도록 요청에 따라 OS에 접근하게 해주는 인터페이스 
운영체제(커널)의 기능을 호출하는 역할을 하는 함수 
Kernal 특권 명령어 실행과 원하는 작업 수행을 위한 자원 접근을 가능하게 하는 모드 
OS 컴퓨터 시스템들의 자원들을 효율적으로 관리하기 위해 환경을 제공해 주는 여러 프로그램의 모임. 
대표적 : window / unix, linux 

 

사용자 모드 vs. 커널 모드 

 

🌟 프로세스가 작동하는 방법 

 

[1] 사용자 모드에서 프로그램 실행 

 : 해당 코드를 컴파일하게 되면 실행파일이 생성됩니다. 실행 파일을 사용자 (user)가 실행하고 싶다면 쉘을 통해 GUI 혹은 CLI 방식으로 운영체제에 실행파일을 실행해 달라고 요청하게 됩니다. 

 

그러면 (실행파일) 프로세스는 운용체제 메모리 공간에 TEXT, DATA, HEAP, STACK 영역으로 구성되어 할당되고 위의 코드는 모두 TEXT 영역에 일단 로드 되게 됩니다. 

 

Stack - 매개변수 , 지역변수 

Heap - 동적 할당

Data - 정적 변수, 전역 변수

Text - 실행할 프로그램의 코드

 

 

 

 

이때, 실행과 동시에 코드가 실행되는것이 아니고 생명주기에 의해 5가지 상태로 관리 되고 스케쥴러 방식으로 자신의 차례인지를 확인하여 상태 변화가 이뤄집니다. 

기본적인 설정으로 실행파일이 생성 (new) 되면 해당 프로세스는 ready 상태로 들어가 있을 것입니다. Running 상태로 바꿔주기 위해서는 scheduling 정책에 따라 어떻게 하드웨어로 부터 일정 시간마다 타이머 인터럽트를 운영체제에 알려줄지 정해줍니다. (ex. 비선점, 선점)

 

ready 상태였던 프로세스가 스케쥴링에 의해 본인의 차례가 되었다면 인터럽트는 사용자 모드를 커널 모드로 바꾸어주고 IDT (Interrupt Descriptor Table) 에서 타이머 인터럽트 번호에 매핑되어있는 시스템 콜에 해당하는 주소(함수)를 찾아서 실행해 줍니다.

그러면 아까 ready 상태였던 프로세스는 running 상태에 들어가게 됩니다.  그러고 중간 중간 해당 스케쥴링 정책에 따라 다시 ready에서 running 상태로 context switching 이 발생할 수 있습니다. 

 

여기서 context switching 이란 ??

기존에 running 상태로 들어가 있는 프로세스의 PCB (Program Counter + Stack Pointer) 정보를 메인 메모리에 저장하고 ready 상태로 바꿔주고 새로 들어오는 프로세스의 PCB (PC/SP) 정보가 new/ ready 일 때 그 정보를 CPU에 넣어두고 이 프로세스에 TEXT에 있는 코드를 실행합니다. 

 

[2] open() 시스템 콜 호출

open() 함수에 대한 정보는 파일의 헤더인 #include <unistd.h>에 open 함수를 구현할 수 있는 라이버리가 만들어져 있어 open 함수를 열어보면 시스템 콜 번호와 인자에 대한 주소 정보와 CPU가 제공하는 인터럽트 주소를 전송합니다. 

 

-- 여기서부터 인터럽트가 하는 일입니다.

 

[3] 커널 모드 전환 

다시! 인터럽트가 하는 역할은 사용자 모드를 커널 모드로 바꾸어주고 IDT(OS가 실행할 때 이미 생성)에서 매핑되어있는 해당 시스템 콜 주소를 찾아 실행합니다.

 

 

[4] open() 함수 처리하는 커널 함수 호출

system_call() 함수에서 eax로부터 시스템 콜 처리 함수 번호를 찾아서 해당 함수로 이동합니다. 

 

 

[5] 파일 열기 연산 수행 

: CPU에서의 호출을 DMA (Direct Memory Access)라는 저장 매체를 통해서 SSD에서 text 파일을 불러와 전달 처리를 부탁합니다. 

DMA의 역할은, 하드웨어 하위 시스템이 CPU와 독립적으로 메인 시스템 메모리에 접근할 수 있게 해주는 시스템의 기능입니다. 

 

CPU -> DMA 

: 해당 저장매체 처리 부탁 

DMA -> SSD 

: SSD를 통해 해당 text 파일을 불러와 DMA로 전달 

 

 

CPU 가 직접 SSD 처리를 하지 않고 DMA에게 처리를 부탁하는 이유는 저장매체를 처리하는것은 시간이 오래 걸리기 때문에 그동안 CPU 가 다음 코드를 실행시킬수 없기 때문에 DMA 에게 처리를 넘겨 DMA 가 sys_open()을 실행하는 동안 프로세스는 waiting 상태로 넘어가게 되고 CPU는 다른 프로세스 처리를 합니다. 

 

[6]  사용자 모드로 전환 

DMA 가 데이터를 다 받아와서 CPU에게 처리가 다 되었다는 시그널을 보내게 되면 이때 다시 인터럽트 발생으로 IDT에서 waiting -> ready 상태로 넘겨주는 시스템 콜이 매핑된 주소를 찾아 waiting에 있는 프로세스를 ready로 바꾸어 주는 역할을 실행합니다. 

해당 시스템 콜 함수 실행 후, 다시 커널 모드에서 사용자 모드로 변경됩니다. 

 

--- 인터럽트 역할 끝! 

 

[7] open() 함수 이후의 프로그램을 계속해서 실행 

그러면 다시 운영 정책의 타이머 인터럽트에 따라 ready 상태 프로세스가 running 될 것이고 다음 줄을 실행할 수 있다. 

 

 

그리고 모든 코드가 실핼되면 terminate로 프로세스의 생명 주기가 끝났으므로 종료됩니다. 

 

 

🌈 사용자 모드 -> 커널 모드 

인터럽트 시, 시스템 콜 처리를 위해서도 커널 모드로 빈번히 바뀌어야 합니다.

IO 처리는 수많은 인터럽트와 관련 시스템 콜, 스케쥴러 도움도 필요하고 하드웨어도 필요하고 시간도 많이 걸리기 때문에

가장 효율적으로 처리하는 방법은 IO 처리 시에는 외부 하드웨어 접근을 최소화시키는 것이 중요합니다. 

빈번한 IO 처리는 오랜 시간이 필요합니다. 웬만하면 한방에 데이터를 읽어서 메모리에서 넣어 처리를 하는 방법이 가장 효율적입니다.