운영체제는 사용자에게 도움을 주기 위해 여러 서비스들을 제공한다.
1. User Interface: 사용자 <-> 시스템 연결 창구
2. Program execution: 프로그램 실행
3. I/O operations
4. File-system manipulation: 파일 관리 기능
5. Communications
6. Error detection
운영체제는 또한 시스템의 효율성을 위해 아래와 같은 여러 서비스들을 제공한다.
1. Resource allocation
2. Accounting
3. Protection and Security
운영체제에서 제공하는 서비스에 접근할 수 있게하는 interface 역할을 하는 것이 system call이다.
왜 system call을 직접 사용하지 않고 API를 이용?
-> system call의 종류가 너무 많아 복잡함. 이때 API를 제공하면, 그 하부에 있는 system call들이 자동으로 실행됨. 즉 system call을 직접 사용하는 것보다 편리하고 효율적임.
위 그림은 source file의 내용을 destination file로 copy할때의 system calls의 예시다. 앞단에 나와있는 것들이 system calls이라고 이해하면 되고, 직관적인 이름이 부여된 것을 알 수 있다.
이 그림은 Win32 API 안에 있는 ReadFile() 함수가 어떻게 구성이 되어있는지를 나타낸 그림이다.
<파라미터들>
-> 즉 위 예시를 통해, system call을 직접적으로 호출하는 것보다 API를 통해 system call을 사용하는 것이 훨씬 간편하고 효율적이라는 것을 알 수 있다.
✔ 각 system call들에 고유한 번호를 할당하고, kernel level에 이 번호들을 인덱스로 사용하는 테이블이 존재한다. 즉 사용자가 특정 system call을 호출하면, 인터페이스는 이 테이블을 보고 커널 내의 해당 함수를 찾아 실행한다. 이때 system call table은 인덱스 값과 그 인덱스에 대응하는 실제 함수들의 메모리 주소가 저장되어 있다.
✔ API를 이용하는 경우 사용자는 system call에 대해 하나도 몰라도 된다. 단지 API 호출 규칙을 준수하기만 하면 된다.
✔ 즉 대부분의 세부 사항은 프로그래머에게 숨겨져 있으며, 이는 런타임 지원 라이브러리가 관리한다.

