Chapter 2 Operating-System Structures

작성자 작성일
youngmki 2022.04.08

2.1 Operating-System Services 55

OS는 사용자에게 컴퓨터의 프로그램을 쉽고 효율적으로 실행할 수 있는 환경을 제공한다.

일종의 제어 프로그램으로서 사용자 프로그램의 오류나 잘못된 자원 사용을 감시함과 동시에 입출력 장치 등의 자원에 대한 연산과 제어를 관리하게 된다.

OS가 제공하는 서비스는 크게 User 의 편의를 위한 서비스System을 위한 서비스로 나뉘게 된다.

먼저 User를 위한 서비스는 다음과 같다.

1. User Interface

OS에서는 사용자와의 상호작용을 위해 GUI(Graphical User Interface)와 CLI(Command Line Interface)를 제공한다. 동시에 mobile에서는 touch interface를 통해 키보드를 통한 입력을 대체하게 된다.

2. Program execution

Loading: 프로그램의 실행 요청이 들어오는 경우, ROM에서 해당하는 코드나 프로그램 실행 시에 필요한 데이터를 RAM(메인 메모리)으로 가져와 프로그램을 실행한다.

3. I/O operation

프로그램 실행 시에 요구되는 I/O 요청에 대해서도 이를 OS가 다루게 된다. 정확히는 이를 OS 내부의 커널에서 다루게 되며, 이때 효율성(I/O operation을 이후 등장하는 개념인 API로 묶어버림)보안의 측면에서 user는 I/O operating을 다룰 수 없게 된다.

4. File-system manipulation

프로그램의 실행 과정에서 파일을 읽고 쓰는 과정이 수반될 수 있다. OS에서 이를 지원한다. 또한 OS를 통해 이름을 주고 이에 따라 파일을 생성(create)하거나 지우고(delete), 검색(search)하거나 나열(list)할 수 있다. file ownership을 통한 파일에 소유권에 대한 권한 관리(permission management)도 지원한다.

5. Communication

프로그램 실행 시 발생하는 프로세스들은 서로 간의 통신이 필요한 경우가 존재한다. 이를 지원하는 방식으로는 크게 두 가지가 존재한다.

  1. shared memory: 메모리 영역 중 공유가 허용되는 부분을 서로 다른 프로세스가 같이 사용하는 방식
  2. message passing : predefined format을 준수하는 패킷을 통해 정보를 전달하는 방식

6. Error detection

OS는 CPU, 메모리, I/O 기기들과 같은 하드웨어 부품들 뿐만 아니라 user program에서 발생할 수 있는 오버 플로우Segmentation fault, Bus error등과 같은 컴퓨터 사용 전반에 있어 발생할 수 있는 에러를 전반적으로 관리한다.

지금까지는 User 의 편의를 위한 서비스였다면 이후에 등장하는 서비스들은 System을 위한 서비스 목록에 대한 내용이다.

7. Resource allocation

자원 할당의 경우, CPU가 제공하는 대표적인 서비스 중의 하나이다. 프로그램이 실행되기 위해서는 각 하드웨어 자원의 할당이 불가피하다.

자원 할당의 예시로 CPU-scheduling routine가 있다. 이를 통해 OS는 CPU를 사용하는 최적의 방법을 통해 CPU 자원을 할당하게 된다. 이 외에도 printer나 USB 등을 관리하기 위해 기기들마다의 별도의 루틴이 존재하게 된다.

8. Logging

OS를 통해 프로그램이 어떻게 얼마나 컴퓨터의 자원을 사용했는지를 기록하게 된다.

시스템 로그는 단순히 기록을 쌓아두는 용도로 사용되기도 하며 혹은 Accounting(대표적으로 윈도우의 작업 관리자)에 사용되기도 한다.

9. Protection and security

OS는 컴퓨터 자원의 Protection과 Security적인 부분을 지원한다.

2.2 User and Operating-System Interface 58

유저가 컴퓨터 시스템과 상호 작용하는 방법에는 Command Interpreter, GUI(Graphical User Interface) 두 가지가 존재한다.

Command Interpreter

먼저, CLI는 잘 알려진 것처럼, 대표적으로 BASH shell, 윈도우의 명령 프롬프트 외에도 C shell, Korn shell 등이 역사적으로 사용되어 왔다.

쉘의 주요한 목적으로 파일의 create, delete, list, print, copy, excute등이 있는데, 두가지 방법으로 쉘을 구현할 수 있다.

  1. Command Interpreter 내부에 실행 가능한 명령어 세트를 포함하는 방식
  2. system program(/bin 하위의 명령어 파일)을 실행하는 방식: 새로운 명령어를 쉽게 추가할 수 있고, 이때마다 쉘을 변경할 필요가 없다.

GUI

GUI를 통해 더욱 시각적인 방법을 통해서도 컴퓨터와 상호작용할 수 있다.

윈도우의 Desktop에서는 파일의 icon이나 folder를 통해서 Command Interpreter에서 명령어를 입력하며 수행했던 작업들을 수행할 수 있다.

