본문 바로가기
시스템 프로그래밍

OS Structures & Linux Overview

by sepang 2021. 11. 12.

  저번에는 OS에 대해 알아봤다면 이번에는 '어떻게 OS를 설계하고 구현하는지''OS의 내부 구성 방법'에 대해 알아볼 것이다. 그리고 OS 중 Linux에 대해서도 알아보자.

OS Design and Implementation

  일단 OS를 설계하고 구현하는데 있어서 최선/절대적인 답은 없다. 그러나 일부 접근 방식은 성공적인 것으로 이미 입증되었다. 그리고 서로 다른 OS의 내부 구조는 매우 다양할 수 있다.

  그래서 우리는 목표 및 사양을 정의하여 설계를 시작해야 한다. 왜냐하면 하드웨어나 시스템의 유형의 선택에 영향을 받기 때문이다. 설계 요구사항을 특정할 때, 다음과 같은 사항들이 고려될 수 있을 것이다.

  • User goals: OS는 사용하기 쉽고, 배우기 쉽고, 신뢰할 수 있고, 안전하고, 빨라야 한다.
  • System developer goals: OS는 설계, 구현 및 유지 관리가 쉽고 유연하고 안정적이며 오류가 없고 효율적이어야 한다.

Separating Policy and Mechanism

  OS를 설계할 때 가장 중요한 원칙은 정책(Policy)과 메커니즘(Mechanism)을 분리하는 것이다. 정책은 '무엇을 해야하는가?'이다. 모든 자원 할당 문제에 대한 정책 결정이 중요하다. 메커니즘은 '어떻게 할 것인가?'이다. 이는 일련의 정책을 구현하기 위한 도구이다. 다시 말하자면 정책은 '컴퓨터 시스템에서 무엇을 할지'이고, 메커니즘은 '정책을 지원하는 툴'이다. 

  이러한 원칙은 나중에 정책 결정이 변경될 경우 최대의 유연성을 허용해야한다. 그러므로 정책과 분리일반적인 메커니즘이 더 바람직하다. 생각해보자, 만약 이 두개가 분리되지 않으면 자주 변경되는 정책의 변화에 따라 메커니즘도 수정해야한다. 메커니즘을 여러 상황에 적용될 수 있게끔 일반적으로 설계하면 동일한 상황에서 메커니즘을 수정할 필요는 없는 것이다.

  예를 들어 시스템에서 프로세스를 전환할 때 쓰이는 CPU 스케줄러를 보자. 여기서 정책(무엇을)은 FIFO, SJF, 우선순위 스케줄링 등의 스케줄링 알고리즘이나 time slice 등이 될 수 있다. 메커니즘(어떻게)은 프로세스 변경을 위한 디스패처, 작업 길이 측정, 우선 순위 큐 등이 될 수 있다.

OS Implementation

  OS를 구현하는데는 많은 변화가 있어왔다.

  초기 OS는 어셈블리 언어로 작성되어왔다. 이런 경우는 CPU의 레지스터를 직접 조정할 수 있어서 성능은 좋으나 관리에 어려움 있었다. 그 다음에는 Algol, PL/1같은 시스템 프로그래밍 언어를 사용했고, 이후에는 C/C++이 우선되었다.

  실제로는 OS를 구현할 때 여러 언어가 혼합된다. lowest-level에서는 어셈블리가 쓰이고 main body에는 C, 시스템 프로그램에는 C/C++, 스크립팅 언어에는 PERL, Phython, shell scripts가 쓰인다. 어셈블리 같은 low-level 언어와 달리 high-level 언어는 다른 하드웨어로 쉽게 이식이 가능하다는 장점이 있지만 CPU의 디테일한 조정은 불가능하기에 더 느리다는 단점이 있다.

 

OS Structure

  이제 OS의 구조에 대해 알아보자. 범용 OS는 매우 큰 프로그램이다. OS의 구성방법은 다양하다. MS-DOS같이 간단한 구조, UNIX같은 모놀리식 구조, 추상화 기반의 계층화 구조, Mach같은 마이크로 커널 등이 존재한다. 여기서 커널이란 OS의 일부인데 시스템이 종료되기 전까지 주메모리에 남아있으면서 SW와 HW를 잇는 다리 역할을 한다.

