2. Backgrounds & Preliminaries
- -
Computer system Architecture
Main processor / memory / IO device
→ 이 장치들이 bus로 연결되어 있음
CPU는 메인 메모리에 있는 것들을 하나씩 가져와서 시작
PC: 현재 실행하고 있는 주소를 담고 있는 register
모든 IO device는 controller가 있음, 일명 펌웨어로 돌아가는 cpu는 아니지만 컨트롤러이다.
https://www.techopedia.com/definition/11356/io-controller-ioc
→ I/O controller한테 명령을 시키고, 그것이 끝날 때 알려달라고 요청. 그래서 interrupt라는 개념이 등장. 매 instruction이 끝날 때마다 interrupt를 체크하고, 만약 interrupt가 왔으면 kernel가 원래 멈췄던 작업을 진행시킴
I/O controller하고 processor는 독립적으로 움직임
- Processor : 범용적으로 이용
- I/O controller: 특정 작업만 수행하도록 만들어진 chip (펌웨어가 들어가 있음)
⇒ 단 os는 processor위에서 돌음 (I/O controller에서는 kernel이 돌지 않음)
Dual-mode operation
Abstraction
왜 Dual-mode operation을 하는가?
kernel mode의 os 코드는 매우 중요하고, 이에 기본적으로 OS는 user mode의 모든 process를 믿지 못한다. 이에 OS를 보호하기 위해 만들어진 개념이 Dual-mode operation이다. user mode의 경우 예민한 것은 건들지 못하고, 예민하고 중요한 것들은 kernel mode에서만 변경할 수 있다.
Mode : cpu가 도는 모드 (어떤 경우에는 user mode 즉 유저가 짠 코드가 도는 상황, 어떤 경우에는 kernel mode로 돌음 즉 이미 짜져 있는 kernel operation이 되기 위한 코드가 돌아가는 상황)
예를 들어 c언어에서 printf, scanf는 system call이다. (system call : system을 call하는 것) 이러한 기능은 커널 안에서 구현함. (대략 유닉스에서 system call은 300개 정도 존재)
코드를 돌리다가 시스템의 기능이 필요하면 system call을 써서 kernel에게 해당 기능을 요청하는 것.
system call을 부르면 kernel로 점프해서 해당 system call에 대응되는 handler를 돌린다. 그리고 해당 작업이 다 끝나면 user mode로 복귀. (만약 해당 system call이 I/O와 관련있는 경우 복귀하지 못할 수 있다.)
특정 system call의 경우 매우 느림. 예를 들어 I/O read/write.
I/O가 일하는 동안 다른 프로세스가 돌게끔 함(multi-process)
시간을 어떻게 account할 것인지?
timer라는 것이 존재함. cpu 밖에 pit라는 하드웨어가 있음. 주기적으로 cpu한테 interrupt를 줌. (특정 시간마다 interrupt를 주는 것) 그에 따라 반드시 kernel mode로 이동하게 됨. 예를 들어 10ms의 시간을 할당했다는 것은 얼마나 pit한테 interrput를 받았는지를 트래킹하면 됨. 다 끝나면 ready queue로 보내는 것.
그 다음에 무슨 프로세스를 고를 것인가?
이것은 스케줄러에 의해 결정
Dual mode를 어떻게 효율적으로 제공할 것인가?
Dual mode를 제공하기 위해서 하드웨어적으로 support가 됨
→ Mode는 protected processor register안에 있는 status bit에 의해 설정된다. (1이면 kernel mode, 0이면 user mode)
User mode에서 kernel mode로 모드 체인지가 되는 경우
- Hardware interrupt
내가 원하는 작업과는 무관함. 다른 I/O 작업을 처리하기 위해서 kernel mode로 이동하는 것.
- pit에 의해 interrupt가 온 상황이고 time quantum이 끝난 경우 → scheduling (즉 time quantum은 OS가 보장해줌)
- 그 이외의 경우 : 원래 돌고 있던 process로 돌아감
- Software interrupt (exception)
실제로는 physical 한 램보다 훨씬 더 큰 address space를 돌릴 수 있음. PC는 logical한 address 사이에서 돌아가는 것. (실제의 메모리 크기와는 무관함) 이게 가능한 이유가 paging에 기반한 virtual memory를 사용하기 때문이다.
내 process에 속하는 페이지가 전부 다 메모리에 없을 수 있음. 어떤 process가 사용하려는 memory가 page fault가 난 경우,
mmu(Memory management unit)
가 이를 알려주고 exception이 발동된다. 커널로 이동해서 메인 메모리에다가 할당하고 다시 user mode로 가는 것. (물론 잘못된 메모리로 이동하는 경우 kernel 모드로 갔다가 해당 프로세스를 죽인다. 즉, 반드시 user mode로 오는 것은 아니다.)Page fault, 0으로 나누는 경우 등등이 이에 해당함. (Software interrupt도 해당 프로세스에 해당되는 코드를 돌리다가 발생하는 것)
- System call
300여개의 system call이 존재. Kernel모드로 돌아가서 user mode로 돌아올 때까지 기다림.
System call과 interrupt의 차이가 무엇인가?
system call의 경우는 내가 원해서 하는 것. 내 것을 돌리는 와중에 내가 원해서 요청하는 것.
또한 software interrput도 내꺼 돌리다가 interrupt를 건다는 측면에서 hardware interrupt와 구분된다.
Interrupt의 경우 나와 관계 없는 process들의 처리. 커널은 많은 프로세스들에 대한 서비스를 같이 수행하고 있기 때문에, hardware interrupt 처리를 중간중간 해줘야 한다.
System call
돌아가고 있는 프로그램과 OS 사이의 인터페이스를 제공하는 역할을 담당
일반적으로 함수처럼 제공된다. 리눅스의 경우 libc.a라는 유저모드 레벨에서 도는 라이브러리에 속한다.
Example
빨간색으로 표시한 것이 kernel mode
int $0x80 : 이 순간에 모드를 바꾸는 것 (int를 명령어를 쓰고 128 parameter제공)
→ 이후로는 커널에서 진행됨
function pointer를 담고있는 array table
movl 2라고 했으므로 sys_call_table에 2번째 친구로 접근하는 것.
Advantage
- Easy to program
유저 입장에서 내부에 정확히 어떻게 돌아가고 있는지 몰라도 되는 장점이 있음
- Increasing system security
커널을 system call을 부르지 않는 이상 진입할 수 없음. 즉 system call의 parameter만 잘 추적해서 이상하면 해당 process를 죽이면 됨. 사실 해킹이라는 것은 커널 진입이라는 말로도 이해할 수 있음.
- Increase program portability
만약 OS마다 코드를 바꿔야한다면 문제가 발생. 즉 표준화가 문제가 됨. 그래서 다른 OS여도 system call은 대부분 유지되는 이유이다.
POSIX APIs and System Calls
API (Application Programming Interface)
정의
Application programmer가 이용가능한 함수들의 모임. input parameter와 return value가 정의되어 있다. 프로그래머는 운영체제가 제공하는 라이브러리를 통해 API에 접근한다. (리눅스에서는 libc). application에서 데이터를 읽거나 쓰기 위해서 필요한 함수.
→ API를 구성하는 함수는 일반적으로 실제 system call을 프로그래머 대신 호출한다. (즉 API를 통해 간접적으로 system call을 호출한다.)
왜 application programmer가 API에 따라 programming하는 것을 선호하는가? 직접 system call을 부르면 안되는가?
- Program portability : API를 통해 구현한 경우, 동일한 API를 지원하는 다른 프로그램에서 동일하게 사용할 수 있을 것이라고 기대할 수 있음.
- 실제의 system call을 다루기에는 어려울 수 있음
RTE(Real Time Environment)
RTE는 System call interface를 제공한다. API 호출을 가로채 필요한 운영채제 system call을 부른다. system call 에 번호가 할당되고, 이 번호로 색인이 되어 찾기 쉽게 된다. 위 과정을 통해 프로그래머는 API만 준수하면 되고, 운영체제가 내부적으로 어떤 동작으로 움직이는지를 몰라도 무방해진다.
System call
software적인 interrupt를 통해 명시적으로 kernel에게 요청하는 것
return value : 정수 (실패하면 -1이 return)
system call은 libc라는 user 공간에 있는 wrapper function으로 구현된다.
System Programs
user mode에서 프로그램 개발이나 실행에 도움을 주는 역할을 담당
ex) editor, compiler, assembler
Operating System Design
새로운 os를 만들 떄 무엇이 issue가 있는가?
System Design goals
- User Goals : 일단 쓰기 편해야하고 빨라야 한다.
- System Goals : 유지/보수가 쉬워야하고 신뢰할 수 있어야 한다.
Mechanisms and policies
Mechanism과 policy를 구분해야한다.
Mechanism : 방법
Policy : 정책
→ 가능하면 mechanism과 policy를 구분하고, policy가 더 좋은 것이 나오면 그것으로 대체하는 것. mechanism은 되도록 천천히 update. policy의 경우 바로 반영가능. 그래서 리눅스가 업데이트가 반영이 가능한 것.
Layering Approach
큰 프로그램을 짤 때 layer approach를 쓰면 좋다.
각각의 layer를 짜고 위 아래만 소통이 가능하게끔
Difficulties
- 네트워크의 경우 각 layer의 기능을 잘 정의할 수 있지만, os의 경우는 layer로 다루기가 힘듦.
- Performance가 낮음. 레이어를 타고가면서 copy를 해야하고 그 과정에서 performance가 줄어듦.
→ 결과적으로 layering concept를 os에 적용하는 것을 포기
Monolithic Kernel
단순화된 커널. 모든 것을 다 flat하게 단일 이미지 형태로 만드는 것. 현재 우리가 사용하고 있는 방식. run time에 아무데나 포인터로 jump할 수 있음.
Microkernel
아주 간단한 형태의 kernel을 만들자! 진짜 코어가 되는 것만 kernel에 두고, 나머지는 application에 뺴자. 마치 system software처럼. 커널이 작기 때문에 kernel managing하기가 상대적으로 쉬워짐. 단지 좀 느림 결과적으로 kernel을 통해 주고받아야 하므로. 미사일 발사처럼 예측가능한 것이 더 중요한 경우에는 사용하기도 함.
ex : Mach, QNX, L4 kernel
교훈
Module 개념을 만들고, loadable kernel module이라는 개념을 도입해서 효율성을 재고함.
System Implementation
60년대에는 Assembly로 OS 코드를 짰음.
→ 하지만 새로운 기계가 등장하고 ISA가 다르면 다시 코드를 짜야하는 문제가 발생함.
그래서 OS 코드를 잘 짤 수 있는 언어를 만들자고 해서 등장한 것이 C언어.
그래서 대부분의 OS들은 c언어로 되어있고, 일부분이 어셈블리 코드
어셈블리로 했을 때보다 c언어로 짜게 되면 10% 정도의 overhead가 발생.
그래서 c언어를 선택하고 portability를 택하고, 일부분만을 어셈블리를 이용 예를 들어 프로세스 스위칭을 할 때는 어셈블리를 사용.
Virtual Machine
예전에는 Application이 특정 ISA와 OS에 묶여있는 경우가 많았음. 또한 OS 또한 특정 하드웨어가 요구되는 경우가 있었음.
What is a Virtual Machine(VM)?
Software for cross-platform compatibility
Why do we need VM?
- Virtual machine으로 인해 여기저기서 돌릴 수 있게되면서 Portability가 증가됨
- 어떤 회사가 CPU를 만들었다고 가정, 그 해당 하드웨어에서 돌아가게끔 emulation하면 됨.
- 하드웨어가 여러개 OS를 돌리고 싶은 것.
- 커널은 개발하기에 어려움. 커널 리부팅하면서 개발하기 귀찮음. 즉 원 os가 안죽음. (이러한 개념을 sandboxing이라고 함)
Two types of VM
- Process VM
individual process를 어떻게 돌릴 것인가에 대한 문제. 프로세스만을 지원하기 위한 VM
- System VM
완전한 의미에서의 가상화. 다른 OS 자체를 돌리는 것
Process VM
서로 다른 ISA나 OS위에서 프로세스를 돌리겠다는 것.
자바 VM을 만들고 그 위에 Java 프로그램을 돌리는 것.
runtime
이라고 불림
System VM
Virtual Machine Manager (Hypervisor)라고 불림
Hypervisor를 (c)처럼 2개를 둘 수 있음.
크게 Hosted 방식과 Stand-alone방식이 있음
1. Hosted
현재 존재하는 host OS 위에서 process처럼 돌리는 방법 (상대적으로 쉬움)
2. Stand-alone
VMM on top of bare hardware
하드웨어 위에 직접 올리므로 더 빠르다. (상대적으로 Hosted 방식보다는 더 빠르나 어렵다.)
Some history
GNU project
Richard stallman이 주도적으로 GNU project를 진행함.
컴파일러부터 여러 프로그램들을 다 만듦.
Goal : to produce free software
GPL : ensure software freedom by copyright terms.
GNU software : UNIX-like program, but no kernel
Why linux?
- Free and Open
소스가 오픈이 되면 많은 사람이 코드를 볼 수 있고, 이를 통해 뻑을 찾을 수 있다.
- Portable from mainframe to handhelds
동일한 커널로 동작이 가능
- Compatibility and efficiency
상당히 효율적. 그렇지 않으면 무료라도 쓰지 않았을 것. 코드의 최적화, 자료구조의 최적화를 계속해서 진행중임.Small and compact kernel.
- Maturity
- Well supported : 많은 기기가 linux 지원
- Monolithic kernel
Fat kernel이다. 효율적으로 동작하기 위함
Complex하지만 효율적이다.
- Preemptive kernel
중요한 것을 위해 kernel이 고민을 많이 한다.
- Multiprocessor support
- File system
모든 file system은 거의 다 제공함.
추가적으로 modules
를 지원한다. 즉 kernel을 reboot하지 않고 modules를 넣었다 뺐다 할 수 있다.(micro-kernel의 장점을 살림)
소중한 공감 감사합니다