운영체제가 제공하는 서비스

운영체제(OS)는 사용자와 하드웨어 사이에서 다양한 서비스를 제공한다. 사용자가 컴퓨터를 편리하게 사용할 수 있도록 인터페이스를 제공하고, 프로그래머가 시스템 자원을 활용할 수 있도록 프로그래밍 인터페이스도 제공한다.
이 글에서는 운영체제의 서비스 구조, 시스템 콜의 동작 방식, 커널의 다양한 설계 방식, 그리고 가상 머신의 개념까지 살펴본다.
사용자 인터페이스
운영체제는 사용자와 소통하기 위해 크게 두 가지 인터페이스를 제공한다.
CLI (Command-Line Interpreter)
사용자가 명령어를 직접 입력하여 실행하는 방식이다. UNIX의 Shell이나 MS-DOS의 Command Prompt가 대표적이다.
GUI (Graphical User Interface)
마우스 기반의 윈도우-메뉴 시스템으로, 데스크톱 메타포(아이콘, 폴더 등)를 통해 시스템을 조작한다. 1973년 Xerox Alto에서 처음 등장했고, 1980년대 Apple Macintosh를 거쳐 MS-Windows, X-window 기반 데스크톱(CDE, KDE, GNOME)까지 발전해왔다.
프로그래밍 인터페이스
운영체제는 프로그램이 하드웨어 자원을 활용할 수 있도록 시스템 콜(System Call) 이라는 프로그래밍 인터페이스를 제공한다. 시스템 콜은 소프트웨어 인터럽트(S/W Interrupt)의 한 종류다.
POSIX I/O 시스템 콜 예시 (unistd.h에 선언):
int open(const char *pathname, int flags, mode_t mode);
int close(int fd);
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);
// size_t: unsigned int, ssize_t: signed int
open() 같은 시스템 콜을 이용해서 C 표준 라이브러리의 fopen()이 구현된다. C 언어 자체에는 없는 OS 고급 기능이 UNIX 시스템 콜에는 존재한다.
시스템 콜의 동작 원리
시스템 콜(System Call) 은 애플리케이션이 OS 커널에 서비스를 요청하는 메커니즘이다. 핵심 특징은 다음과 같다.
- 인터럽트를 통해 커널 함수를 호출하며, user mode에서 kernel mode로 전환된다
- 일반적으로 C/C++ 또는 어셈블리로 작성된 인터럽트 핸들러(Interrupt Handler) 형태로 제공된다
- 낮은 권한 모드에서 높은 권한 모드로 안전하게 제어를 이전하는 메커니즘이다
대표적인 POSIX 시스템 콜: open, close, read, write, fork, kill, wait 등

인터럽트 핸들러와 시스템 콜 핸들러는 약간 다르다. 인터럽트 핸들러가 처리할 때 시스템 콜 핸들러를 호출하는 구조다.
시스템 콜의 매개변수 전달
시스템 콜은 내부적으로 인터럽트를 통해 처리되며, 추가 정보가 필요한 경우 매개변수 전달(Parameter Passing) 방법이 사용된다.
| 방법 | 설명 |
|---|---|
| 레지스터 | 정보의 양이 작을 때 사용 |
| 블록 주소 | 정보의 양이 클 때, 메모리 블록의 주소를 레지스터에 저장 |
| 시스템 스택 | 스택에 매개변수를 push/pop |

시스템 콜의 유형
시스템 콜은 기능에 따라 다섯 가지로 분류된다.
- 프로세스 제어 (Process Control)
- 파일 관리 (File Management)
- 장치 관리 (Device Management)
- 정보 유지 (Information Maintenance)
- 통신 (Communication)

시스템 콜 인터페이스
고급 언어에서 시스템 콜을 어떻게 호출할까? 예를 들어 int open(const char *path, int oflag); 같은 함수를 호출하면, 내부적으로 어떤 일이 일어나는 것일까?
시스템 콜 인터페이스(System-Call Interface) 는 프로그래밍 언어의 런타임 지원 시스템과 OS 시스템 콜 사이의 연결 고리다. glibc(Linux)나 MS libc(Windows)가 이 역할을 담당한다.