Simple Structure: MS-DOS

fig 1.1 MS-DOS

  확인해볼 구조들 중에 가장 단순한 구조를 가진다. 최소한의 공간에서 최대한의 기능을 제공하도록 작성되었다. 모듈로 분할되지 않았다. fig 1에서 확인할 수 있듯이 약간의 구조를 가지나 인터페이스와 기능 수준이 잘 구분되지 않는다. 무슨 말인가 하면 각 계층에서 인접한 계층에만 접근할 수 있는게 아니라 멀리 떨어진 계층에도 접근이 가능하다는 것이다. 이렇게 되면 어플리케이션이 HW에 접근할 수 있게 된다.

fig 1.2

  fig 1.2에서 확인할 수 있듯이 단일 작업만 가능하고, 단일 메모리 공간을 가진다. 시스템 부팅 시 쉘(shell, CI:명령 인터프리터)이 호출된다. 프로그램이 실행 될 때는 프로그램을 메모리에 로드하여 커널과 CI의 일부를 제외한 모든 것을 덮어쓴다. 즉 다른 프로세스는 생성되지 못한다. 프로그램 종료시에 남아있던 CI의 일부는 디스크에서 쉘(CI)를 다시 로드한다.

Layered Approach

fig 2

  이번에는 OS 구조를 계층 형식으로 접근해보자. OS는 여러 계층(레벨)로 나뉘며 각각은 하위 계층 위에 구성된다. 맨 아래 계층(계층 0)은 하드웨어이고 가장 높은 계층(계층 N)은 사용자 인터페이스이다. 각 계층은 하위 계층의 기능과 서비스만 사용할 수 있다. 이러한 방식은 모듈식(modular)이라고도 하는데 코드 기반의 확장 및 유지 관리를 단순화한다.

Monolithic: UNIX

fig 3

  fig 3는 전통적인 유닉스 시스템 구조이다. 커널 계층 기준으로 위는 User, 아래는 HW이다. 이 처럼 3-layered 구조이나 완전히 계층화되지는 않았다. 위의 layered approach에서는 커널 기능마다 하나의 계층을 이루었다. 하지만 유닉스에서는 커널 기능들이 하나의 계층에 속한다. 이를 모놀리식(monolithic)구조라고 한다.

  즉 커널을 하나의 거대한 SW라고 생각하면 된다. 시스템 콜 인터페이스 아래, 물리적 하드웨어 위의 모든 항목으로 구성되어 있다. 하나의 계층에서 파일 시스템, CPU 스케줄링, 메모리 관리, I/O등 많은 기능을 제공한다.

  이러한 모놀리식의 장점은 일단 성능이다. 모듈식과 비교해보면 많은 계층을 거치지 않아도 되기에 여기서 처리속도의 이점이 생기는 것이다. 단점은 다양한 커널 기능이 하나의 계층에 속하기에 발생한다. 우선 코드의 상호 의존성으로 인한 유지보수 및 업그레이드의 어려움이다. 다른 단점으로는 보안/신뢰성이 낮다. 전체 OS 코드가 하나의 단일 메모리 공간에서 실행되기 때문이다.

MicroKernel