mac OS 의 GUI의 큰 부분을 담당하는 aqua

Linux 의 GUI로 사용될 수 있는 K Desktop Environment(KDE)

Linux 의 GUI로 사용될 수 있는 Gnome

2.3 System Calls 62

시스템 콜을 이해하기 위해서는 1장에서 배웠던 Interrupt라는 개념을 다시 살펴보자

Interrupt를 크게 살펴보면 다음과 같다.

Interrupt라는 큰 범주 아래 다음과 같이 먼저 분류가 된다.

  1. (H/W) Interrupt -> 1장에서 언급이 되었던 Interrupt
  2. (S/W) Trap

이 중에서 우리가 지난 번 1장에서 살펴보았던 Interrupt가 바로 (H/W) interrupt를 의미한다. 우리가 흔히 interrupt라고 부르는 것들은 대부분이 하드웨어에 대한 interrupt를 의미하게 된다.

이 외에도 소프트웨어적으로 CPU에게 보내는 신호가 존재한다. 이를 Trap이라고 한다. Trap 또한 interrupt의 일종으로 software interrupt라고도 불린다. 이 중에서 Trap을 다시 두 개로 분류한다. 바로 Exception과 System call이다.

Exception은 우리가 자주 마주하게 되는 Segmentation Fault, Bus Error, Overflow 등등과 같은 컴퓨터 시스템을 사용하면서 발생하는 에러를 처리하는 Trap이다.

우리가 알아보고자 하는 Trap은 Exception과 함께 Trap의 일종으로 커널의 서비스(대표적으로 ISR로 인한 함수 calling)나 유저 애플리케이션의 실행 시 발생하는 하드웨어 자원 요청 등이 일어났을 때 발생하는 신호로 이해할 수 있다.

User Application의 System Call이 일어나는 과정은 다음과 같다.

  1. 유저 프로세스의 실행 (이때 mode bit 1)
  2. 하드웨어 자원을 요청하기 위한, system call
  3. mode bit를 0으로 바꾸고, system call을 적절하게 처리
  4. mode bit를 다시 1로 바꾸고, 시스템 콜 결과를 user process의 리턴

이때, System call을 일으키기 위해 유저는 앞서 OS에서 미리 정의된 API를 활용하게 된다. API는 곧 다시 함수들을 라이브러리 형식으로 모아둔 형태로 동작한다. 책에서 등장한 libc가 대표적이다. 이렇게 API로 커널 영역과 User 영역을 분리함으로써, 애플리케이션 프로그래머는 커널 이하 부분의 구현을 이해하지 않아도 되고, 커널은 외부에서 수정, 편집이 불가능하여 보안적으로 이점을 갖게 된다.

또한 직접 시스템 호출 대신 API를 사용하면 서로 다른 시스템 간의 프로그램 이식성이 향상된다. API는 시스템 호출 인터페이스를 통해 적절한 시스템 호출을 수행하게 되며, 테이블을 룩업함으로써 특정 번호가 매겨진 시스템 호출이 이뤄진다.

Interrupt만큼이나 System Call 상당히 많이 이뤄진다는 점을 기억해야 한다. 하드웨어의 접근이 발생할 때마다 그에 따라 당연히 System Call이 발생한다고 생각하면 그 빈도를 유추해볼 수 있다.

System Call이 가능하게 하는 요소로 RTE라는 것이 등장한다. RTE는 프로그램의 실행에 관여하는 Library와 Loader등을 포함한다. 중요하게 봐야할 점으로 RTE는 system-call interface를 포함하고 있다는 점이다.

System Call Interface는 앞서 1장에서 살펴보았던 Interrupt Vector Table의 System Call 버전이라고 생각하면 되겠다.

해당 과정을 더 자세히 살펴보자

1. 유저 프로그램이 시스템 콜(System Call)을 호출한다. my code에서 printf()는 I/O instruction과 관련된 함수이다. 해당 함수는 library에 있는 함수이고 실제 동작할 때 library는 I/O를 하기 위해 시스템 콜을 호출할 것이다. 이는 커널에게 I/O instruction의 수행을 부탁하다는 의미이다.

2. 유저는 CPU 제어권을 뺏기고 더 이상 user mode에서 실행할 수 없게 되는 Trap에 걸린다. Wrapper Routine은 Trap으로 넘어갈 내용을 준비하고 실질적으로 Trap을 일으키는 공간이다. Trap을 일으키기 전 Prepare parameter들을 준비하는데 이 중에서 가장 중요한 것이 아래 나올 System call number이다.

* 커널은 System call function의 시작 주소를 담고 있는 배열을 가지고 있다. 이 배열이 System call table이고 배열의 index 번호가 System call number이다. 예를 들어 System call number가 4이면 커널에서 write() 함수를 불러 작업을 수행한다는 뜻이다.

3. 하드웨어가 유저 모드에서 커널 모드로 모드 비트(mode bit)를 바꾼다. chmodk(change CPU mode to Kernel) 명령을 통해 CPU의 비트 모드가 유저 모드에서 커널 모드로 변경되게 된다. chmodk가 실행되면서 프로그램은 런타임 중 Trap에 걸려 커널 영역으로 가게 된다.

