Home
posts:
CV Courses Publications Experience Projects

2. Background

[2] OS - background

System_overview 즉 CPU가 작업을 하다가(main memory code 수행(그 코드가 내 프로그램이든 OS 코드이든)) 어떤 때는 내 코드, 어떤 때는 1000만개도 넘는 시스템 코드(ex. interrupt)가 돌아감

  1. Code(Disk)->Ram(Memory) : 코드 소스파일이 저장되는 위치는 디스크라면 실행했을 때 실행되는 곳은 램.
  2. Network통신(Ethernet) controller : IO controller CPU가 IO controller로부터 정보를 얻는 법

기본 철학 : Interrupt

IO Controller : 펌웨어(약간.. CPU 미니 버전같은 느낌)가 돌아가는 전용 제어장치 -> 코드를 내부에 넣고, 그 코드를 넣어버림(embedded system이라고 부르는 이유)
프로세서는 프로세스만 계속 돌림
네트워크를 통해서 온 패킷을 OS가 적절한 목표 패킷에 전달하고 싶은데, 이 메커니즘이 interrupt임
조그만 컴퓨터가 있는 것과 비슷하게 취급할 수 있음
** controller는 CPU와 무관하게 스스로 돌아감 -> 펌웨어코드, 컴파일된 특정 코드만 -> active한 주체라고 볼 수 있음(그러나 계속 이것만 돌기에 OS와 다름) 참고로 코드를 만드는 사람은 그 controller를 만든 회사
ex> Network와 연결된 IO controller : 계속 Network만 함(Ethernet(LAN: 패킷 도착을 모니터링하거나 보낼 패킷이 있는지 체크함), Wifi)
ex> Display와 연결된 IO controller : 보여주는 역할만 함
ex> Network controller: ethernet, wifi
ethernet : LAN line : 패킷 도착 모니터링, 또는 보낼 패킷이 있는지 체크함 -> 예시상황 : 옆컴퓨터에서 데이터를 보내려고 함. 문제는 패킷이 온 걸 알때, 프로세스를 어떻게 처리할지임. -> controller는 OS에게 이 사실을 알려야하고, “이 패킷 OS 너가 해당 목표 process에 전달해라”라고 하고 싶음 -> <h1 style="color: #ff0000">Interrupt</h1> -> 모든 IO controller는 CPU interrupt 핀에 연결됨
Interrupt : CPU의 physical pin에 signal을 줌으로서 controller가 알려주는 것
CPU는 모든 instruction cycle 끝에 interrupt를 체크한다.
즉 Interrupt가 active이면 그 순간 CPU는 수행하던 일을 멈추고, IO controller가 요구한 interrupt를 처리한다

  • 패킷(in buffer)을 Main Memory로 OS가 copy
  • CPU는 process를 하다가 Interrupt, 즉 OS 모드로 바뀜(유저가 아니라 운영체제 코드로 jump)
  • buffer내용을 Ram 어딘가로 Transfer해야 종료 -> Interrupt Handling
  • 이 다음에 다시 process로 돌아감

정리하면

CPU는 메인 메모리와 Process사이를 계속해서 돌고, n개의 IO controller는 독립적으로 계속 돌고 있음
이렇게 본인 일만 하다가 할일이 생기면 Interrupt를 통해 CPU에게 부탁 -> OS는 그 할일을 대행

헷갈리는 부분

  • interrupt실행 중 다른 interrupt가 들어올 경우
    • 무시하기
    • priority 기반의 중첩 interrupt(더 높은 우선순위인 interrupt가 들어온 경우 그걸 실행하고 이전 interrupt를 실행하고, 더 낮은 거면 대기)
    • Top Half / Bottom Half
      • top half: 발생 직후에 무조건 실행하는 부분
      • botton half: 발생 직후에 실행 안하는 부분 Real-World PC Abstract(Pentium) Real-World Embedded System Abstract

[2] Hardware Protection

Dual mode operation

: CPU의 동작 모드를 두가지로 나눈다

  • 목적 : incorrect program이 다른 프로그램까지 incorrectly 동작하는 것을 막기 위해서
  • 방법 : OS는 hardware support를 지원해서 user mode, kernel mode 두가지로 모드를 나눈다. 이 방법으로 여러 application fault를 막는다
  • User mode: Process 1, 2, …, user의 execution
  • Kernel mode: System call handler(진입 시점: system call), Scheduler(timer interrupt), Interrupt handler(device interrupt), os의 execution