fig 4

  마이크로커널(microkernel)은 커널의 기능들을 가능한 많이 user mode 서비스로 이동시킨 것이다. 예를 들어 MAC OS X가 일부 기반으로 하는 Mach가 있다. IPC(interprocess communication) 기반의 메시지 전달(message passing)을 사용하여 사용자 모듈 간에 통신이 이루어진다. fig 4 처럼 커널 모드의 IPC 기능을 통해 이뤄진다. 이처럼 커널 계층에는 OS의 핵심 기능들만 위치시키고 그외의 기능들의 user 계층에 위치시킨다.

  이러한 마이크로커널의 장점에는 우선 서비스를 추가할 때 커널 계층을 수정할 필요가 없으므로(모듈식) 확장에 유리하다. 그리고 상대적으로 커널 계층의 크기가 작은데 이 계층이 하드웨어와 의존성이 가장 강하다. 때문에 다른 아키텍처에 이식하기에도 쉽다. 또한, 보다 안정적이고 안전하다. 커널 모드에서 실행되는 코드가 적기도 하고 유저모드의 기능 중 하나에 문제가 생겨도 해당 기능만 살피면 되기 때문이다.

  단점으로는 IPC가 이뤄지므로 이때 성능 오버헤드가 발생한다. fig 4를 예로들면 어플리케이션 프로그램에서 파일 시스템으로 요청을 보낼 때 어플리케이션에서 보낼 내용을 IPC 커널 계층에 복사를 한다음 이것을 다시 파일 시스템에 복사한다.

Modules

fig 5

  모듈 구조는 런타임에 로드가 가능한 커널 모듈을 구현한다. 즉 커널의 중요 기능은 계속 돌아가고 다른 커널 기능은 secondary storage에 저장되어 있다가 필요할 때 메모리에 로드되는 것이다. 즉 객체 지향적 접근 방식을 사용한다. 각 핵심 구성 요소는 분리되어있고 각각은 이미 정해진 인터페이스를 통해 서로 통신한다. 리눅스, solaris 등의 시스템에서 사용된다.

  그리고 레이어, 마이크로 커널과 유사해 보일 수 있는데 이를 명확히 구분할 필요가 있다. 레이어 같은 경우에는 인접한 레이어들끼리만 상호작용이 가능하므로 모듈 구조가 더 유연하다. 그리고 마이크로 커널처럼 기능들이 독립된 코드 블럭으로 동작하지만 모듈 구조에서는 필요 기능이 동일한 메모리 공간(커널 모드)에 올라와서 이미 있던 core part와 상호작용한다. 때문에 더 빠른 성능을 얻을 수 있다.

Hybrid Systems

fig 6

  대부분의 최신 OS는 실제로 하나의 순수한 모델만을 적용하지 않고 성능, 보안, 사용성 등의 요구사항을 해결하기 위해 서로 다른 시스템의 여러 접근 방식을 결합한다. Linux 및 Solaris 커널은 단일 커널 주소 공간에 OS가 있기에 모놀리식이기도 하지만 기능의 동적 로딩을 위해 모듈 구조도 사용한다.

  Windows는 대부분 모놀리식으로 구현되어 있고, 다양한 하위 시스템 특성을 위해 마이크로 커널도 사용한다. MAC OS X는 layered 구조를 사용하는데 내부 커널에는 Mach(마이크로 기반), BSD(모놀리식 기반), kernel extensions(모듈 구조)를 사용한다.

 

Linux Overview

  이번 파트는 리눅스에 대한 내용이다. 다음과 같은 내용들을 살펴볼 것이다.

  • 리눅스와 리눅스 커널
  • 리눅스란?
  • 리눅스 OS 구조
  • 리눅스 OS의 구성요소

 Linux & Linux Kernel

  우선 리눅스를 배우는 것은 리눅스에서 제공하는 다양한 명령과 도구(ex. vim ,emacs, make, gcc, ...)를 사용하여 응용 프로그램 개발/테스트 능력을 향상 시키는 것을 의미한다. 이러한 리눅스를 배워야 하는 이유는 다양한 IT 업계에서 사용하는 디바이스 대부분은 리눅스 OS 기반이기 때문이다. 예를 들어 모바일용 Android, 클라우드 서버, 네트워크 장비 등이 있다. 그리고 많은 오픈 소스 프로그램이 리눅스 기반 환경에서 실행된다.

  리눅스 커널을 배우는 것은 커널의 세부적인 동작을 이해하고 커널의 여러 부분을 수정하는 능력을 향상시키는 것을 의미한다. 이러한 리눅스 커널을 배워야 하는 이유는 고급 프로그래머가 되기 위해서는 코드 최적화, 디버깅 기술 등과 관련된 커널에 대한 지식을 필요로 하기 때문이다. 그리고 리눅스 커널 지식은 low-level 하드웨어 인터페이스(device driver)를 다르는 임베디드 시스템 개발자에게는 필수적인 사항이다. 이러한 리눅스 커널은 오픈 소스 기반 OS이다.

  리눅스는 UNIX 표준을 기반으로 하는 최신 무료 OS이다. 1991년 Linus Torvalds에 의해 작지만 독립적인 커널(ver. 0.01)로 처음 개발되었으며 UNIX 호환성의 주요 설계 목표가 오픈 소스로 출시되었다. fig 7은 버전이 올라갈 수록 코드의 길이가 계속해서 증가하고 있음을 보여준다. 그만큼 기능들이 추가되고 있는 것이다.