일반적으로 각 시스템 콜에는 번호가 부여된다. 시스템 콜의 IRQ 번호는 Linux에서 0x80, DOS/Windows에서 0x21이다. 시스템 콜 인터페이스는 이 번호로 인덱싱된 테이블을 유지한다.
시스템 콜 인터페이스는 OS 커널에서 의도된 시스템 콜을 호출하고, 그 상태와 반환값을 돌려준다. 호출자는 시스템 콜이 어떻게 구현되었는지 알 필요가 없다. API를 준수하고 OS가 무엇을 해줄지만 이해하면 된다. 대부분의 OS 인터페이스 세부사항은 API 뒤에 숨겨져 있으며, 런타임 지원 라이브러리(Run-time Support Library) 가 관리한다.
시스템 콜 인터페이스가 하는 일:
- 커널에 정보를 전달
- 커널 모드로 전환
- 커널 모드 실행을 위한 데이터 처리 및 준비
시스템 콜 vs. 프로그래밍 언어의 I/O 함수:
read(): OS가 제공하는 시스템 콜fread(): C 언어에서 정의된 표준 함수 (호환성을 위해 존재)fread()는 내부적으로read()를 사용하여 구현된다

API (Application Programming Interface)
API는 컴퓨터 시스템(OS), 라이브러리, 애플리케이션이 서비스 요청을 허용하기 위해 제공하는 인터페이스다. 시스템 콜보다 좀 더 큰 단위의 함수로 구성된다.
- 애플리케이션 프로그래머가 사용할 수 있는 함수, 매개변수, 반환값의 집합이다
- 예: Win32 API, POSIX API 등 (
MessageBox(..),CreateWindow(..)등)
- 예: Win32 API, POSIX API 등 (
- 시스템 콜과 밀접한 관계를 가질 수 있다
- 예: POSIX API는 UNIX 시스템 콜과 거의 동일
- 시스템 콜을 기반으로 더 고수준의 기능을 제공하기도 한다
- 예: Win32 API, POSIX 스레드 라이브러리 API

Windows OS에서는 API가 호환성을 크게 높여주지만, Linux에서는 시스템 콜과 API의 차이가 비교적 작다.
프로세스 제어
프로세스의 적재와 실행
하나의 프로그램이 다른 프로그램을 적재(load)하고 실행(execute) 할 수 있다. CLI, Windows 탐색기, macOS Finder가 대표적인 예다.
이때 부모 프로그램은 다음 중 하나의 상태를 가진다.
- 자식 프로그램에 의해 교체됨 (lost)
- 일시 중지됨 (paused)
- 계속 실행됨: 멀티프로그래밍 / 멀티태스킹
- 새 프로세스를 생성하거나 작업을 제출
FreeBSD UNIX 예시

커맨드 인터프리터는 새 프로그램을 실행한 뒤에도 계속 실행될 수 있다. 부모의 실행에는 두 가지 경우가 있다.
Case 1: 계속 실행
새 프로그램이 백그라운드에서 실행된다. 이 경우 콘솔 입력이 불가능하다.
Case 2: 자식 프로세스를 기다림 (wait())
새 프로세스가 I/O를 접근하며, 종료(exit()) 시 상태 코드(0 또는 에러 코드)와 함께 부모(예: 셸)에게 제어권이 돌아간다.
핵심 시스템 콜:
fork(): 반환값이 0이면 자식, 0보다 크면 부모, 0보다 작으면 에러exec()계열 함수execlp: 예)execlp("ls", "ls", "-al", "/tmp", NULL);execvp: 예)execvp(argv[0], argv);
wait(): 자식 종료 시까지 대기. 반환값이 0보다 작으면 에러
프로세스 제어 기능:
- 프로세스 속성 조회/설정 (우선순위, 최대 실행 시간 등)
- 프로세스 종료 (다른 사용자의 프로세스는 종료할 수 없다)
- 일정 시간 대기 또는 이벤트/시그널(타이머, 마우스 등) 대기

디버깅:
- Dump: 메모리 상태 저장
- Trace: 매 명령어 실행 후 트랩