4. 하드웨어가 sys_call() 이라는 Trap Handler로 가게 되고, Trap Handler는 커널 안의 assembly function을 수행한다.

*커널 안의 모든 System call function의 이름은 sys_로 시작한다.

5. 지금까지 유저 프로그램에서 진행했던 단계를 저장한다. 커널 쪽 일이 다 끝나면 시스템 콜을 호출했던 곳으로 돌아가서 다시 진행을 해야하기 때문에 저장을 한다.

6. System call number가 커널 안에 System call table에 있는 번호에 맞는 번호인지 확인한다. 예를 들어 Wrapper Routine에서 설정한 write() 함수의 시작 주소를 뜻하는 index 번호, 즉 System call number가 4번 인 것과 실제 커널 안의 System call table에서 write() 함수의 시작 주소가 있는 index 번호가 일치하는지 비교한다.

7. 맞는 번호라면 System call function의 주소를 가져온 후 작업을 수행한다.

8. 커널 쪽 일이 다 끝나면 다시 시스템 콜을 호출했던 유저의 영역으로 돌아가고 모드 비트를 커널 모드에서 유저 모드로 다시 전환한다.

IDT

  • 시스템 콜을 직접적으로 호출하는 대신 간접적으로 IDT(Interrupt Descriptor Table)를 거쳐서 해당 call이 System Call임을 명시
  • fork는 라이브러리 함수이고, 이에 대해서 system call num을 명시(INT $0x80)하는 형태로, 이후 시스템 콜 테이블에 따라 유저 스페이스에서 커널 스페이스로의 접근이 가능
  • IDT에서 0x80이므로 해당 Call이 System Call(software interrupt)임을 인지하고, SYMBOL_NAME에 따라서 해당하는 system call table로 이동
  • 시스템 콜은 테이블 형태(sys_call table)로 정의되어 있다. 이를 system call interface라고도 한다.

*IDT는 User 영역과 Kernel 영역을 구분해준다는 점을 주목하자.

System call이 이뤄질 때는 단순히 API의 호출로만 이뤄지지 않는다. 호출과 동시에 다양한 매개변수를 전달할 필요가 있다. 이때 매개변수를 넘기는 방법에는 총 세가지 방법이 존재한다.

  1. Parameter를 레지스터에 직접 넘긴다.
  2. Table, Block으로 여러 정보를 Wrap해서 해당하는 주소를 넘긴다.
  3. 위의 두 방식을 혼합한다(Linux에서 채택, *매개 변수가 5개를 넘어가면, 즉 6개 이상부터는 2번을 사용한다. 이 외에는 1번을 채택, 매개변수의 개수를 조절할 필요가 있는 이유)

Types of System Calls

System Call를 다음 6개의 카테고리로 분류할 수 있다.

  1. process control
  2. file management
  3. device management
  4. information maintenance
  5. communication
  6. protection

process control

(1) 끝내기(end), 중지(abort)

  • 실행 중인 프로그램은 수행을 정상적으로(end()), 혹은 비정상적으로(abort()) 종료할 수 있어야 한다.

(2) 적재(load), 수행(execute)

  • 한 프로그램을 실행하고 있는 process나 job이 다른 프로그램을 load 하고, execute 하기를 원할 수 있다.
  • load된 프로그램이 종료 되었을때, 어디로 Control을 Return해야 하는가에 대한 질문이 생길 수 있다.
  • 만약 Control이 기존 프로그램으로 돌아간다면, 기존 프로그램의 Memory image를 save 해놔야 한다.
  • 만약 두 프로그램이 병행하게 수행된다면, Multi-programming 될 새로운 job이나 process를 생성한 것이다.

(3) 프로세스 생성, 프로세스 종료

  • new job, process 혹은 set을 생성한다면, 이들의 execution을 control 할 수 있어야 한다.

(4) 프로세스 속성 획득, 속성 결정

  • job의 priority, maximum allowable execution time 등을 포함하여 속성을 결정할 수 있어야 한다.

(5) 시간 기다리기(wait for time)

  • new job이나 process를 생성한 후에는, 이들의 실행을 끝나기를 기다릴 필요성이 있을 수 있다.

(6) wait event, signal event

  • 특정 사건이 일어날 때까지 기다릴 수 있을 수 있어야 한다.(wait event)
  • 그 사건이 일어나면 신호를 보낼 수 있을 수 있어야 한다. (Signal event)

(7) Allocate and free memory

  • 둘 이상의 프로세스들이 공유하는 데이터들에 대해 일관성을 보장해야 할 필요성이 있을 수 있다.
  • 이에 운영체제는 종종 프로세스가 공유 데이터를 lock 할 수 있는 System Call을 제공한다.

File Management

(1) 파일 생성(Create File), 파일 삭제(Delete File)

  • 이 System Call은 보통, 파일 이름이나 파일 속성의 일부를 요구한다.

