운영체제 구조
운영체제는 프로그램 실행환경을 제공한다. 운영체제는 프로그램과 그 프로그램의 사용자에게 특정 서비스를 제공한다.
- 사용자 인터페이스(user interface): 거의 모든 운영체제는 사용자 인터페이스(UI)를 제공한다. 이 인터페이스는 여러 형태로 제공될 수 있다. 가장 일반적으로 그래픽 사용자 인터페이스(GUI)가 사용된다. 여기서 인터페이스는 윈도 시스템으로 I/O를 지시하고, 메뉴에서 선택하고, 화면을 선택하는 포인팅 장치인 마우스와 텍스트를 입력할 키보드를 가지고 있다. 또 다른 옵션은 명령어 라인 인터페이스(CLI)이다. 이 인터페이스는 명령을 사용하여 이를 입력할 방법이 사용된다.
- 프로그램 수행(program execution): 시스템은 프로그램을 메모리에 적재해 실행할 수 있어야 한다. 프로그램은 정상적이든, 혹은 비정상적이든 실행을 끝낼 수 있어야 한다.
- 입출력 연산(I/O operation): 수행 중인 프로그램은 입출력을 요구할 수 있다. 이러한 입출력에는 파일 혹은, 입출력 장치가 연관될 수 있다. 특정 장치에 대해서는 특수한 기능(네트워크 인터페이스에서 읽거나 파일 시스템에 쓰기 등)이 요구될 수 있다. 효율과 보호를 위해, 사용자들은 통상 입출력 장치를 직접 제어할 수 없다. 따라서 운영체제가 입출력 수해으이 수단을 제공해야 한다.
- 파일 시스템 조작(file system manipulation): 파일 시스템은 특히 중요한 분야이다. 명핵히, 프로그램은 파일을 읽고 쓸 필요가 있다. 프로그램은 또한 이름에 의해 파일을 생성하고 삭제할 수 있고 지정된 파일을 찾을 수 있어야 하고 파일의 정보를 열거할 수 있어야 한다. 마지막으로 몇몇 프로그램은 파일 소유권에 기반을 둔 권한 관리를 이용하여 파일이나 디렉터리 접근을 허가하거나 거부할 수 있게 한다. 많은 운영체제들은 떄로는 개인의 선택에 따라 그리고 때로는 특정 특성과 성능 특성을 제공하기 위하여 다양한 파일 시스템을 제공한다.
- 통신(communication): 한 프로세스가 다른 프로세스와 정보를 교환해야 할 필요가 있는 상황이 있다. 통신은 공유 메모리를 통해서 구현될 수도 있고, 메시지 전달(message passing) 기법을 사용해서 구현될 수 있는데, 후자의 경우 정보의 패킷들이 운영체제에 의해 프로세스들 사이를 이동한다.
- 오류 탐지(error detection): 운영체제는 모든 가능한 오류를 항상 의식하고 있어야한다. 오류는 CPU, 메모리 하드웨어, 입출력 장치, 또는 사용자 프로그램에서 일어날 수 있다. 운영체제는 올바르고 일관성 있는 계산을 보장하기 위해 각 유형의 오류에 대해 적당한 조치를 해야 한다.
- 자원 할당(resource allocation): 다수의 프로세스나 다수의 작업이 동시에 진행될 때, 그들 각각에 자원을 할당해 주어야 한다. 운영체제는 여러 가지 다른 종류의 자원을 관리한다.
- 로깅(logging): 우리는 어떤 프로그램이 어떤 종류의 자원을 얼마나 많이 사용하는지 추적할 수 있길 원한다. 이와 같은 기록 관리는 회계, 또는 단순히 사용 통계를 내기 위해 사용된다. 사용 통계는 컴퓨팅 서비스를 개선하기 위해 시스템을 재구성하고자 하는 시스템 관리자에게 중요한 자료가 될 수 있다.
- 보호(protection)와 보안(security): 다중 사용자 컴퓨터 시스템 또는 네트워크로 연결된 컴퓨터 시스템에 저장된 정보의 소유자는 그 정보의 사용을 통제하길 원한다. 서로 다른 여러 프로세스가 병행하게 수행될 때, 한 프로세스가 다른 프로세스나 운영체제 자체를 방해해서는 안 된다. 보호는 시스템 자원에 대한 모든 접근이 통제되도록 보장하는 것을 필요로 한다. 외부로부터의 시스템 보안 또안 중요하다. 이러한 보안은 각 사용자가 자원에 대한 접근을 원할 때 통상 패스워드를 사용해서 자기 자신을 인증하는 것으로부터 시작된다. 보안은 네트워크 어댑터 등과 같은 외부 입출력 장치들을 부적합한 접근 시도로부터 지키고, 침입의 탐지를 위해 모든 접속을 기록하는 것으로 범위를 넓힌다.
사용자와 운영체제 인터페이스
명령 인터프리터(Command Interpreter)
Linux, UNIX 및 Windows를 포함한 운영체제 대부분은 명령 인터프리터를 프로세스가 시작하거나 사용자가 (대화형 시스템상에서) 처음 로그온 할 때 수행되는 특수한 프로그램으로 취급한다. 선택할 수 있는 여러 명령 인터프리터를 제공하는 시스템에서 이 해석기는 셸(shell)이라고 불린다.
명령 인터프리터의 중요한 기능은 사용자가 지정한 명령을 가져와서 그것을 수행하는 것이다. 이 수준에서 제공된 많은 명령은 파일을 조작한다. 즉 생성, 삭제, 리스트, 프린트, 복사, 수행등을 합니다. UNIX 시스템에서 사용 가능한 다양한 셸은 이런 방식으로 실행된다. 이 명령어들은 두가지 일반적인 방식으로 구현될 수 있다.
한 가지 방법은 명령 인터프리터 자체가 명령을 실행할 코드를 가지는 경우이다. 예를 들면, 한 파일을 삭제하기
위한 명령은 명령 인터프리터가 자신의 코드의 한 부분으로 분기하고, 그 코드 부분이 매개변수를 설정하고
적절한 시스템 콜을 한다. 이 경우 제공되는 명령의 수가 명령 인터프리터의 크기를 결정하는데, 그 이유는 각
명령이 자신의 구현 코드를 요구하기 때문이다. 여러 운영체제 중 UNIX에 의해 사용되는 다른 대안의 접근
방법은, 시스템 프로그램에 의해 대부분의 명령을 구현하는 것이다. 이러한 경우 명령 인터프리터는 전혀 그
명령을 알지 못한다. 단지 메모리에 적재되어 있는 실행될 파일을 식별하기 위해 명령을 사용한다. 따라서
파일을 삭제하는 다음의 UNIX 명령은 rm file.txt rm이라고 불리는 파일을 찾아서, 그 파일을
메모리에 적재하고, 그것을 매개변수 file.txt로 수행한다. rm 명령과 관련된 로직은 rm이라는 파일
내의 코드로 완전하게 정의된다. 이러한 방법으로 프로그래머는 적합한 프로그램 로직을 가진 새로운 파일을
생성함으로써 시스템에 새로운 명령을 쉽게 추가할 수 있다. 명령 인터프리터 프로그램은 이제 아주 작아질
수 있으며, 새로운 명령을 추가하기 위해 변경될 필요가 없다.
그래픽 기반 사용자 인터페이스
운영체제와 접촉하는 두 번째 방식은 사용자 친화적인 그래픽 기반 사용자 인터페이스 또는 GUI를 통하는 방식이다. 이 방식에서는 명령어 라인 인터페이스를 통하여 사용자가 직접 명령어를 입력하는 것이 아니라 데스크톱이라고 특정지어지는 마우스를 기반으로 하는 윈도 메뉴 시스템을 사용한다.
사용자는 마우스를 움직여 마우스 포인터를 프로그램, 파일, 시스템 기능등을 나타내는 화면상의 이미지에 위치시킨다. 마우스 포인터의 위치에 따라, 마우스 버튼을 누름으로써 프로그램을 호출하거나 파일 혹은 디렉터리를 선택할 수도 있고, 또는 명령을 포함한 메뉴를 잡아당길수도 있다.
터치 스크린 인터페이스
대부분의 모바일 시스템에는 명령 라인 인터페이스나 마우스 및 키보드 시스템이 실용적이지 않기 때문에 스마트폰 및 휴대용 태블릿 컴퓨터는 일반적으로 터치스크린 인터페이스를 사용한다. 사용자는 터치스크린에서 손가락을 누르거나 스와이프하는 등의 제스처를 취하여 상호작용한다.
인터페이스의 선택
명령어 라인 또는 그래픽 기반 인터페이스를 사용할 것인지는 개인의 선호에 달려 있다. 컴퓨터를 관리하는 시스템 관리자와 시스템에 대해 깊게 알고 있는 파워 유저들은 명령어-라인 인터페이스를 사용한다. 그들로서는 하고자 하는 작업에 대해 더 빨리 접근할 수 있으므로 명령어-라인 인테퍼이스가 더 효율적이다. 사실 몇몇 시스템에서는 GUI를 통해서는 시스템 기능의 일부만을 이용할 수 있고 자주 쓰이지 않는 나머지 기능은 명령어-라인을 사용할 수 있는 사용자만이 이용할 수 있다.
셸 스크립트는 UNIX와 Linux와 같이 명령어-라인 인터페이스에 기반을 둔 시스템에서는 매우 흔한 형태이다.
시스템 콜(System Calls)
시스템 콜은 운영체제에 의해 사용 가능하게 된 서비스에 대한 인터페이스를 제공한다. 특정 저수준 작업(예를 들면 하드웨어를 접근해야 하는 작업)은 어셈블리 명령을 사용하여 작성되어야 하더라도 이러한 호출은 일반적으로 C와 C++ 언어로 작성된 함수 형태로 제공된다.
예제
운영체제가 어떻게 시스템 콜을 사용할 수 있게 만드는지에 대해 논의하기 전에 시스템 콜이 어떻게 사용되는지 설명하는 예를 보도록 하자.
한 파일로부터 데이터를 읽어서 다른 파일로 복사하는 간단한 프로그램을 작성한다고 해보자. 프로그램이 필요로 하는 첫번째 입력은 두 개의 파일, 즉 입력 파일과 출력 파일의 이름일 것이다. 이 이름들은 운영체제의 설계에 따라 여러 가지 방법으로 지정할 수 있다. 한 가지 방법은 명령의 일부로 두 파일의 이름을 전달하는 것이다.(예: UNIX cp 명령)
cp in.txt out.txt
이 명령은 입력 파일 in.txt를 출력하일 out.txt에 복사한다. 두 번째 방법은 프로그램이 사용자에게 이름을 요청하는 것이다. 대화형 시스템에서 이 방법은 일련의 시스템 콜이 필요하다. 먼저 화면에 프롬프트 메시지를 작성한 다음 키보드에서 두 파일의 이름을 지정하는 문자를 읽는다. 마우스 기반 및 아이콘 기반 시스템에서 파일 이름 메뉴는 일반적으로 창에 표시된다. 그런 다음 사용자는 마우스를 사용하여 소스 이름을 선택할 수 있으며 대상 이름을 지정할 수 있는 창을 열 수 있다. 이 일련의 작업을 위해서는 많은 I/O 시스템 콜이 필요하다.
일단 두개의 파일 이름이 얻어지면, 프로그램은 반드시 입력 파일을 오픈하고 출력 파일을 생성한 후 오픈한다. 각각의 이러한 연산은 또 다른 시스템 콜을 필요로 하며 각 시스템 콜에서 오류가 발생하면 처리되어야 한다. 예를 들어 프로그램이 입력 파일을 오픈하려고 했을 때, 그 이름을 갖는 파일이 존재하지 않거나 그 파일에 대한 접근이 금지되어 있는 것을 발견할 수 있다. 이러한 경우 프로그램은 에러 메시지를 출력하고(또 다른 일련의 시스템 콜이다) 비정상적으로 종료(또 다른 시스템 콜이다)한다. 만약 입력 파일이 존재하면 새로운 출력 파일을 생성해야 한다. 이때 동일한 이름을 가진 출력 파일이 이미 존재하는 경우가 있다. 이러한 상황은 프로그램을 중단(abort)(하나의 시스템 콜임)하게 하거나, 또는 우리가 기존 파일을 삭제(다른 시스템 콜임)한 후, 새로운 파일을 생성(다른 시스템 콜임)할 수도 있다. 대화형 시스템에서 또 다른 방법은 기존의 파일을 대체할 것인지, 혹은 프로그램을 중단할 것인지를 사용자에게 물어보는(프롬프트 메시지의 출력과 터미널로부터 응답을 읽기 위한 일련의 시스템 콜) 것이다.
이제 두 개의 파일이 준비되면, 입력 파일로부터 읽어서, 출력 파일에 기록하는 루프에 들어가게 된다. 각 읽기와 쓰기는 가능한 여러가지 오류 상황의 정보를 반환해야 한다. 입력에서 프로그램 이 파일의 끝에 도달하거나 읽기 중에 하드웨어 오류(이를테면 패리티 오류)가 발생할 수도 있다. 쓰기 연산시 출력 장치에 따라 여러 가지 오류들(예를 들어 디스크 공간의 부족)이 발생할 수도 있다.
마지막으로 전체 파일이 복사된 후, 프로그램은 두 개의 파일을 닫고, 콘솔 또는 윈도에 메시지를 기록하고, 결국 정상적으로 종료하게된다.