상황 설명

  1. OS는 최대한, 여러 프로세스를 공평하게 돌아가게 만들고 싶음
  2. 그래서 interrupt가 걸린 순간, Process 1이 CPU가 계속 Process 1만 처리하길 원하더라도 OS는 항상 interrupt걸린 CPU를 처리함
  3. 그렇지만 문제는, Kernel에는 User code들이 쉽게 접근하면 안된다는 점이다. Kernel code는 중요해서, fundamental requirements들이 존재하고, 이를 쉽게 작성하면 안되기에 Kernel code를 보존하고자 한다.
  4. Interrupt는 알아서 kernel 모드로 접근하지만, System call 상태가 위험하다.
  5. 그래서 Hacking이 이루어지는 방식은 허용되지 않은 루트로 kernel mode에 접근한 것이다.

방법 설명

  1. Mode를 나누는 방법은 protected processor register의 status bit으로 표현된다.
  2. 어떤 machine instruction들은 privileged(protected) instruction으로 작성되고, kernel mode에서만 작동한다.(ex. io instructions)

이 경계를 오가는 상황 설명

user program 이 protected된 명령어..같은 걸 call -> kernel service routine에 control을 넘겼는데, kernel이 parameter가 적합하다는 것을 보장할 때 세가지 상황

  • Hardware interrupt(Interrupt)

    : 나와 무관 -> OS가 여러 process를 happy하게 하려면 필요

  • SW interrupt

    (exception) : Main memory ex1> user program을 작동하다가, 내가 요청한 Page가 없다고 생각해보자. 그러면 page fault exception을 내고, 이 때 kernel mode로 진입한다. process가 요구하는 해당 code, data를 main memory에 로드 후 원래의 user에게.. user 관점 : 내꺼 수행 -> system memory에 내 거가 없음 -> kernel이 도와줌 -> exception -> cpu detect ex2> 0으로 나누기 : kernel이 kill ex3> memory 잘못 사용(array가 100개 원소 저장하는데 101에 접근)

  • System call : User mode: user code가 돌 때, explicit하게 call(IO, fork…)등 -> 철저히 user가 원할 때 돈다 (C library등)(그리고 system call의 구현은 sw interrupt로 이루어진다 Case Study : x86 Real mode(과거) : system BIOS는 real mode에서만 동작하므로, boot code는 real mode에서 동작 Segmented memory : all segments는 64KB 크기이다 Protected mode(요즘): 32bit memory address, memory protection, virtual memory paging, interrupt handling… 즉 사용자가 OS의 memory에 접근할 수 있던 real mode와는 다르게, 메모리 보호 기능을 가지고 있다. x86 Protected mode: Privilege Level

Privilege Check!

현재 동작하는 프로그램이 허용되지 않은 구역에 접근하지 못하게 한다. -> Protection Rings. Privilege Ring OS Services 종류

  1. User Services
    • Program execution
    • IO operation: read, write 등
    • File system(read, write, create, delete files) -> 다른 프로세스와 통신
    • communication(process가 실행될 때 information 교환(다른 컴퓨터간, 같은 컴퓨터간))
    • Error detection(0으로 나누기 등)
  2. Resource allocation(효율적으로 자원을 사용.. 예를 들어 CPU, GPU 계산 등)
  3. Accounting(어떤 리소스를 사용하여 billing을 청구할지 계산.. 예를 들어 통신사에서 데이터 요금 청구하는 것처럼)
  4. Protection : 시스템 자원이 잘 조절되고 있는지

System call

A. 정의, 종류 System call이란? 유저가 OS의 기능에 access할 수 있는 함수

  1. Process Management
    • fork : create process
    • waitpid : wait for process
    • execve : createprocess= fork + execve
    • exit :terminate execution
    • kill : send signal
  2. File Management : open, close, read, write, iseek, stat, chmod
  3. File System Manage: mkdir, rmdir, link, unlink, mount, umount, chdir B. 원칙 application과 hardware사이에 extra layer를 넣는 방식이다. 즉, 이 기능만 user에게 open하겠다고 선언해주는 것과 같다.
    이는 kernel과 유저가 통신할 수 있는 유일한 창구와 같다. 즉 user는 kernel의 복잡한 기능을 시스템 콜 없이는 접근할 수 없는 것이다.
  4. 장점
    • 프로그램을 짜기 쉽다. low-level까지 유저가 확인할 필요가 없어진다
    • system 보안이 올라간다. 커널이 요청의 올바른 정도를 체크한다
    • 프로그램 portability가 상승한다. 왜냐하면, Linux와 같은 Unix계열이 있다고 생각해보자. 운영체제마다 system call이 다 다른데Unix는 이미 잘 정의되어있는 system call 에 동의하였기에 Linux 용으로 개발된 앱이 솔라리스와 같은 다른 유닉스 계열에서도 잘 작동한다.
  5. System calls C. POSIX APIs and System Calls
  6. API란: Application Programming Interface의 약자로, 어떻게 주어진 서비스를 획득할 것인지 함수를 정의한 것이다.
    • 예를 들면, malloc(), calloc(), free()와 같은 POSIX APIs는 libc의 brk() 시스템 콜로 구현되어있다.
    • POSIX-compliant : system이 어플리케이션에게 적절한 API 집합을 제공했을 때 -> 즉, 이런 함수를 구현하면 POSIX compliant하다는 것을 정의함
    • 유저 모드 라이브러리 같은 느낌
  7. System call
    • software interrupt를 통한 커널모드 진입 요청
    • integer return
    • 구현 방법 : libc의 wrapper function으로 구현됨
  8. 예시
    • user가 fork() 호출 -> libc.a의 fork()로 진입하는데, CPU가 커널모드로 바뀌면서 IDT(=Interrupt Disrupt Table: gateway함수를 link) system call()을 호출(<h1 style="color: #ff0000">여기부터는 커널모드</h1>) -> call *SYMBOL_NAME(sys_call_table)(,%eax, 4) 호출 -> sys_call_table에서 sys_fork()호출 -> sys_fork 수행
    • 문제없이 완수되면 역순으로, libc.a의 user mode로 들어가서 user task 완료

시스템 프로그램 종류

  • file manipulation : create, delete, copy, rename, print, dump
  • status information : data, df, du, top
  • file modification : editor(vi..)
  • programming language support : (코드를 컴퓨터가 이해할 수 있게)
  • program loading and execution : library와 내 코드 연결 -> Loader, linkage editor, debugger
  • application programs : DBMS, Web browsers, …

헷갈리는 부분 정리

  1. interrupt, exception, trap은 CPU가 정상 흐름을 끊고 핸들러로 들어가는 사건 그 자체
    • interrupt: 외부 이벤트가 cpu에 일을 시키는 경우 발생하는 사건
    • exception: 현재 실행 중인 명령 때문에 발생하는 사건으로, 버그성 예외와 의도된 예외를 모두 포함
      • Intel은 “프로세서가 프로그램 실행 중 에러를 감지할 때 예외를 생성한다”고 설명하고, ARM은 예외를 “정상 실행을 멈추고 예외 핸들러를 실행해야 하는 모든 조건”으로 설명
      • ARM 쪽에서는 interrupt도 exception의 한 종류라고 보는 문서가 많음
    • trap
      • RISC-V에선 trap = interrupt + exception
      • x86, intel: exception의 하위 분류 중 하나가 trap
        • faults, traps, aborts로 분류
        • fault는 보통 수정 가능하고, 수정 후 그 명령을 다시 실행할 수 있는 exception
        • Intel에서 abort는 정확한 원인 명령 위치가 항상 보장되지 않고 재시작이 어려우며 심각한 오류를 보고하는 exception
      • 즉, intel에서는 interrupt, exception(여기에 trap포함, 3개 분류)
      • risc-v에서는 trap = interrupt + exception
  2. system call은 사용자 프로그램이 OS 기능을 요청하는 행위, 즉 커널 서비스를 요청하는 의도적이고 동기적인 성격을 띄는 서비스 요청 인터페이스
    • 즉, syscall은 exception(risc), trap(linux)을 통해 구현됨

3. OS Design

  1. 목표
    • user 목표 : OS가 쉽게 사용할 수 있으며, 쉽게 배울 수 있고, 믿을 수 있고, 안전하며, 빠를 것. 즉 crash없고, safe, 빠르다.
    • system 목표 : 쉽게 디자인할 수 있고, 개발할 수 있고, 유지할 수 있고, 유연하며, 믿을 수 있고, 에러가 없으며, 효율적일 것
  2. Mechanism : 어떻게 할 것인가(time sharing system에서 time slicing)
  3. Policy : 무엇을 할 것인가(time quantum: 구현된 실제 코드)
  4. (디자인1) Layering Approach
    • 배경: 초기에는 기능 위주였는데, OS의 코드가 너무 길어지면서 방대한 코드가 되어버림. OS를 이렇게 기능마다 확장할지 고민하게 됨. SW engineering, module approach를 해보자
    • OS가 여러개의 레이어로 구성되어있음
    • 장점
      • modularity: layer는 각각 자기보다 낮은 레벨의 레이어에 있는 함수와 서비스들만을 사용한다
      • 디버깅, system verification이 쉽다
    • 단점
      • 레이어 정의를 조심해서 해야한다
      • 성능
        • OS에서는 성능이 중요하다. 빠르게 결과를 내고, 전체 퍼포먼스를 내는 것이 중요한 것이다. 최상위에서 최하위까지 너무 많은 레이어가 있으면 성능이 감소한다.
  5. (디자인2) System Implementation
    • 전통적으로 어셈블리어로 썼다면, 요즘은 high-level로 쓴다.
    • 어셈블리어로 짠다면 : 특정 IS(Instruction Set)에 특화된다. 다른 IS에 맞게 짤 수가 없는데, 명령어 내용을 변경해서 Portability를 높이려면 어떻게 해야 할까?
    • 특히, syntax, pointer같은 것들이 너무 많아지는데 이게 거의 암호처럼 되어버린다. semantically 어려운데 이 때, 효율적 구현을 위해 차라리 운영체제를 high level로 짜자는 움직임이 생기고 이게 바로 C 언어이다. OS를 잘짜기 위한 특별한 프로그래밍 언어를 만들고자 했고, 이렇게 만들어진 언어가 C언어인 것이다.
    • High level : 다른 아키텍쳐에 porting이 쉽다.
    • C언어는 high level임에도 매우 효율적이다. 같은 문제를 그 언어의 symbolic으로 프로그래밍 했을 때, C언어로 짜고, 어셈블리어로 짰을 때 C언어와 어셈블리어의 차이가 10%밖에 나지 않은것이다.

Microkernels

  1. Monolithic Kernel
    • 모든 운영체제 system 서비스는 하나의 큰 monolithic kernel에 구현된다.
    • 사실상, 어떤 과정이 다른 과정 어떤 것이든 호출할 수 있다.
    • 우리가 쓰는 대부분의 운영체제
    • 큰 덩치의 커널, 모든 기능이 하나의 커널에 포함된다.
    • 많은 procedure, 하나가 다른 것 부를 수 있다.
    • user는 usermode, kernel mode에서 도는 것의 차이만 있음
    • 커널 서비스가 필요하면 커널 모드로 들어가서 하고 나옴
    • 단점 : 문제가 생기면 debug 어려움
    • 장점 : kernel 수행이 빠르다. layering approach가 필요없다.
  2. Microkernel
    • essential core OS 함수만 커널에 있다.
    • 덜 중요한 서비스와 application은 microkernel에 있으며 이를 user mode에서 수행한다.
    • OS service는 독립된 프로세스들의 집합으로 structured된다.
    • communication은 유저모듈간 message passing으로 이루어진다.
    • 단점 : 좋아보이지만, layering approach의 performance 문제가 생김
      • mode, process 스위치를 위한 서비스를 계속 invoke. 커널 내의 이동이 어려움.
      • user mode에서 수행하기 위한 필수적인 system service
    • 장점 : 확장성, modularity
    • 작은 커널이 디버그하기 쉽게 만들고, 더 효율적으로 만든다
    • 내장형, 초소형에서 사용하기 쉬움

4. Virtual Machine

과거) application program이 ISA + OS라는 특정한 platform에 매여있었음. VM Virtual Machine(VM)이란?

  • real-platform contraint를 없애줌 -> Portability, flexibility 증가
  • SW for cross-platform compatibility VM의 필요성 :
    1. Portability : networked computing에서 필수적임.
  • 모바일이나 무선장치에서 소통한다고 생각해보자. 우리가 consistent한 environment를 가지고 있지 않다. CPU, OS, hardware모두 다 다른 걸 가지고 있을 것이다. JVM(Java VM)은 이를 돌리는 하드웨어, 운영체제가 무엇이든 이 위에서 코드가 돌아간다.
  • CPU 혁신은 오래된 interface에 의해서 막혀있었는데, 왜냐하면 강력한 CPU가 등장해도 기존의 x86 binary를 수행하지 못하는 CPU는 시장에서 쓸모가 없었기 때문이다. 즉 옛날 것을 수정없이 돌리고 싶은 마음이 있었던 것이다.
  • 단일 OS는 security에 문제가 있는데, 생각해보자.. 여러 유저가 하나의 서버를 사용한다면? 만약 해커가 뚫으면 프로세스가 망가짐. 그래서 OS를 철저히 분리하는 게 어떤가?
  • Sandboxing: 원래 OS를 건드리지 않고 커널 내부에 구현. 즉 개발중인 OS가 잘못되어도 원래 운영체제 리부트 없이 VM 튜닝만 하면 됨.
    1. VM Solution
  • mapping virtual guest system, real host system
    1. VM 종류
  • Process VM : virtualization of individual processes, 즉 하나의 os위에서 특정 프로세스만 가상화 1) Run executables of different ISA or OS 2) runtime이라고 부름

    Process Virtual Machine

    : 하나의 프로그램(프로세스)을 실행할 수 있는 가상화된 환경으로, 프로그램이 실행될 때 그 위에서 돌아가는 일종의 가상 컴퓨터. VM emulates ABI(application binary interface). Runtime이라고 불림
    이 때 Runtime (Runtime Environment): 프로그램이 실행될 때 필요한 환경이나 인프라를 뜻하는데, 이게 바로 process virtual machine 역할을 하는 경우가 많음. ex. x86 application을 Alpha system위에서 돌리기

    • runtime(시간) = 코드 실행 중
    • runtime(시스템) = 코드 실행 환경
      • 그 실행 환경이 VM 형태일 수도 있고, 아닐 수도 있다.
  • System VM : virtualization of complete system, 즉 완벽한 os를 host위에서 돌림 1) 전체 OS(es)와 executables run ex. Linux를 Window에서 돌리기 2) VMM, Hypervisor라고 부름 3) Hosted, Stand-alone 으로 구분됨
    • hosted : runs as a process on existing host OS (일반적으로 호스트 OS 위에 가상화 소프트웨어(VMware Workstation, VirtualBox 등)를 설치하고 그 위에서 VM을 실행.)
      • 하드웨어 interaction을 위해서는 host OS에 의존하며, native application도 그대로 있다
      • VMWare Workstation, User-Mode Linux…
    • Stand-alone : VMM on top of bare hardware
      • VMM위에 여러개의 운영체제가 있고, 그위에 각각에 맞는 앱이 있음 -VMWare ESX …