(2) 열기 (Open), 닫기(Close)

  • 생성된 파일들을 사용하기 위해서는 열기(Open) System call을 사용한다.
  • 파일을 더 이상 사용하지 않을 때는, 파일 닫기(Close) System Call을 사용한다.

(3) 읽기, 쓰기, 위치 변경(Reposition)

  • 파일을 열은 뒤에, 읽기(Read()), 쓰기(Write())를 할 수 있다.
  • 위치 변경(Reposition(), (ex) 파일의 끝으로 건너뛰기)를 할 수 있다.

(4) 파일 속성 획득 및 결정 (get file attributes, set file attributes)

  • 파일 속성에 대한 정보를 얻을 수 있고, 변경할 수 있다.

Device Management

  • 프로세스는 작업을 계속 수행하기 위해 Main Memory, Disk Drive 등의 추가 자원을 필요로 할 수 있다.

(1) 장치를 요구(Request Devices), 장치를 방출(Release Devices)

  • 다수의 사용자가 동시에 사용하는 시스템은 독점적인 device 사용을 보장받기 위해 Request 해야 한다.
  • device의 사용이 끝나면, 반드시 Release 해야 한다.

(2) 읽기, 쓰기, 위치 변경(Reposition)

  • 일단 device를 request 하여 Allocated 되면, 해당 device를 Read, Write, Reposition 할 수 있다.
  • I/O 장치와 파일들 간에는 유사성이 많기 때문에, UNIX를 포함한 많은 운영체제가 이들 둘을 통합한 파일-장치 구조(file device structure)로 결합하였다.

(3) 장치 속성 획득, 장치 속성 결정 (get device attributes, set device attributes)

(4) 장치의 논리적 부착 또는 분리 (Logically attach or detach devices)

몇몇 OS에서는 File system과 Device management를 동일 선상에 두고 처리하는 경우도 많다. 예를 들어 Device를 관리할 경우 이를 특수한 형태의 file name으로 인지하는 경우가 존재한다.

Information Maintenance

(1) 시간과 날짜의 설정과 획득 (get time or date, set time or date)

  • 대부분의 시스템은 현재 시간(time()), 날짜(date())를 되돌려 주는 System Call을 가지고 있다.

(2) 시스템 데이터의 설정과 획득 (get System data, set System data)

  • 현재 사용자 수, OS의 Version number, Free Memory와 같은 시스템에 관한 정보를 알려주기도 한다.

(3) 프로세스, 파일, 장치 속성의 획득 (get process, file, or device attributes)

  • 운영체제는 현재 실행되고 있는 프로세스들에 관한 정보를 가지고 있다.

(4) 프로세스, 파일, 장치 속성의 설정 (set process, file, or device attributes)

  • 운영체제는 현재 실행되고 있는 프로세스들에 관한 정보에 접근하여 재설정할 수 있다.

Communication

  • Communication에는 Message Passing과 Shared Memory model이 있다.

1. Message Passing Model

  • 통신하는 두 process가 정보를 교환하기 위해 서로 메시지를 주고받는다.
  • Message는 두 프로세스 사이에서 직접 교환되거나, mailbox를 통해 간접적으로 교환될 수 있다.

2. Shared Memory Model

  • process는 다른 process가 소유한 Memory 영역에 대한 접근을 하기 위해 System call을 사용한다.
  • 정상적으로, OS는 한 process가 다른 process의 memory를 접근하는 것을 막으려고 한다.
  • 이에, Shared Memory Model에서는 이런 제한을 제거해야 할 필요성이 있다.
  • 대부분의 System은 두 가지(Message Passing, Shared Memory) 모두를 구현한다.
  • Message Passing은 Conflict가 없기 때문에, 소량의 데이터를 교환하는데 좋다.
  • SHared Memory는 최대 속도와 편리한 통신을 할 수 있지만, 동기화 부분에서 문제점이 있다.

(1) 통신 연결의 생성, 제거 (create, delete communication connection)

(2) 메시지의 송신, 수신 (Send, Receive Messages)

(3) 상태 정보 전달 (Transfer status information)

(4) 원격 장치의 부착(Attach) 및 분리(Detach)

Protection

  • 과거에, Protection은 다수의 사용자를 가지는 다중 프로그램 시스템에서만 고려되는 문제였다.
  • Networking과 Internet의 출현으로, 서버에서 휴대용 컴퓨터까지 모든 컴퓨터 시스템에서 고려해야 한다.

2.4 System Services 74

  • 시스템 프로그램은 시스템 유틸리티(System Utility)로 알려져 있으며, 프로그램 개발과 실행을 위해 보다 편리한 환경을 제공한다.

  • 시스템 프로그램(System Program)은 파일 관리, 상태 정보, 파일 변경, 프로그래밍 언어 지원, 프로그램 적재와 수행, 통신, 백그라운드 서비스의 범주로 분류할 수 있다.

  • 대부분의 운영체제는 시스템 프로그램과 함께 일반적인 문제점을 해결하거나, 일반적인 연산을 수행하는데 유용한 프로그램들도 제공한다.

  • 이러한 Application Program에는 Web Browser, Word Processor, DataBase System 등이 포함된다.