fig 7

The Linux System

  리눅스는 Berkeley의 BSD OS, MIT의 X Window System 및 Free Software Foundation의 GNU 프로젝트의 일부로 개발된 많은 도구를 사용하는 등 다른 OS나 프로젝트에서 사용되는 툴들을 많이 사용한다. 그리고 리눅스 시스템은 인터넷을 통해 협력하는 개발자 네트워크(리눅스 커뮤니티)에 의해 유지/관리 된다. 원래는 주요 시스템 라이브러리들은 GNU 프로젝트에서 시작되었다가 지금은 리눅스 커뮤니티에서 주도적으로 업그레이드 하고 있다.

fig 7

  그리고 리눅스 커뮤니티에서는 다앙한 시스템 구성 요소 간의 호환성을 보장하기 위해 파일 시스템 계층 표준 문서를 유지/관리하고 있다. \(root directory)내부에 \bin, \dev, \home, ...등 표준 리눅스 파일 시스템의 전체 레이아웃을 지정하고 구성 파일, 라이브러리 시스템 바이너리 및 런타임 데이터 파일이 저장되어야하는 디렉토리 이름을 결정한다.

Design Principles of Linux

  리눅스는 UNIX 기반이기에 UNIX와 떼려야 뗄 수 없는 관계이다. 그래서 UNIX 호환가능 툴들을 이용하여 멀티유저(multiuser), 멀티태스킹(multitasking)을 지원한다. 파일 시스템도 기존 UNIX 의미 체계를 준수하며 표준 UNIX 네트워킹 모델을 구현한다. 설계 목표는 속도, 효율성, 표준화이다.

  속도와 효율성은 모놀리식 구조를 사용함으로써 만족시킨다. 그리고 표준화는 POSIX(Portable Operating System Interface) 문서를 준수하여 설계함으로써 만족시킨다. POSIX는 시스템/유저 레벨의 API function들을 정의한 문서이다.- 이는 UNIX와 상호 운용 가능하도록 디자인되어 있다. 때문에 POSIX를 따르는 다른 OS들끼리 호환성이 뛰어나다. POSIX는 IEEE에서 정의한다.

Linux OS Structure

  리눅스는 UNIX를 기반으로 하며 모놀리식과 유사한 구조로 되어있다. 즉 커널의 모든 기능을 단일 주소 공간에서 실행되는 단일 정적 바이너리 파일에 배치하여 속도와 효율성 측면에서 이점을 보인다.

  또한 리눅스에는 런타임 중에 커널을 수정할 수 있는 모듈식 설계도 있다. 주로 장치 드라이버 및 파일 시스템 지원을 위해 로드 가능한 커널 모듈(LKMs)을 사용한다. LKM은 시스템이 시작될 때 또는 USB 장치가 실행 중인 시스템에 연결/해제되는 경우와 같이 런타임 중에 커널에 "삽입 또는 제거"될 수 있다.

Components of the Linux System

fig 8

  대부분의 UNIX 구현과 마찬가지로 Linux는 커널, 시스템 라이브러리, 시스템 유틸리티의 세 가지 주요 코드 본문으로 구성된다.