5. Linux

unix history 위에는 UNIX의 진화 지도이다. Linux는 UNIX의 클론이다. Linus라는 사람이 MINIX를 보고 완벽히 오픈소스로 새로운 unix-likely source를 만들었다. GNU : Open Source free software. UNIX-like program이었지만 커널은 없었음. 세상의 모든 프로젝트는 공유가 필요하다는 오픈 소스 철학을 갖고 만들어짐 -> 목표는 컴파일러를 만드는 것이었고, 만들어서 open하였음. -> 많은 것을 해냈는데, OS-kernel이 없어서 UNIX Kernel과 비슷한 것을 만들기로 함. -> Linux 만들어짐

Linux의 장점

  1. Free, Open
  2. Portable from mainframe to handhelds
  3. Compatibility, Efficiency Highly compatible with many common OS
  4. Maturity
  5. Well supported: 거의 모든 하드웨어에서 사용 가능함

Linux의 특징

  1. Monolithic kernel: 고성능
  2. module을 지원함 : 동적으로 커널 코드의 일부를 load, unload하기 위함.
  3. kernel threading : kernel thread란 커널이 자체적으로 처리하는 스레드로, common address space에 독립적으로 scheduled될 수 있는 execution context가 된다.
  4. Multithreaded application support
  5. Preemptive kernel : 급박한 거 먼저
  6. Multiprocessor : CPU 여러개
  7. File system 지원 ** Fig첫번째: Linux Kernel Architecture. UserLevel : syscall로 접근하는데, 이는 Linux라기보다는 대부분의 운영체제 일반적인 내용이다. ** Fig두번째: from https://slidesplayer.org/slide/15031576/ arch가 가리키는 12개의 노드들 : 리눅스의 모든 CPU architecture architecture 종속 kernel code interface