2.5 Linkers and Loaders 75

링커와 로더는 많은 부분이 연관되어 수행되지만 다른 작업들을 수행한다.

  • 프로그램 로딩: 이것은 프로그램을 실행가능한 상태로 만들기 위해 Disk로부터 프로그램 이미지를 읽어서 메인 메모리로 복사하는 것을 말한다.
  • Relocation: 컴파일러와 어셈블러는 각각의 입력 파일들로부터 시작 주소가 제로인 오브젝트 코드를 생성한다. Relocation이라는 것은 프로그램의 각기 다른 부분들(코드와 데이터)에 대해 로드되는 주소를 할당하는 것이다. 이러한 작업은 같은 타입(코드 혹은 데이터)으로 정의된 모든 구간들을 하나의 구간으로 합치고, 이러한 구간들이 런타임때 올바른 주소를 가리킬 수 있도록 조정하는 것을 말한다.
  • 심볼 해석: 프로그램은 다양한 하위 프로그램들도 구성된다. 하나의 상위 프로그램이 다른 하위 프로그램을 참조하는 것은 심볼이라는 것을 통해 이뤄진다. 링커의 작업은 이러한 심볼의 위치를 알아내어 상위 프로그램의 오브젝트 코드에 하위 프로그램의 주소를 기입하여 참조를 해석하도록 한다.

참고) 오브젝트 파일

오브젝트 파일이란 오브젝트 코드로 구성된 파일을 말한다. 컴파일러는 컴파일 과정에서 C언어나 Java와 같은 High level언어로 구성된 소스코드를 기계어나 어셈블리어로 변환시킨다. 이 같은 과정을 통해 변환된 기계어를 컴퓨터가 이해할 수 있는 언어, “Object code” 또는 “목적 코드“라고 부른다.

하지만 이렇게 만들어진 오브젝트 파일은 실행가능하지 않다.

실행 가능한 오브젝트 파일을 만들기 위해서는 링커의 역할이 필요하다.

일반적인 재배치 가능한 오브젝트 파일은 내부에 심볼 테이블을 가지고 있다. 심볼 테이블에는 소스코드에서 참조되는 심볼들의 이름과 주소가 정의되어 있다.

하지만 오브젝트 파일의 심볼 테이블에는 해당 오브젝트 파일의 심볼 정보만을 가지고 있어야 하기 때문에, 다른 파일에서 참조되는 심볼들의 이름과 주소가 정의되어 있다.

링커는 오브젝트 파일에 있는 데이터의 주소나 코드, 심볼들을 참조하고, 불완전한 데이터, 코드와 심볼들을 묶어 연결하고 여러 소스파일 간에 심볼들을 공유하여 기계어로 된 실행 파일인 “실행가능한 오브젝트 파일”을 생성한다.

심볼 테이블 출력 예시

링커가 하는 일은 크게 2 가지가 있다.

1. Relocation

오브젝트 파일에 있는 데이터의 주소나 코드 메모리 참조 주소를 알맞게 재배치한다.

2. Symbol Resolution

오브젝트 파일에 같은 이름의 심볼이 존재하고 있을 때, 어떤 심볼을 사용할 것인지 결정한다.

오브젝트 파일은 3가지 종류가 존재한다.

Relocatable object file

하나의 파일을 컴파일하였을 때 나오는 일반적인 오브젝트 파일이다. 실행가능한 오브젝트 파일을 만들기 위한 재배치 가능한 심볼 정보(symbol table)들을 가지고 있다. 실행 가능한 오브젝트 파일을 만들기 위해 다른 relocatable file들과 결합될 수 있다.

Executable Object file

링커가 여러 개의 오브젝트 파일들을 연결시켜서 만들어낸 실행 가능한 오브젝트 파일이다. 직접 메모리에 로드, 다운하여 실행될 수 있는 프로그램 파일이다. 메모리로 직접 로드되어 실행될 수 있는 것을 말한다.

Shared Object file

재배치 가능한 오브젝트 파일의 특별한 타입 , 동적인 컨텍스트들을 링크할 수 있는 코드와 데이터를 가지는 파일이다.

참고) ELF란

링커는 오브젝트 파일에 있는 주소나 코드, 심볼들을 참조하고, 불완전한 데이터와 코드, 심볼들을 묶어 연결하고, 여러 소스 파일 간에 이를 공유하여 기계어로 된 실행파일인 “Executable Object File”을 생성한다. 이와 같은, 링크 과정을 위해서는 오브젝트 파일이 그 자체로써 링커에게 정보를 제공할 수 있어야 한다.

모든 파일이 일정한 포맷 형식을 가진다면 어떤 오브젝트 파일이더라도 링커는 정보를 쉽게 읽어올 수 있다. 리눅스와 같은 유닉스 시스템들은 파일 포맷으로 ELF, Executable and Linkable File format 형식을 따른다.

