운영체제 구조

근본적으로, 한 운영체제에서 컴파일된 애플리케이션은 다른 운영체제에서는 실행되지 않는다. 만약 실행되었다면, 세상은 훨씬 더 나은 곳이 되었을 것이고, 우리가 사용할 운영체제를 선택하는 기준은 사용 가능한 애플리케이션이 아니라 유용성과 기능에 따라 결정되었을 것이다.

그러나, 시스템 호출이 동일하다고 해도, 여전히 애플리케이션 프로그램은 다른 운영체제에서는 실행되지 않는다. 이러한 차이를 극복하는 데에는 여러 방법이 있다.

첫 번째 방법: 인터프리터 언어 사용

애플리케이션을 Python이나 Ruby 같은 인터프리터 언어로 작성하고, 해당 언어의 인터프리터가 설치된 운영체제 위에서 실행하는 것이다.

인터프리터는 소스 코드를 줄 단위로 읽고 해당 줄을 실행한다. 이 때, 호스트 운영체제의 명령어 집합에 맞게 해석하여 시스템 호출을 수행한다.

두 번째 방법: 실행 환경 포함 언어 사용

Java 애플리케이션은 Java 런타임 환경(RTE)을 필요로 하며, 이 환경은

  • 로더

  • 바이트코드 검증기

  • 실행을 위한 기본 기능들 을 포함한다.

Java 프로그램은 JVM 위에서 실행되며, JVM은 다양한 운영체제에서 구현되어 있어 이식성을 보장한다.

세 번째 방법: 운영체제별 포팅 (Native compilation with porting)

POSIX와 같은 API 표준을 따르는 소스 코드를 작성하고, 각 운영체제마다 다르게 컴파일하는 방식이다.

이 방법은 이식성을 높이지만,

  • 운영체제마다 포팅, 테스트, 디버깅을 반복해야 하며

  • 매우 시간과 자원이 많이 든다.


추가적 문제들

  • 시스템 호출 번호와 호출 방식이 OS마다 다르며,

  • 실행 파일 포맷도 다름 (예: Windows: PE, Linux: ELF, macOS: Mach-O)

  • CPU 명령어 세트가 다름 (x86, ARM 등)

이로 인해 이식성이 제한된다.


운영체제 설계와 구현 (Operating-System Design and Implementation)

운영체제는 설계와 구현이라는 두 가지 주요 활동으로 나뉜다. 이 둘은 밀접하게 연결되어 있지만 서로 다른 고려사항을 가진다.


설계 목표 (Design Goals)

운영체제 설계는 주로 다음 두 가지 목표로 구성된다:

  • 사용자 입장에서의 시스템: 사용하기 쉽고, 빠르고, 믿을 수 있어야 한다.

  • 시스템 구현자 입장에서의 시스템: 구성 요소가 구현 및 유지보수하기 쉬워야 한다.

운영체제의 설계는 타겟 사용자에 따라 달라질 수 있다.

  • 개인용 컴퓨터 → 사용자 친화성 강조

  • 메인프레임 → 자원 효율 강조

  • 임베디드 시스템 → 실시간성 강조


메커니즘과 정책 (Mechanisms and Policies)

  • 메커니즘: 시스템이 무엇을 할 수 있는지를 정의함 예: CPU 스케줄러는 프로세스를 선택할 수 있음

  • 정책: 시스템이 특정 상황에서 어떻게 행동할지를 정의함 예: 어떤 프로세스를 선택할지 결정하는 기준

운영체제 설계에서 정책과 메커니즘을 분리하는 것이 중요하다. 이렇게 하면, 정책을 쉽게 바꿀 수 있고 시스템이 유연해진다.


구현 (Implementation)

운영체제는 예전에는 어셈블리 언어로 작성되었지만, 현재는 대부분 C 언어로 작성되며, 일부 하드웨어 제어 코드만 어셈블리어를 사용한다.

구현에 있어 두 가지 방식이 있다:

  • 모놀리식 커널(monolithic kernel)

    • 모든 기능이 하나의 커다란 커널 모듈로 구현됨

    • 빠르지만 유지보수가 어려움

  • 모듈 방식(modular) 또는 계층적 구조

    • 각 기능이 독립적인 모듈로 나뉘며, 인터페이스로만 연결됨

    • 유지보수, 확장성이 좋음

운영체제 구현은 설계 목표를 고려한 트레이드오프의 연속이며, 성능, 복잡도, 이식성 사이의 균형을 잡아야 한다.

Last updated