프로세스 종료
프로세스 종료에는 두 가지 방식이 있다.
정상 종료: exit()
프로세스의 자원을 해제하고, 현재 프로세스에 대한 정보를 정리한다.
비정상 종료: abort()
디버깅과 분석을 위해 메모리를 파일에 덤프(Dump) 한다. 메모리 덤프란 컴퓨터 메모리의 내용을 파일로 저장하는 것으로, 프로그램 크래시 등의 원인을 파악하는 데 사용된다. 덤프 파일은 텍스트가 아닌 이진 파일로 저장되며, 전용 도구를 통해 분석한다. 잘못된 메모리 주소 접근 같은 경우에는 사용자에게 처리 방법을 물을 수도 있다.
파일 관리

운영체제의 파일 관리 기능은 다음을 포함한다.
- 파일 생성 / 삭제
- 읽기 / 쓰기 / 위치 변경(reposition)
- 파일 속성 조회 / 설정
- 디렉토리 연산
- 이동(move), 복사(copy) 등의 추가 서비스
이러한 기능은 시스템 콜, API, 또는 독립적인 시스템 프로그램 중 하나의 형태로 제공된다.
장치 관리
운영체제에서 장치는 물리 장치(디스크, 테이프 등)와 추상/가상 장치(파일 등)로 나뉜다.
장치에 대한 기본 연산은 파일 연산과 비슷하다.
| 연산 | 시스템 콜 |
|---|---|
| 독점 사용 요청 | open() |
| 읽기, 쓰기, 위치 변경 | read(), write() |
| 사용 해제 | close() |
운영체제는 파일과 장치를 통합된 구조로 관리한다. I/O를 특수 파일에 매핑하여 파일과 장치 모두에 동일한 시스템 콜을 사용한다. 장치마다 다양한 버전이 필요한 부분은 디바이스 드라이버(Device Driver) 가 제공한다.

정보 유지 (Information Maintenance)
운영체제와 사용자 프로그램 사이에서 다양한 정보를 전달하는 기능이다.
- 현재 시간, 날짜
- 시스템 정보: 현재 사용자 수, OS 버전, 남은 메모리/디스크 공간
OS는 모든 프로세스에 대한 정보를 관리한다. Linux의 /proc 파일시스템이 대표적인 예로, CPU 정보나 메모리 상태 같은 커널 내부 데이터(변수)를 파일처럼 접근할 수 있다.
시스템 프로그램
시스템 프로그램(System Program) 은 프로그램 개발과 실행을 위한 편리한 환경을 제공하는 프로그램이다. user mode에서만 동작하며, 사실상 사용자 애플리케이션이다. 셸, 윈도우 탐색기, 맥 Finder가 대표적이다.

운영체제의 구조
범용 운영체제는 매우 큰 프로그램이다. 이를 어떻게 구조화하느냐에 따라 성능, 안정성, 확장성이 크게 달라진다. 대표적인 구조 방식은 다음과 같다.
- 모놀리식(Monolithic): MS-DOS, 초기 UNIX, Linux
- 계층형(Layered): 추상화 계층 기반
- 마이크로커널(Microkernel): Mach
단순 구조 (Simple Structure)

MS-DOS (1981) 는 단일 사용자, 단일 작업 환경을 위해 설계되었다. 최소한의 공간으로 최대한의 기능을 제공하는 것이 목표였다.
하지만 인터페이스와 기능 수준이 잘 분리되지 않았으며, 다음과 같은 한계가 있었다.
- 하는 일이 많지 않아 이중 모드(Dual Mode) 나 하드웨어 보호가 없음
- 애플리케이션이 I/O에 직접 접근 가능
- 잘못된 프로그램에 취약: 하나의 프로그램 오류가 전체 시스템을 다운시킬 수 있음
- 특정 하드웨어에 종속적
모놀리식 구조 (Monolithic Structure)

모놀리식 커널(Monolithic Kernel) 은 시스템 콜 인터페이스 아래, 물리 하드웨어 위에 있는 모든 것을 하나의 커널에 포함하는 구조다. 파일 시스템, CPU 스케줄링, 메모리 관리 등 모든 기능이 하나의 레벨에 집중된다.
가장 큰 장점은 빠르다는 것이다. 모든 기능이 같은 주소 공간에서 실행되기 때문이다.
- 초기 UNIX (1973): 하드웨어 기능에 제한되었고, 시스템 프로그램으로 셸, 컴파일러, 인터프리터, 시스템 라이브러리 등을 제공
- Linux (1991): 모놀리식 구조를 기반으로 발전
계층형 접근 (Layered Approach)