실행 가능한 오브젝트 파일(Executable Object File)의 ELF 포맷 형식은 다음과 같다. 왼쪽은 링커가 오브젝트 파일을 링크하기 전의 Linking View 이며, 오른쪽은 링크가 끝나고 난 후에 생성된 실행가능한 ELF 오브젝트 형식인 Execution View이다.

ELF에 해당하는 파일 리스트

none("예를 들어 main 이라는 실행 가능한 파일"), .axf, 
.bin, .elf, .o, 
.out, .prx, .puff, 
.ko, .mod, and .so

Section과 Segment

Section은 타겟 PC에 프로그램을 다운로드하기 쉽도록 데이터의 유형에 따라 자세하게 나눠놓은 것이라고 생각하면 되며, Segment는 그러한 Section들의 집합이라고 생각하면 된다.

참고로, 메모리 구조에서 메모리를 크기가 서로 다른 논리적 단위인 세그먼트(Segment)로 나누는 방식을 “세그먼트 기법”이라고 한다. 메모리를 일괄적으로 같은 크기로 나누는 것이 아니라, 데이터의 용도와 크기에 따라 나누는 방식이다.

각각의 구성 요소는 다음과 같이 구성되어 있다

노션 정리 링크: ELF 구성요소들의 의미

추가로 컴파일러에 따라서 데이터를 RW, ZI, RO 세가지 형태로도 구분하는데

  • RW: Read-Write, 초기값이 설정되어 있는 전역변수, static 변수
    • .data Section과 유형의 속성이 같다.
  • ZI: Zero-Initialized, 초기값이 0인 전역 변수 또는 프로그램 실행시에 0으로 초기화되는 전역 변수, 그리고 프로그램에 의해 Stack, Heap 메모리에 공간을 할당받는 지역변수
    • .bss Section과 유형의 속성이 같다
  • RO: Read-Only, 수정이 불가능한 코드, const 전역변수
    • .text, .rodata Section과 유형의 속성이 같다.

더 나아가, RAM과 ROM 메모리가 모두 있는 타겟 시스템에서 RW, ZI, RO 세가지 형태의 데이터가 어떤 메모리에 저장되는 것이 효율적인지 알아보자.

  • RW는 ‘초기값’을 가지고 있는 데이터 이므로 ROM 메모리에 저장하되, ‘변수’로써 사용하기 위해 프로그램 실행시에 수행되는 스타트업 코드(Startup Code)에서 RAM 메모리로 불러와 사용한다.
  • ZI는 초기값으로 0을 가지고 있는 변수들이므로 굳이 ROM에 저장하지 안하도 된다. 스타트업 코드를 수행하면서 0으로 초기화하여 RAM 메모리로 불러와 사용한다.
  • RO는 변하지 않아야 하는 데이터이므로, 시스템의 상태에 관계없이 데이터가 유지될 수 있도록 ROM에 저장한다. ROM에 직접 접근하여 데이터를 읽거나, RAM으로 복사하여 사용한다.

2.6 Why Applications Are Operating-System Specific 77

2.7 Operating-System Design and Implementation 79

설계 목표 (Design Goals)

  • 요구 조건은 근본적으로 사용자 목적과 시스템 목적의 두 가지 기본 그룹으로 나눌 수 있다.
  • 사용자 입장에서, 시스템은 사용하기에 convenient to use, easy to learn, Reliable, safe, fast 해야 한다.
  • 시스템적 입장에서, 운영체제는 설계, 구현, 유지 보수가 쉬워야 하며, 적응성, 신뢰성, 효율성 등을 가져야 한다.

기법과 정책 (Mechanisms and Policies)

  • 기법(Mechanism)은 how to do something(어떻게 할 것인가)을 결정하는 것이다.
  • 정책(Policy)는 what will be done(무엇을 할 것인가)를 결정하는 것이다.

구현 (Implementation)

  • 초창기 운영체제는 어셈블리어로 작성되었으며, 현재 대부분의 운영체제는 고급 언어로 작성된다.
  • 운영체제를 고급 언어로 구현하는 것에 대한 단점은 속도가 느리고, 저장 장치가 많이 소요되는 것이다.
  • 하지만, 고급언어를 통한 sophisticated optimization을 수행하고, 좋은 자료구조와 알고리즘을 사용함으로써 위의 문제를 해결하고, 성능 향상을 이룰 수 있다.

2.8 Operating-System Structure 81

Monolith Structure

복잡하고 모듈화된 OS구조의 반대 개념이다.

어플리케이션 및 모든 커널 서비스들이 같은 주소 공간에 위치하기 때문에, 각 컴포넌트 간의 커뮤니케이션이 비교적 효율적이다.

하지만 어느 한 부분이 수정되면 전체를 다시 컴파일 해야하고, 한 컴포넌트에서 발생한 문제가 전제 시스템의 문제가 될 가능성이 크다.

  • 모듈로 나눠지지 않는다.
  • 인터페이스와 기능 수준이 잘 구분되어있지 않다.
  • ex) MS-DOS