위 그림을 보면, user application이 C언어로 작성된 어떤 프로그램에 해당한다. 이때 printf()는 C 라이브러리에서 제공하는 함수이며, printf()라는 함수에서 write()라는 system call을 호출한다. 이후 그 write()라는 system call이 실행되는 과정이 바로 인덱스 값과 그 인덱스에 대응하는 실제 함수들의 메모리 주소를 보고 실행하는 과정이다. 이후 return값이 system call interface로 올라가고, system call interface는 이 값을 다시 user application으로 돌려주는 흐름인 것을 알 수 있다.
위에서 봤듯이 system call 호출 시 OS에게 여러 파라미터 값들을 전달해야 하고, 이러한 파라미터들을 전달하는 방법에는 크게 세가지 방법이 있다.
CPU 주변의 레지스터들을 이용해 파라미터 값들을 전달할 수 있으며, 이때 여유분으로 남아있는 레지스터의 개수보다 파라미터의 개수가 더 많을 경우 문제가 발생할 수 있다.
파라미터들을 메모리의 특정 블록이나 테이블에 한꺼번에 저장하고, 그 블록이 시작되는 메모리 주소만 레지스터에 담아 OS에 넘겨준다. 주로 linux 계열에서 많이 사용된다.
그림의 예시를 통해 memory를 이용하는 방식의 흐름을 알아보자.
위 그림의 왼쪽 파란색 박스를 보면, 프로그램은 system call(13번)에 필요한 파라미터들을 메모리의 특정 구역(X)에 미리 적어둔다.
이후 파라미터 데이터 그 자체가 아닌 그 데이터가 시작되는 메모리 주소값(address X)을 CPU의 레지스터(register)에 담는다.
프로그램이 system call 13번을 호출하면 제어권이 OS로 넘어가고, OS는 레지스터를 확인해 파라미터들이 메모리 address X번지부터 기록돼 있다는 것을 알게 된다.
이후 OS는 해당 주소를 참조해 메모리에서 필요한 매개변수들을 읽어오고, 읽어온 정보를 바탕으로 system call 13번의 실제 기능을 수행한다.
프로그램이 파라미터를 메모리의 stack 영역에 집어넣고(Push), OS가 그 stack에서 값을 꺼내가는(Pop) 방식이다.
system call의 유형에는 아래와 같이 크게 5가지의 유형이 있다.
프로그램의 개발과 실행을 위해 편리한 환경을 제공해주는 것이 system program이다.
사용자 입장에서 보이는 OS 기능들이 System Program이다.
system program들의 밑단에는 system call들이 있으며, 이는 눈에 보이지 않는다.
system program은 아래와 같이 6개로 이루어져있다.
파일과 디렉터리를 조작하는 기능을 제공한다.
날짜, 시간, 메모리 공간, 디버깅 정보 등 시스템 상태의 정보를 제공하며, 설정 정보(Configuration)를 저장하고 관리하기 위해 registry(set of registers)라는 system program을 사용한다.
텍스트 에디터를 통해 파일을 새로 생성하거나 수정한다. 추가로 search 기능도 제공한다.
컴파일러, 어셈블러, 인터프리터 등으로 프로그래밍 언어를 지원한다.
프로그램을 메모리에 load하는 과정을 관리한다.
프로세스와 프로세스 또는 컴퓨터와 컴퓨터 사이에 정보를 주고받을 일이 있을 때 그 기능을 제공한다.
운영체제의 설계와 구현의 측면에서는 어떠한 개발방법이 중요한게 아니라, 운영체제의 목표에 따라 다양하게 설계 및 구현이 된다.
User goals (사용자 목표)
System goals (시스템 목표)
Policy (정책)
Mechanism
policy와 mechanism을 섞지 않고 반드시 서로 분리하는 것이 중요하다. 그래야 추후에 policy가 바뀌더라도 유연성 확보가 가능하다.
예를 들어, 어떤 한 프로그램의 무한 루프를 방지하는 상황에서, policy는 "무한루프를 방지해야한다" 이고, mechanism은 "어떻게 무한루프를 방지할까" 이다. 이때 그 프로그램의 사용자가 학생들이라고 했을때, 단순한 사용 환경을 가정하여 “일정 횟수 이상 반복되면 종료한다”는 방식으로 구현하고, 이를 4비트 카운터(최대 15회)로 제한했다고 하자.
여기서
이때의 문제는 “4비트 카운터”라는 선택이다.
이는 “최대 15회까지만 허용”이라는 정책까지 포함한, 즉 policy를 mechanism에 포함시킨 설계다.
이후 프로그램이 연구 개발용으로 변경되어 더 많은 반복이 필요해지면, 단순히 policy만 바꾸는 것이 아니라 mechanism까지 수정해야 한다.
따라서 mechanism(카운터 구조)은 유지하고 policy(최대 반복 횟수)는 변경 가능하게 분리하는 것이 올바른 설계이다.



UNIX는 크게 두 개의 파트로 구성되어 있다.
그림을 통해 보면, 커널의 윗부분에 해당하며, 사용자가 직접적으로 상호작용하는 부분이다.
-> 단점: UNIX 구조는 너무 많은 기능이 커널 안에 들어가 커널이 커지는 문제가 발생하고, 여러 기능이 맞물려 있어서 커널 내의 어떤 기능을 수정하려고 하면 이것들이 서로 맞물려 수정이 굉장히 복잡하고 어렵다.


하드웨어 위에 하나의 커널(OS)이 올라가고, 그 위에서 여러 프로세스가 실행되며, 모든 프로세스가 하나의 OS 자원을 공유한다.
하드웨어 바로 위에 Virtual-machine implementation이라는 층이 추가된다.
그 위로 여러 개의 독립된 VM(VM1, VM2, VM3)이 생성되며, 각 VM은자신만의 커널(OS)을 별도로 가질 수 있다.
즉 결과적으로 하나의 컴퓨터 안에서 리눅스, 윈도우 등을 동시에 독립적으로 돌릴 수 있게 된다.
요즘은 VMware라는 가상화 플랫폼이 가장 많이 쓰인다.

JVM은 호환성이 좋아 JVM이 깔려있는 컴퓨터는 서로 다른 컴퓨터에도 동일한 프로그램을 실행시킬 수 있다.
설치의 첫단계로, 해당 컴퓨터 시스템의 하드웨어들에 대한 구성 정보들을 파악해야 한다.
부팅과정은 크게 두 단계로 나눌 수 있다.
1. bootstrap loader 프로그램이 어디있는지 파악
2. bootstrap program이 돌면서 실제 booting 작업 실행