2026. 3. 10. 11:56ㆍ운영체제(OS)
시스템 소프트웨어 구성과 OS의 역할
1OS 기초 첫 번째. 2시스템 소프트웨어의 구성 요소와 각각의 역할, OS와 나머지 도구들이 어떤 관계인지, 그리고 소스코드가 실행 파일이 되기까지의 처리 과정 전체를 정리한다.
1. 시스템 소프트웨어 구성
시스템 소프트웨어는 컴퓨터 전체를 작동시키는 프로그램의 총칭이다. 사용자가 직접 목적을 갖고 사용하는 응용 소프트웨어와 달리, 컴퓨터 자체가 제대로 돌아가도록 뒷받침하는 역할을 담당한다. 일상으로 비유하면 건물의 전기·배관·통신망처럼 눈에 잘 보이지 않지만 없으면 아무것도 동작하지 않는 인프라에 해당한다.
시스템 소프트웨어의 구성 요소는 아래와 같다. OS가 가장 핵심적인 위치에 있고, 나머지는 OS 위에서 동작하는 개발 도구들이다.
교재에 따라 "시스템 소프트웨어 = OS"로 동일시하는 경우가 있다. OS가 워낙 핵심 위치를 차지하다 보니 자연스럽게 묶이는 것이다. 그러나 엄밀히는 OS가 이 중 하나의 구성 요소이고, 나머지는 OS 위에서 실행되는 독립 도구들이다. 컴파일러를 따로 설치하거나 버전을 바꿀 수 있다는 사실 자체가 이 둘이 별개임을 보여준다.
| 구성 요소 | 한 줄 역할 |
|---|---|
| OS | 하드웨어를 추상화하고, 다른 모든 프로그램이 실행될 수 있는 기반을 제공한다 |
| 컴파일러 / 인터프리터 | 사람이 쓴 소스코드를 기계가 이해할 수 있는 형태로 번역한다 |
| 어셈블러 | 저급 언어인 어셈블리어를 기계어로 1:1 변환한다 |
| 링커 | 번역된 코드 조각들과 라이브러리를 하나의 실행 파일로 합쳐준다 |
| 라이브러리 | 자주 쓰는 기능을 미리 구현해 묶어둔 코드 집합이다 |
| 로더 | 완성된 실행 파일을 RAM에 올려 CPU가 실행을 시작하게 한다 |
| 매크로 프로세서 | 코드 내 #define 같은 매크로를 실제 코드로 치환한다 (보통 컴파일러에 내장) |
2. OS와 나머지 도구들의 관계
컴파일러, 링커, 라이브러리는 모두 OS 위에서 실행되는 독립 6프로세스다. OS가 이것들을 "관리"하는 게 아니라, 이것들이 실행될 수 있도록 CPU 시간·메모리 공간·파일 시스템 접근을 제공해주는 것이다. 이 차이를 명확히 이해해두는 게 중요하다.
다만 로더(Loader)만은 예외다. 실행 파일을 메모리에 올리는 이 역할은 OS 내부에 포함되어 있다. 사용자가 별도로 설치하거나 교체할 수 없고, OS가 직접 담당한다. 예를 들어 Windows에서 .exe를 더블클릭하면 OS의 로더가 해당 파일을 RAM에 올리고 실행을 시작한다. 이 과정은 사용자 눈에 보이지 않지만 모든 프로그램 실행의 첫 단계다.
| 구성 요소 | OS와의 관계 | 독립 설치 가능? |
|---|---|---|
| 컴파일러, 링커 | OS 위에서 실행되는 독립 프로그램 | ✅ 가능 |
| 라이브러리 | OS 위에서 참조되는 독립 코드 묶음 | ✅ 가능 |
| 로더 | OS 내부에 포함된 기능 | ❌ OS 일부 |
| 매크로 프로세서 | 보통 컴파일러 내부에 통합 | △ 보통 불필요 |
Unity 개발에서도 이 관계가 그대로 드러난다. 7Roslyn 컴파일러, 8IL2CPP 툴체인, UnityEngine.dll 같은 라이브러리는 모두 OS 위에서 동작하는 독립 도구들이다. Unity 에디터 자체도 하나의 응용 프로그램으로 OS 위에서 실행된다. 빌드 버튼을 누르면 이 도구들이 OS의 실행 환경을 빌려 순서대로 동작하면서 최종 바이너리를 만들어낸다.
User Mode와 Kernel Mode
OS와 일반 프로그램의 관계를 더 깊게 이해하려면 9커널 모드와 10유저 모드 개념을 알아야 한다. CPU는 실행 권한을 두 단계로 나눈다.
| 구분 | 실행 주체 | 접근 가능 자원 |
|---|---|---|
| Kernel Mode | OS 커널 | 하드웨어 직접 제어, 모든 메모리 접근 가능 |
| User Mode | 일반 프로그램 (컴파일러, 게임 등) | OS가 허용한 범위 내에서만 자원 사용 가능 |
컴파일러도, Unity 게임도, 심지어 링커도 모두 User Mode에서 실행된다. 하드웨어에 직접 접근하려면 반드시 OS를 통해 11시스템 콜을 호출해야 한다. 이 구조 덕분에 하나의 프로그램이 다른 프로그램의 메모리를 함부로 침범할 수 없다.
3. 제어 프로그램 vs 처리 프로그램
시스템 소프트웨어를 기능 기준으로 분류하면 두 가지로 나뉜다. 제어 프로그램은 시스템 전체를 감시하고 제어하는 OS의 핵심 영역이고, 처리 프로그램은 그 감시 하에서 실제 작업을 수행하는 도구들이다. 이 둘의 관계는 "무대 감독"과 "배우"에 비유할 수 있다. 제어 프로그램이 무대 전체를 조율하고, 처리 프로그램이 그 위에서 역할을 수행한다.
제어 프로그램 (Control Program)
시스템 전체의 동작 상태를 감시하고, 작업 순서를 지정하며, 데이터를 관리하는 역할을 담당한다. 현대 OS의 12커널 기능 대부분이 여기에 해당한다.
| 종류 | 역할 | 현대 OS 대응 |
|---|---|---|
| 감시 프로그램 Supervisor Program |
각종 처리 프로그램의 실행 상태를 감시·감독한다. 모든 처리 프로그램은 이 감시 하에서만 실행되며, 비정상적인 동작이 감지되면 중단시키거나 오류를 보고한다. 운영체제의 가장 핵심적인 역할이다. | 커널 / 프로세스 모니터링 |
| 작업 제어 프로그램 Job Control Program |
어떤 작업을 언제, 어떤 순서로 실행할지 결정하고 CPU와 메모리 등의 자원을 할당한다. 일반적인 컴퓨터에서 CPU는 하나이므로, 여러 작업이 공평하게 CPU를 나눠 쓸 수 있도록 조율한다. | 13CPU 스케줄러 |
| 데이터 관리 프로그램 Data Management Program |
파일과 데이터를 14주기억장치와 15보조기억장치 사이에서 전송하고 갱신·유지보수한다. 프로그램이 파일을 열고 저장하는 행위 전체가 이 프로그램을 통해 이루어진다. | 16파일 시스템 |
처리 프로그램 (Processing Program)
제어 프로그램의 감시 하에서 사용자가 요청한 실제 작업을 처리하는 프로그램들이다. 언어 번역기, 서비스 프로그램, 문제 프로그램으로 나뉜다. 중요한 점은 이 프로그램들이 독립적으로 돌아가는 것처럼 보여도, 실제로는 항상 제어 프로그램의 관리 아래에서 실행된다는 것이다.
| 종류 | 설명 | 예시 |
|---|---|---|
| 언어 번역기 | 사용자가 작성한 17원시 프로그램을 18기계어 형태의 19목적 프로그램으로 변환한다. 번역 방식에 따라 컴파일러(전체 번역), 인터프리터(한 줄씩 실행), 20어셈블러(어셈블리어 → 기계어)로 나뉜다. | Roslyn, GCC |
| 서비스 프로그램 | 개발과 운영에 필요한 유틸리티 성격의 도구들이다. 링커, 21라이브러리안, 정렬/합병 프로그램 등이 여기에 속한다. | 링커, ar, make |
| 문제 프로그램 | 사용자가 특정 목적을 위해 직접 작성한 응용 프로그램이다. 제어 프로그램의 감시 하에서만 실행된다. | Unity 게임, 서버 앱 |
4. 프로그램 처리 과정
소스코드가 작성된 후 실제로 실행되기까지의 흐름은 크게 다섯 단계로 이루어진다. 이 흐름은 수십 년 전이나 지금이나 본질적으로 동일하다. 단계가 추가되거나 자동화됐을 뿐, 원시 프로그램 → 번역 → 링킹 → 로딩 → 실행이라는 구조는 변하지 않았다.
원시 프로그램
언어 번역
실행 불가
라이브러리 연결
.exe / .apk
메모리 적재
결과 출력
각 단계 상세
| 단계 | 역할 | Unity 실체 |
|---|---|---|
| 원시 프로그램 | 개발자가 작성한 소스코드다. 사람이 이해하기 쉬운 22고급 언어로 작성되며, 이 상태로는 CPU가 직접 실행할 수 없다. | .cs 파일 |
| 언어 번역기 | 소스코드를 CPU가 이해할 수 있는 형태로 변환한다. 현대에는 바로 기계어로 가지 않고 23중간 언어(IL)로 변환하는 경우가 많다. 이 단계에서 문법 오류가 발견되면 빌드가 중단된다. | Roslyn → IL |
| 목적 프로그램 | 번역은 완료됐지만 외부 라이브러리 참조가 미해결 상태라 단독으로 실행할 수 없다. 마치 부품은 다 만들었지만 아직 조립하지 않은 상태와 같다. | IL (중간 언어) |
| 링커 | 목적 프로그램과 라이브러리 함수들을 하나의 실행 파일로 합친다. 여러 .obj 파일과 .dll 참조를 하나의 바이너리로 합쳐주는 역할이다. |
IL2CPP + C++ 링커 |
| 로드 모듈 | 링킹이 완료된 최종 실행 파일이다. 이 시점부터 실행 가능한 완전한 상태가 된다. 사용자가 배포받아 실행하는 파일이 바로 이것이다. | .exe / .apk |
| 로더 | OS가 로드 모듈을 RAM에 올리고 CPU에게 실행을 시작하도록 지시한다. 사용자 눈에는 "프로그램이 실행된다"로 보이는 순간이 바로 이 단계다. | OS 로더 |
Console.WriteLine() 같은 함수는 내가 작성한 코드가 아니라 .NET 라이브러리에 있다. 컴파일러는 번역 시점에 "이 함수가 어딘가에 존재한다"는 미해결 참조만 기록해둔다. 링커가 실제 라이브러리를 찾아서 연결해줘야 비로소 완전한 실행 파일이 완성된다.Unity에서
using UnityEngine;을 선언하는 것도 같은 원리다. 링킹 단계에서 UnityEngine.dll이 연결되어야 GameObject나 MonoBehaviour 같은 클래스가 실제로 동작한다. 만약 링커가 참조된 라이브러리를 찾지 못하면 "Unresolved external symbol" 같은 링크 에러가 발생한다.
Unity 빌드 파이프라인 전체 흐름
Unity는 단순한 1단계 번역이 아니라 두 번의 번역 과정을 거친다. IL2CPP 방식 기준으로 보면 아래와 같다. 각 단계마다 별도의 도구가 개입하며, 모두 OS 위에서 독립적으로 실행된다.
C# → IL
중간 언어
IL → C++
+ 링커
.exe / .apk
현대에는 소스코드와 기계어 사이에 IL이라는 중간 언어 단계가 추가됐지만, 흐름의 본질은 동일하다. 중간 언어를 거치는 이유는 24플랫폼 이식성을 확보하기 위해서다. 동일한 C# 코드가 Windows, Android, iOS, 콘솔 플랫폼에서 모두 동작할 수 있는 건 이 구조 덕분이다.
또한 Mono 백엔드를 사용하는 경우에는 IL을 C++로 변환하지 않고 IL 상태로 직접 실행한다. 개발 속도는 빠르지만 최적화 측면에서는 IL2CPP보다 불리하다. 실제 릴리즈 빌드에서 IL2CPP를 권장하는 이유가 여기에 있다.
📎 용어 설명
- OS (Operating System, 운영체제) — 컴퓨터 하드웨어와 사용자/응용 프로그램 사이의 인터페이스를 담당하는 시스템 소프트웨어. Windows, Linux, macOS, Android 등이 있다.
- 시스템 소프트웨어 — 컴퓨터 전체를 작동시키기 위한 프로그램의 총칭. OS와 각종 개발 도구들(컴파일러, 링커, 라이브러리 등)을 포함한다.
- 컴파일러 (Compiler) — 고급 언어로 작성된 소스코드 전체를 한번에 기계어 또는 중간 언어로 번역하는 프로그램. 번역 후 별도의 실행 파일이 생성된다.
- 링커 (Linker) — 컴파일된 목적 파일들과 라이브러리를 하나의 실행 파일로 합쳐주는 프로그램. "연결 편집기(Linkage Editor)"라고도 한다.
- 라이브러리 (Library) — 자주 사용하는 기능을 미리 구현해 묶어둔 코드 집합. 정적 라이브러리(.lib, .a)와 동적 라이브러리(.dll, .so)로 나뉜다.
- 프로세스 (Process) — 메모리에 올라와 실행 중인 프로그램의 인스턴스. OS가 CPU 시간을 배분하는 기본 단위. 하나의 프로그램을 여러 번 실행하면 여러 프로세스가 생성된다.
- Roslyn — Microsoft가 만든 .NET 공식 C# 컴파일러. Unity에서 C# 코드를 IL로 변환하는 역할을 담당한다. 오픈소스로 공개되어 있다.
- IL2CPP — Unity의 빌드 백엔드 중 하나. IL(중간 언어)을 C++ 코드로 변환한 뒤 네이티브 바이너리로 컴파일한다. 성능과 플랫폼 호환성 확보가 목적이다.
- 커널 모드 (Kernel Mode) — CPU의 실행 권한 중 가장 높은 수준. OS 커널이 이 모드에서 실행되며, 하드웨어에 직접 접근하고 모든 메모리를 읽고 쓸 수 있다.
- 유저 모드 (User Mode) — 일반 응용 프로그램이 실행되는 제한적인 권한 수준. 하드웨어에 직접 접근할 수 없으며, 커널 기능이 필요하면 시스템 콜을 통해 요청해야 한다.
- 시스템 콜 (System Call) — 응용 프로그램이 OS 커널의 기능(파일 읽기, 네트워크 통신, 메모리 할당 등)을 요청하는 공식 인터페이스. User Mode에서 Kernel Mode로 진입하는 유일한 통로다.
- 커널 (Kernel) — OS의 핵심 부분. 하드웨어 자원(CPU, 메모리, I/O)을 직접 제어하고 프로세스·파일 시스템·네트워크 등을 관리한다. 항상 메모리에 상주한다.
- CPU 스케줄러 — 여러 프로세스가 CPU를 번갈아 사용할 수 있도록 실행 순서와 시간을 결정하는 OS의 핵심 알고리즘. FCFS, Round Robin, Priority 등의 방식이 있다.
- 주기억장치 — RAM(Random Access Memory). CPU가 직접 접근할 수 있는 빠른 메모리. 전원이 꺼지면 데이터가 사라지는 휘발성 메모리다.
- 보조기억장치 — HDD, SSD 등 대용량 저장 장치. 전원이 꺼져도 데이터가 유지되는 비휘발성 메모리지만 주기억장치보다 접근 속도가 느리다.
- 파일 시스템 (File System) — 보조기억장치에 데이터를 파일 단위로 저장하고 관리하는 OS의 기능. Windows의 NTFS, Linux의 ext4, macOS의 APFS 등이 있다.
- 원시 프로그램 (Source Program) — 개발자가 직접 작성한 소스코드 파일. 컴파일 전 상태의 텍스트 형태 코드.
- 기계어 (Machine Code) — CPU가 직접 실행할 수 있는 0과 1의 이진수 명령어. CPU 아키텍처(x86, ARM 등)마다 기계어 형식이 다르다.
- 목적 프로그램 (Object Program) — 컴파일 후 생성된 중간 산출물. 번역은 완료됐지만 외부 참조가 미해결 상태라 단독 실행이 불가능하다.
- 어셈블러 (Assembler) — 어셈블리어(저급 언어)로 작성된 코드를 기계어로 변환하는 프로그램. 명령어가 기계어와 거의 1:1 대응된다.
- 라이브러리안 (Librarian) — 라이브러리 파일을 생성·수정·관리하는 도구. 여러 목적 파일을 하나의 라이브러리로 묶거나, 특정 모듈을 추가·삭제하는 역할을 한다.
- 고급 언어 (High-Level Language) — 사람이 이해하기 쉬운 형태의 프로그래밍 언어. C#, Java, Python 등이 해당한다. CPU에 종속되지 않아 이식성이 높다.
- 중간 언어 (Intermediate Language, IL) — 소스코드와 기계어 사이의 중간 단계 코드. 특정 CPU에 종속되지 않아 이식성이 높다. .NET의 CIL, Java의 Bytecode가 대표적이다.
- 플랫폼 이식성 (Portability) — 하나의 코드가 Windows, Android, iOS 등 서로 다른 플랫폼에서 동작할 수 있는 능력. Unity가 멀티 플랫폼을 지원할 수 있는 핵심 이유 중 하나다.
'운영체제(OS)' 카테고리의 다른 글
| [OS] 스레드(Thread)와 멀티 스레딩 - 6 (0) | 2026.03.12 |
|---|---|
| [OS] 프로세스 개요와 PCB, 상태 전이 - 5 (0) | 2026.03.11 |
| [OS] CPU 구조와 파이프라인 - 4 (0) | 2026.03.11 |
| [OS]운영체제 발전 흐름 - 3 (0) | 2026.03.11 |
| [OS] 프로그래밍 언어 분류와 링커, 로더 - 2 (0) | 2026.03.10 |