Layered Approach

Monolithic의 단점을 보완할 수 있는 계층 구조이다.

상위 계층은 하위 계층을 호출되고, 하위 계층은 상위 계층에게 호출되어 정보를 제공한다.

각 계층별로 구분되어 있기 때문에 Monolithic 구조에 비해 어느 한 부분에 발생한 문제를 해결하기 비교적 쉽다. (그 계층에 대해서만 수정 및 재컴파일 하면 되기 때문)

하지만, 개념적으로 어디서부터 어디까지를 한 계층으로 둘 것인지 정의하는 것이 애매하고, 계층 간 정보를 주고받는 과정에서 발생하는 오버헤드로 효율은 비교적 낮은 편이다.

  • 운영체제를 여러 계층으로 나누는 방식
  • 상위 계층은 하위 계층의 인터페이스를 이용
  • 최하위 계층 : 하드웨어. 최상위 계층 : 유저 인터페이스
  • 장점 : 인터페이스만 유지하면, 하위계층은 상위계층과 무관하게 수정이 가능하다.
  • 단점 : 근데 계층구분이 어렵다.

Microkernel

단일 구조인 UNIX가 점점 확장되면서, 커널이 점점 더 커지고 관리하기가 어려워지기 시작했다.

이에 따라 커널에서 중요하지 않은 구성 요소는 제거하여 사용자 수준 프로그램으로 구현한 마이크로커널 형태의 접근이 생겨났다. 커널의 핵심 기능들만 유지시키고, 다른 기능들은 User-mode에서 동작하도록 상위 계층으로 올려 구현한다.

위 그림에서 CPU 스케줄링, 메모리 관리 등의 핵심 기능은 마이크로커널 내에 유지되고, 파일 시스템, 장치 드라이버 등의 기능은 User-mode 프로그램으로 올라간 것을 볼 수 있다. 이로써 커널의 규모를 축소시킨 구조이다.

커널의 확장성과 이식성이 증대하지만, 성능에 대한 영향이 단점으로 작용할 수 있다.

  • 커널에 꼭 필요한 기능만 가지도록 구성된 커널
  • 장점 : 확장이 용이, 높은 안정성, 높은 신뢰성, 높은 이식성

Modules

단일 구조인 UNIX가 점점 확장되면서, 커널이 점점 더 커지고 관리하기가 어려워지기 시작했다.

이에 따라 커널에서 중요하지 않은 구성 요소는 제거하여 사용자 수준 프로그램으로 구현한 마이크로커널 형태의 접근이 생겨났다. 커널의 핵심 기능들만 유지시키고, 다른 기능들은 User-mode에서 동작하도록 상위 계층으로 올려 구현한다.

위 그림에서 CPU 스케줄링, 메모리 관리 등의 핵심 기능은 마이크로커널 내에 유지되고, 파일 시스템, 장치 드라이버 등의 기능은 User-mode 프로그램으로 올라간 것을 볼 수 있다. 이로써 커널의 규모를 축소시킨 구조이다.

커널의 확장성과 이식성이 증대하지만, 성능에 대한 영향이 단점으로 작용할 수 있다.

대부분의 현대 운영체제(LInux, Mac OS X, Windows)는 LKM(Loadable Kernel Modules)를 구현한다.

Hybrid Systems

대부분의 실제 운영 체제는 위에서 설명한 운영 체제 설계 모델의 순수한 예가 아니다. 실제 시스템은 일반적으로 서로 다른 설계 모델이 혼합되어 있다.

mac OS, iOS system은 darwin으로 알려진 커널 환경을 가지고 있다.

다윈은 다양한 설계가 혼합된 하이브리드 시스템의 한 예이다. mach 마이크로커널과 mach 마이크로커널을 통해 구현된 BSD 유닉스 커널을 가지고 있다.

프로그램은 mach system call을 통해서, 혹은 BSD system call를 통해서 커널과 통신할 수 있다.

Android 시스템 내부에 일부 수정된 Linux 커널이 존재한다.

Window Subsystem for Linux

다른 운영 체제를 에뮬레이트하는 일부 서브시스템을 가지고 있다. 예를 들어 윈도우즈 시스템에서 기본 리눅스 응용 프로그램을 실행할 경우 이를 가능하게 하는 Linux를 위한 subsystem이 존재한다. 리눅스 환경에서 실행 중인 응용 프로그램이 시스템 호출을 할 때 기본적으로 윈도우즈 시스템 호출로 이를 변환한다.

2.9 Building and Booting an Operating System 92

운영체제 생성(Operating-System Generation)

  • 운영체제는 다양한 주변 구성을 가진, 여러 부류의 기계에서 수행되도록 설계되는 것이 일반적이다.
  • 이 경우, 시스템은 각 specific computer site를 위해 구성되거나 또는 생성되어야 한다.
  • 이 절차를 시스템 생성(SYSGEN)이라고 한다.