운영체제를 여러 계층(Layer) 으로 구성하는 방식이다. 각 계층은 추상 객체와 연산을 구현하며, 하위 계층의 함수/서비스만을 사용한다. 상위 계층은 하위 계층을 호출하고, 하위 계층은 상위 계층에 의해 호출된다.
장점: 개발과 디버깅이 단순하다

하위 계층부터 상위 계층으로 개발하면, 각 단계에서 현재 계층에만 집중할 수 있다. 하위 계층의 세부 구현을 알 필요가 없다.
어려움:

다양한 계층을 정의하려면 신중한 설계가 필요하다. 서로를 필요로 하는 모듈 간의 계층 관계를 정의하기 어렵다.

또한 비효율성 문제가 있다. 하위 계층으로의 반복 호출이 오버헤드를 만든다.

해결책: 계층 수를 줄여서 장점은 취하고 단점은 피한다.
마이크로커널 (Microkernel)

마이크로커널은 커널을 최대한 작게 만드는 접근 방식이다. 불필수적인 구성 요소는 커널에 구현하지 않고, 시스템/사용자 수준의 프로그램으로 분리한다. 일반적으로 프로세스 관리, 메모리 관리, 통신 기능만 커널에 포함된다.
시스템 콜은 메시지 패싱(Message Passing) 을 통해 제공된다. 클라이언트와 서비스는 사용자 공간에서 실행되고, 커널은 클라이언트와 서버 사이의 메시지 전달 기능만 제공한다.
장점: 커널의 양이 작아서 문제가 발생할 확률이 낮다 (안전성이 높다)
- 확장 용이: 새로운 서비스를 추가하기 쉽다
- 이식 용이: 다른 하드웨어로 포팅하기 쉽다
- 보안과 신뢰성: 대부분의 서비스가 사용자 공간에서 실행된다
단점:
- 시스템 기능 오버헤드 증가로 인한 성능 저하
커널 모듈 (Loadable Kernel Modules)

적재 가능 커널 모듈(LKM, Loadable Kernel Modules) 은 객체 지향적 접근 방식을 사용한다. 각 핵심 구성 요소가 분리되어 있고, 잘 정의된 인터페이스를 통해 서로 통신한다. 필요에 따라 커널 안에 동적으로 적재/제거할 수 있다. Linux, Solaris 등에서 사용된다.
사용하는 모듈만 끼웠다 뺐다 할 수 있어서 매우 유연(flexible) 하며, 실행 중에도 빠르게 대응할 수 있다.
장점:
- 핵심 서비스를 제공하면서, 특정 기능을 동적으로 구현 가능
계층형 구조와의 비교:
- 더 유연하다. 어떤 모듈이든 다른 어떤 모듈을 호출할 수 있다
마이크로커널과의 비교:

- 각 모듈이 커널 모드에서 실행된다
- 메시지 패싱을 호출할 필요가 없어 빠르다
- 다만 커널 모드에서 실행되므로, 모듈에 문제가 생기면 시스템 전체가 영향을 받을 수 있다 (약간의 위험성)
하이브리드 시스템 (Hybrid Systems)
대부분의 현대 운영체제는 하나의 순수 모델이 아니다. 하이브리드 방식은 여러 접근법을 결합하여 성능, 보안, 유용성을 모두 충족시킨다.
- Linux, Solaris: 커널 주소 공간 안에 모놀리식 구조를 사용하면서, 기능의 동적 로딩을 위해 모듈 방식을 결합
- Windows: 대부분 모놀리식이지만, 서브시스템 지원을 위해 마이크로커널 개념을 결합
- Apple macOS X: 계층형 구조 + Aqua UI + Cocoa 프로그래밍 환경. 커널은 Mach 마이크로커널과 BSD UNIX로 구성되며, I/O Kit과 동적 적재 모듈(커널 익스텐션)을 포함
macOS와 iOS