Kernel

  OS의 모든 중요한 추상화를 유지/관리 한다. 모든 커널 코드는 컴퓨터의 모든 물리적 리소스에 대한 전체 액세스 권한이 있는 프로세서의 권한 모드(=커널 모드)에서 실행된다. 또한 모든 커널 코드와 자료 구조는 동일한 단일 주소 공간에 유지된다.

System Libraries

  응용 프로그램이 커널과 상호 작용하고 커널 코드의 전체 권한이 필요하지 않은 많은 OS 기능을 구현하는 표준 기능들의 집합을 정의한다. 가장 중요한 시스템 라이버리는 libc로 알려진 C라이브러리이다.

System Utilities(= System Services)

  개별적이고 전문적인 관리 업무를 수행하는 프로그램이다. 일부는 시스템의 일부 측면을 초기화하고 구성하기 위해 한 번만 호출된다. 그 외(UNIX 용어로 daemons라고 함)는 영구적으로 실행되어 들어오는 네트워크 연결 응답, 터미널에서 로그온 요청 수락, 로그 파일 업데이트 등의 작업을 처리한다.

 

Kernel Modules

  커널 모듈은 커널의 나머지 부분과 독립적으로 컴파일, 로드/언로드할 수 있는 커널 코드 섹션이다. 커널 모듈은 일반적으로 장치 드라이버, 파일 시스템 또는 네트워킹 프로토콜을 구현할 수 있다. 그리고 모듈 인터페이스를 통해 제3자는 GPL에 따라 배포할 수 없는 장치 드라이버 또는 파일 시스템을 자체 조건으로 작성 및 배포할 수 있다.

  이게 무슨 말이냐면 GPL은 free SW를 위한 라이센스인데 리눅스는 해당 라이센스를 따르고 있다. 때문에 만약 우리가 리눅스의 커널을 변경하여 사업을 하려고 하면, GPL에 따라 모든 소스코드를 공개해야만 한다. 하지만 커널 모듈을 사용하여 리눅스 커널에 기능을 추가한다면 이는 리눅스 커널의 소스 코드와는 독립적인 코드로 처리되기에 GPL을 따르지 않아도 되는 것이다.

  어쨌든 이러한 커널 모듈 덕분에 추가로 장치 드라이버들을 내장하지 않고 표준적이고 미니멀한 커널로 리눅스 시스템을 구성할 수 있게 된다.

Linux System Architecture

fig 9

  fig 9을 통해 전체적으로 정리해보자. 사용자 계층과 커널 계층, 하드웨어 계층으로 나눠진 것을 확인할 수 있다. 사용자 계층에서 어플리케이션시스템 라이브러리를 이용하여 동작한다. 시스템 라이브러리나 어플리케이션이 직접 시스템 콜을 호출할 수 가 있는데 그 이유는 어플리케이션이 OS 서비스를 이용하기 위함이다. OS 서비스(kernel subsystems)가 동작하게 되는데 만약 HW 리소스가 필요하다면 디바이스 드라이버를 통해 HW 리소스를 사용하게 된다.

  이렇듯 리눅스에는 시스템 콜과 디바이스 드라이버라는 2가지의 인터페이스가 있다. 즉 HW 리소스를 필요로 할 때 2개의 인터페이스만 거치면 되므로 속도에 이점이 있다.

  이후에는 리눅스의 5가지 주요 기능인 프로세스 관리, 메모리 관리, 파일 시스템 관리, I/O(장치 드라이버) 관리, 네트워크 관리 중 메모리 관리를 제외한 나머지 특성들에 대해 알아볼 것이다.


자료 출처

  • Abraham Silberschatz, Peter Baer Galvin, and Greg Gagne, “Operating System Concepts (10th Edition) - Wiley 2019

'시스템 프로그래밍' 카테고리의 다른 글

Process and Thread Management  (0) 2021.12.25
I/O Systems and Operations  (0) 2021.11.29
Operating Systems(OS) Overview  (0) 2021.11.05
Linkers and Loaders  (0) 2021.11.01
Assemblers (4)  (0) 2021.10.15

댓글