**시스템 부트(System Boot)**

  • 운영체제가 생성된 이후에는, 하드웨어에 의해 사용 가능해야 한다.
  • 대부분의 컴퓨터 시스템에서는 Bootstrap Program이 커널을 찾은 뒤, Main Memory에 load 하고,

수행을 시작한다.

  • PC와 같은 일부 컴퓨터 시스템은 단순한 Bootstrap Program이 좀 더 복잡한 Bootstrap Program을

디스크로부터 적재하고, 이 Bootstrap Program이 다시 커널을 load 하는 2단계 절차를 사용한다.

  • 컴퓨터가 전원을 켜거나, 재부팅 등의 사건을 받으면 명령 레지스터는 미리 지정된 메모리 위치를 가리키고,

그곳에서부터 실행을 시작한다. (해당 위치에는 최초의 Bootstrap Program이 존재한다.)

  • RAM은 시스템 시작 시에 알 수 없는 상태가 되기 때문에, Bootstrap Program은 ROM 안에 저장된다.
  • bootstrap program은 먼저, 기계의 상태를 진단하여 이상이 없는지 확인한다.
  • 이후, CPU Register, device Controller, Main Memory 등 시스템 전반에 걸쳐 초기화한다.
  • 위의 작업이 끝나면, bootstrap program은 운영체제를 시작한다.
  • 휴대전화, 태블릿 등의 시스템들은 운영체제의 크기가 작은 경우가 많아서 운영체제 전체를 ROM에 저장한다.
  • 이 방식은 bootstrap code가 변경되면, ROM을 변경해야 한다는 점이다.
  • 이 문제를 해결하기 위해 쓰기가 가능한 EPROM을 사용할 수 있다.

컴퓨터를 켜면 부트스트랩 프로그램(Bootstrap program)이라는 초기화 프로그램이 실행된다. 이 프로그램을 컴퓨터의 ROM(Read-Only Memory)이나 EEPROM(Electrically Erasable Programmable Read-Only Memory)에 저장되어 있으며, 주로 펌웨어(Firmware)라고 불린다. 부트스트랩 프로그램은 시스템을 초기화하고, 부트로더(Boot loader)를 실행한다. (멀티부팅 컴퓨터의 경우 부트로더가 여러 운영체제를 가리키고 있는데, 이 경우엔 어떤 운영체제를 실행할지 선택해야 한다.) 그리고 부트로더는 최종적으로 운영체제를 실행하게 된다.

부트 스트랩의 경우 BIOS, UEFI, GRUP 등이 책에 등장한다. 먼저, BIOS의 경우 컴퓨터가 실행되면 실행되는 initial boot loader이다. 이러한 initial boot loader는 일부 초기화 작업 등을 진행한 이후 작업을 이후의 boot loader에게 넘기게 된다. 대부분의 부팅 작업은 GRUP과 같은 이후의 boot loader에서 이뤄지게 된다.

커널이 로드, 실행되면 시스템과 사용자에게 서비스를 제공해야 한다. 이때 일부 서비스는 커널 외부에서 제공되는데, 이들은 부팅할 때 메모리에 로드되는 시스템 프로세스(System processes)나 시스템 데몬(System daemons)이다. UNIX의 경우 첫 시스템 프로세스는 init이며, 이 프로세스는 또 다른 데몬들을 실행시킨다. 데몬은 프로세스로 백그라운드에서 돌면서 시스템 로그는 남기는 등의 여러 작업을 한다. 이러한 과정이 끝나면 시스템이 완전히 부팅되고, 이벤트가 발생하기를 기다리게 된다.

대부분의 OS의 경우, 부팅 과정에서 메인 메모리를 접근하는 방식이 다소 특이하다. initramfs 라는 임시 RAM file 시스템에서 작업을 진행하다가, 이후 부팅 프로세스가 완료되면 이를 Real root file system에 옮기게 된다.

2.10 Operating-System Debugging 95

  • 디버깅(Debugging)란 Hardware와 Software에서의 시스템 오류를 발견하고, 수정하는 행위이다.
  • 장애 분석(Failure Analysis)
  • 프로세스가 실패했을 때, 운영체제는 프로세스가 사용하던 메모리를 캡처한 뒤 ‘core dump’를 취하고

이후의 분석을 위해 파일로 저장한다.

  • 커널 장애는 Crash라고 불린다. 해당 오류 정보는 로그 파일에 저장되고, 이때의 메모리의 상태가 ‘Crash dump’에 저장된다.

  • 성능 조정(Performance Tuning)
  • 병목(Bottlenecks) 지점을 제거함으로써 성능을 향상시키려고 하는 것도 디버깅에 포함된다.
  • 이에 병목 지점을 발견하기 위하여 시스템 성능을 감시할 수 있어야 하며, 시스템 동작을 측정하고 표시할 수 있는 방법을 가지고 있어야 한다. ((ex) Windows 작업 관리자.)

2.11 Summary 100

Practice Exercises 101

https://codex.cs.yale.edu/avi/os-book/OS10/practice-exercises/PDF-practice-solu-dir/2.pdf

Further Reading 101

results matching ""

    No results matching ""