Apple 운영체제는 네 개의 계층으로 구성된다.
User Experience 계층
사용자가 컴퓨팅 장치와 상호작용하는 소프트웨어 인터페이스를 정의한다. macOS는 Aqua UI, iOS는 Springboard UI를 사용한다.
Application Frameworks 계층
Objective-C와 Swift 프로그래밍 언어를 위한 API를 제공한다. macOS는 Cocoa, iOS는 Cocoa Touch 프레임워크를 사용한다.
Core Frameworks
그래픽과 미디어를 지원하는 프레임워크를 정의한다. QuickTime, OpenGL 등이 포함된다.
Kernel Environment (Darwin): 하이브리드 구조
주로 Mach 마이크로커널과 BSD UNIX 커널로 구성된 계층형 시스템이다.

Android

Android는 Linux 커널 위에 구축된 모바일 운영체제로, 다음과 같은 구성 요소를 가진다.
- ART (Android Run-Time): AOT(Ahead-of-Time) 컴파일을 지원하는 런타임
- JNI (Java Native Interface): Java와 네이티브 코드 간의 인터페이스
- Native Libraries: 네이티브 라이브러리
- HAL (H/W Abstraction Layer): 특정 하드웨어에 독립적인 일관된 뷰를 제공
- Bionic: Android용 표준 C 라이브러리 (glibc의 Android 버전)
가상 머신 (Virtual Machines)
가상 머신(Virtual Machine) 은 컴퓨터 플랫폼과 운영체제 사이에 가상화된 환경을 만드는 소프트웨어다. 사용자는 추상적인 머신 위에서 소프트웨어를 운영할 수 있다.
하나의 컴퓨터 하드웨어를 여러 개의 서로 다른 실행 환경으로 추상화하며, 각 실행 환경은 호스트 컴퓨터를 정확히 에뮬레이트한다.

구현의 문제
가상 머신을 구현하기 위해서는 기반 머신을 정확히 복제해야 하므로 많은 작업이 필요하다. 이중 모드 연산을 지원하기 위해 가상 이중 모드(Virtual Dual Mode) 를 사용한다.
- VM 소프트웨어 자체는 커널 모드에서 실행될 수 있지만, VM 위의 OS는 사용자 모드에서 실행된다
- 가상 사용자 모드 / 가상 커널 모드가 존재한다
- 가상 사용자 모드에서의 시스템 콜은 VM 모니터가 시뮬레이션한다
- 많은 CPU가 2개 이상의 특권 수준을 지원한다

가상 머신의 자원 관리
각 프로세스는 자신만의 CPU와 메모리를 가진 것처럼 보인다. 이는 CPU 스케줄링과 가상 메모리 기술로 구현된다. 가상 메모리는 소프트웨어가 실제 물리 메모리와 반드시 연결되지 않은 메모리 주소 공간에서 실행될 수 있게 해준다.
가장 큰 어려움은 디스크 공간이다. 모든 가상 머신에 동일한 디스크 드라이브를 할당하는 것은 불가능하므로, 가상 디스크(Minidisks) 를 사용한다. 가상 디스크는 크기를 제외하면 모든 면에서 실제 디스크와 동일하다.
가상 머신의 장점
- 시스템 자원의 완전한 보호: 각 VM은 외부와 격리되어 있다
- VM 간 공유가 필요한 경우: 공유 미니디스크, 가상 네트워크 연결
- OS 연구, 개발, 교육에 완벽한 도구: OS를 변경하는 것은 위험한 작업이다. VM에서 작업하면 물리 머신을 중단할 필요가 없다
가상 머신의 한계
호스트 시스템과의 불가피한 차이가 존재한다.
- 디스크 크기: 실제보다 작은 가상 디스크를 사용
- 실행 시간:
- 많은 VM 간의 멀티프로그래밍이 예측 불가능한 방식으로 VM을 느리게 만들 수 있다
- 특권 명령어(Privileged Instructions) 는 시뮬레이션되므로 느리다
- 가상 I/O는 스풀링(spooling)으로 더 빠를 수도 있고, 해석(interpreted)으로 더 느릴 수도 있다
HGU 전산전자공학부 김인중 교수님의 23-1 운영체제 수업을 듣고 작성한 포스트이며, 첨부한 모든 사진은 교수님 수업 PPT의 사진 원본에 필기를 한 수정본입니다.