운영체제는 프로그램과 사용자들에게 프로그램과 서비스들이 실행할 수 있는 환경을 제공해준다.
여기서 다음 서비스들은 사용자들에게 편리한 기능들이다.
거의 모든 운영체제에는 사용자 인터페이스(UI)가 있다. 종류로는 CLI, GUI, 터치스크린, 배치 형식이 있다.
프로그램 수행(program execution) : 시스템은 프로그램을 메모리에 적재해 실행할 수 있어야 한다. 정상적이든, 비정상적이든 실행을 끝낼 수 있어야 한다.
입출력 연산(I/O operations) : 수행 중인 프로그램은 입출력을 요구할 수 있다. 이러한 입출력에는 파일 혹인 입출력 장치가 연관될 수 있다.
파일 시스템 조작(file system manipulation) : 프로그램은 이름에 의해 파일을 생성하고 삭제할 수 있고 지정된 파일을 찾을 수 있어야 하고 파일의 정보를 열거할 수 있어야 한다.
통신(communication) : 한 프로세스가 다른 프로세스와 정보를 교환해야 할 필요가 있는 여러 상황이 있다.
통신은 공유 메모리나 메시지 전달(OS를 통해 패킷을 옮기는) 기법을 사용하여 구현될 수 있다.
오류 탐지(error detection) : 운영체제는 모든 가능한 오류를 항상 의식하고 있어야 한다.
자원 할당(resource allocation) : 다수의 프로세스나 다수의 작업이 동시에 실행될 때, 그들 각각에 자원을 할당해 주어야 한다. 운영체제는 여러 가지 다른 종류의 자원을 관리한다. (CPU 사이클, 메인 메모리, 파일 저장장치)
기록 작성(logging) : 어떤 프로그램이 어떤 종류의 컴퓨터 자원을 얼마나 많이 사용하는지를 추적하는 기능
보호와 보안(protection & security) : 네트워크로 연결된 컴퓨터 시스템에 저장된 정보의 사용자는 그 정보의 사용을 통제하는 기능. 서로 다은 여러 프로세스가 병행하게 수행될 때, 한 프로세스가 다른 프로세스나 운영체제 자체를 방해해서는 안 된다.
CLI 혹은 command interpreter는 직접 입력한 명령어만 허용한다.
사용자 친화적인 그래픽 기반 사용자 인터페이스 GUI를 통하는 방식이다.
많은 시스템들이 CLI와 GUI를 둘 다 사용한다.
시스템 콜은 운영체제에 의해 사용 가능하게 된 서비스에 대한 인터페이스를 제공한다.
이러한 호출은 일반적으로 높은 레벨의 언어 (C or C++)로 호출하는 것이 일반적이다. (HW는 어셈블리 명령)
프로그램의 의한 접근은 직접 시스템 콜을 사용하기 보다 보통 높은 레벨의 API를 사용한다. (이식성과 편이성 때문에)
가장 흔히 사용되는 API는 Win32 API, POSIX API, Java API이다.
시스템 콜 인터페이스 : 시스템 콜은 통상 숫자가 할당되어 있는데 이런 숫자들을 인덱싱해서 테이블에 관리하는 것이 시스템 콜 인터페이스이다.
시스템 콜 인터페이스는 의도하는 시스템 콜의 상태와 반환 값을 돌려준다.
호출자는 시스템 콜이 어떻게 구현되고 실행 중 무슨 작업을 하는지 아무것도 알 필요가 없다.
단지 API를 준수하고 시스템 콜의 결과로서 운영체제가 무엇을 할 것인지만 이해하면 된다.
운영체제 인터페이스에 대한 대부분의 자세한 내용은 API에 의해 프로그래머로부터 숨겨진다.
실행시간 환경 : 컴파일러 또는 인터프리터를 포함하여 특정 프로그래밍 언어로 작성된 응용 프로그램을 실행하는 데 필요한 전체 소프트웨어 제품군과 라이브러리 또는 로더와 같은 다른 소프트웨어를 함께 가르킨다.
통상 원하는 시스템 콜을 식별하기 위해서는 더 많은 정보를 필요로한다.
정확한 정보의 유형과 양은 운영체제와 콜에 따라 다르다.
3개의 표준적으로 사용되는 운영체제에 파라미터 패스해주는 방법들
가장 간단한 방법 : 매개변수를 레지스터 내에 전달하는 것, 어떤 경우에는 레지스터보다 더 많은 매개변수가 있을 수 있따.
이 경우 메모리 내의 블록이나 테이블에 매개변수를 저장하고, 블록의 주소가 레지스터 내에 매개변수로 전달된다. (Linux와 Solaris에서 사용)
매개변수는 프로그램에 의해 스택에 넣어질수도 있고 꺼내질수도 있다.
블록이나 스택 방법은 전달되는 매개변수들의 개수나 길이를 제한하지 않는다.
시스템 서비스(시스템 유틸리티)는 프로그램 개발과 실행을 위해 더 편리한 환경을 제공한다. 다음과 같이 분류된다.
대부분의 사용자 관점에서 보는 운영체제는 시스템 프로그램의 의한 것이지 시스템 콜이 아니다.
소스 파일은 임의의 물리 메모리 위치에 적재되도록 설계된 오브젝트 파일로 컴파일 된다. - 이러한 형식을 재배치 가능 오브젝트 파일이라 한다.
링커 : 재배치 가능 오브젝트 파일을 하나의 이진 실행 차일로 결합한다. 다른 오브젝트 파일이나 라이브러리도 포한된다
로더 : 이진 실행 파일을 메모리에 적재하는 데 사용되며, CPU 코어에서 실행할 수 있는 상태가 된다.
링크 및 로드와 관련된 활동은 재배치로, 프로그램 부분에 최종 주소를 할당하고, 프로그램 코드와 데이터를 해당 주소와 일치하도록 조정하여 프로그램이 실행될 때 코드가 라이브러리 함수를 호출하고 변수에 접근할 수 있게 한다.
현대 범용 시스템은 모든 라이브러리를 실행 파일에 링크해 메모리에 적재되지 않는다.
대신 동적 링킹 라이브러리(DLL)을 사용해 실행 파일에서 사용되지 않을 수 있는 라이브러리를 링크하고 로드하지 않아도 된다. 라이브러니는 조건부로 링크되며 프로그램 실행 시간에 필요한 경우 적재된다. (한 번만 적재된다.)
오브젝트 파일 및 실행 파일은 일반적으로 표준화된 형식을 가진다. (Unix Linux는 ELF라 한다.) 이를 통해 운영체제가 어떻게 적재하고 시작할지 않다.
응용 프로그램들은 하나의 시스템에서 컴파일된다. 보통 다른 운영체제에서 실행되지 않는다.
각 운영체제는 운영체제에 맞는 유니크한 시스템 콜을 제공합니다. (자체 파일 포맷 등)
응용 프로그램들은 다음 세 가지 방법 중 하나로 여러 운영체제에서 실행될 수 있다.
인터프리터 언어(파이썬, 루비)로 쓰여진 언어인 경우, 성능이 떨어지지만 가능하다.
응용 프로그램에 가상머신이 있고, 가상 머신에서 응용 프로그램(자바)을 실행하는 경우
표준 언어(standard language, C)인 경우, 실행될 각 운여ㅇ체제에 각자 컴파일되어야 한다.
Application Binary Interface(ABI) : 아키텍처 수준에서 이진 코드의 여러 구성요소가 주어진 아키텍처에서 특정 운영체제와 상호 작용할 수 있는 방법을 정의한다.
운영체제의 설계와 구현 정답은 없지만 몇 가지 성공적인 사례들이 있다.
여러 운영체제의 내부 구조는 굉장히 다양하다.
시스템을 설계하는데 첫번째 문제는 시스템의 목표와 명세를 정의하는 것이다.
하드웨어와 시스템의 유형(일괄처리, 시분할, 단일 사용자, 다중 사용자, 분산, 실시간 혹은 범용)의 따라 설계가 영향 받는다.
근본적인 요구 조건은 사용자 목적과 시스템 목적으로 나눌 수 있다.
사용자 목적 : 운영체제가 사용하기 쉽고, 배우기 쉬우며, 믿을 수 있으며, 안전적이고 빨라야 한다.
시스템 목적 : 운영체제가 설계와 구현이 쉽고, 유지보수가 쉬우며, 유연하고, 믿을 수 있으며, 오류가 없고, 효율적이어야 한다.
한 가지 중요한 원칙은 기법(policy)으로부터 정책(mechanism)을 분리하는 것이다.
정책과 기법의 분리는 융통성을 위해 아주 중요하다. (기법이 바뀌는 경우를 위해)
운영체제 설계를 명세하는 것은 소프트웨어 엔제니어링에서 매우 창의적인 작업이다.
구현은 많은 방법이 있다.
사실 보통은 여러 언어로 구현된다.
좀 더 상위 레벨의 언어들은 다른 하드웨어에 이식성은 좋지만 속도는 느리다.
에뮬레이션은 다른 시스템의 하드웨어들을 운영체제에서 실행할 수 있게 한다.
범용 목적 운영체제는 굉장히 규모가 큰 프로그램이다.
여러 방식의 구조들이 있다.
UNIX : (하드웨어 기능 때문에) 제한적인 구조를 가진 운영체제
UNIX 은영체제는 두 가지로 분리되어 있다. (시스템 프로그램과 커널)
간단한 구조보다는 복잡하지만 완벽한 계층 구조는 아니다.
모놀리식 구조에 모듈 설계가 더해진 형태
이 방식에서는 운영체제가 여러 개의 층으로 나누어진다. 취하위 층은(층 0) 하드웨어이고 최상위 층(층 N)은 사용자 인터페이스이다.
이러한 시스템은 더 많은 기능을 가진 더 적은 개수의 층을 가지므로 층 기능의 정의 및 상호작용의 문제를 피하면서 모듈화된 코드의 장점을 최대한 활용할 수 있다.
모든 중요치 않은 구성요소를 커널로부터 제거하고, 그들을 별도의 주소 공간에 존재하는 사용자 수준 프로그램으로 구현하여 운영체제를 구현하는 방식
대표적인 예로 Mach가 있다. (Mac OS X 커널 (Darwin)은 부분적으로 Mach이다.)
통신은 유저 모듈의 통신을 메시지 전달로 가능하게 한다.
이 구조의 장점은
가중된 시스템 기능 오버헤드 떄문에 성능이 나빠진다 두 개의 사용자 수준 서비스가 통신해야 하는 경우 별도의 주소 공간에 서비스가 존재하기 때문에 메시지가 복사되어야 한다.
많은 근대 운영체제는 loadable kernel modules(LKMs)기법을 사용한다.
계층구조와 유사하지만 더 유연한 구조이다. (Linux, Solarix, etc)
현대 운영체제는 엄격하게 정의된 하나의 구조를 채택하지 않고 다양한 구조를 결합하여 만든다.
시스템에 파워가 들어오면 고정된 메모리 주소에서 시작된다.
운영체제는 하드웨어에 접근이 되야 하드웨어를 작동 시킬수 있다.
GRUB : 여러 디스크, 버전, 커널 옵션 중에 커널을 선택할 수 있게 한다.
커널이 적재되야 시스템이 실행된다.
부트 로더는 자주 여러 부트 상태(싱긍 유저 모드 같은)를 제공한다.