[OS] 2장 운영체제구조

sujpark·2022년 4월 8일
0

운영체제

목록 보기
2/2
post-thumbnail

운영체제에서 제공하는 서비스

운영체제에 포함하도록 정해진 서비스는 없으나 공통적으로 제공하는 서비스들이 있다.

사용자 인터페이스

거의 모든 운영체제는 사용자 인터페이스를 제공한다.
인터페이스에는 여러 형태가 존재하는데 명령 인터프리터, 그래픽 사용자 인터페이스(GUI), 터치 스크린 인터페이스가 있다.

명령 인터프리터 (Command-Interpreter)

명령 인터프리터는 사용자의 명령을 해석하고 수행한다. 명령의 수행은 두가지 방식으로 이뤄질 수 있다.
한가지 방법은 인터프리터가 사용자의 명령을 수행하는 코드를 가지는 것이다. 예를 들어, 파일을 삭제하는 명령은 명령 인터프리터가 매개변수를 설정하고 적절한 시스템 호출을 수행하는 코드영역으로 가도록 한다. 이 경우 제공할 수 있는 명령의 수가 명령 인터프리터의 크기를 결정한다. 각 명령이 구현 코드를 요구하기 때문이다.
다른 방법은 명령의 구현이 시스템프로그램에 의해 이루어지는 것이다. 이 경우 명령 인터프리터는 전혀 그 명령을 알지 못한다. 단지 파일을 식별하기위해 명령을 사용한다.
예를 들어 UNIX 에서 파일을 삭제하는 명령을 입력하면,

rm file.txt

rm 이라 불리는 파일을 찾아서, 그 파일을 메모리에 적재하고, 그것을 매개변수 file.txt로 수행한다. rm 명령과 관련된 로직은 rm이라는 파일 내의 코드로 수행된다. 새로운 파일을 생성함으로써 시스템에 새로운 명령을 쉽게 추가할 수도 있다. 그로인해 명령 인터프리터 프로그램은 아주 작아질 수 있으며, 새로운 명령을 추가하기 위해 변경될 필요가 없다.

그래픽 기반 사용자 인터페이스 (Graphical User Interface, GUI)

GUI는 명령 인터프리터처럼 명령어를 직접 입력하는 것이 아닌, 마우스를 기반으로 조작하는 윈도우-메뉴 시스템이다. 우리가 흔히 사용하는 인터페이스이기도 하다. 사용자는 마우스를 움직여 마우스 포인터를 화면상의 이미지(아이콘)에 위치시킨다. 마우스 포인터를 위치시키고 버튼을 누름으로써 프로그램을 호출하거나 파일을 선택하는 등의 여러가지 조작을 할 수 있다.

터치스크린 인터페이스

모바일 시스템에서는 일반적으로 터치스크린 인터페이스를 사용한다.
사용자는 터치스크린에서 손가락을 누르거나 스와이프 하는 등의 제스처를 취하여 상호작용한다.

프로그램 수행(execution)

시스템은 프로그램을 메모리에 적재해 실행(running)할 수 있어야 한다. 프로그램은 정상적이든, 혹은 비정상적이든(오류를 표시하면서) 실행을 끝낼 수 있어야한다.

입출력 연산

실행(running)중인 프로세스는 입출력을 요구할 수 있다. 이런 입출력에는 파일이나 입출력 장치가 연관될 수 있다. 효율성과 보호를 위해 사용자는 입출력 장치를 직접 제어할 수 없다. 운영체제가 입출력 수행의 수단을 제공해야한다.

파일시스템 조작

운영체제에서 파일시스템은 특히 중요한 분야다. 프로그램은 파일을 읽고 쓸 수 있다. 또한 파일을 생성하고 삭제할 수 있고 지정된 파일을 찾을 수 있으며 파일의 정보를 나열할 수 있어야한다. 몇 몇 프로그램은 권한 관리를 이용하여 파일과 디렉터리의 접근을 허가하거나 거부할 수 있다.

통신

프로세스 사이에서 정보를 교환해야할 필요가 있다. 이런 통신에는 두가지가 있는데 하나는 동일한 컴퓨터에서 수행 중(execution)인 프로세스들 사이에서 나타나고, 다른 하나는 네트워크로 연결된 서로 다른 컴퓨터 시스템 상에서 수행 중인 프로세스들 사이에서 일어난다. 통신은 공유 메모리를 통해서 구현될 수도 있고, 메시지 전달기법을 사용하여 구현될 수도 있다. 메세지 전달기법에서는 정보 패킷들이 운영체제에 의해 프로세스 사이를 이동한다.

오류탐지

운영체제는 가능한 모든 오류를 항상 의식하고 있어야한다. 오류는
CPU,
메모리 하드웨어 (메모리 오류, 정전 등),
입출력 장치 (테이프의 패리티오류, 네트워크 접속 실패, 프린터 종이 부족 등),
또는 사용자 프로그램 (연산의 오버플로우, 잘못된 위치의 메모리에 접근 시도 등)
에서 일어날 수 있다. 운영체제는 올바르고 일관성있는 처리를 보장하기 위해 각 유형의 오류에 대해 조치를 취해야한다.

자원할당

다수의 프로세스가 동시에 실행(running)될 때, 각각의 프로세스에 자원을 할당해 주어야 한다.

기록 작성

어떤 프로그램이 어떤 종류의 컴퓨터 자원을 얼마나 많이 사용하는지 추적할 수 있다. 이와 같은 기록 관리는 회계 또는 사용 통계를 내기 위해 사용된다. 사용 통계는 서비스를 개선하기 위해 시스템을 재구성하고자 하는 시스템 관리자에게 귀중한 자료가 될 수 있다.

보호와 보안

다중 사용자 컴퓨터 시스템 또는 네트워크로 연결된 컴퓨터 시스템에서 저장된 정보의 소유자는 그 정보의 사용을 통제하기를 원한다. 서로 다른 여러 프로세스가 병행하게 수행될 때, 한 프로세스가 다른 프로세스나 운영체제 자체를 방해해서는 안된다. 보호는 시스템 자원에 대한 모든 접근이 통제되도록 보장하는 것이다.
외부로부터의 보안 또한 중요하다. 보안은 각 사용자가 자원에 대한 접근을 원할 때 통상 패스워드를 사용해서 시스템에게 자기 자신을 인증하게한다. 네트워크 어댑터 등과 같은 외부 입출력 장치들을 부적합한 접근 시도로부터 지키고, 침입 탐지를 위해 모든 접속을 기록한다.


시스템 콜

사용자 프로그램이 잘못된 영역에 접근해서 치명적인 오류를 발생시키는 것을 막기 위해 컴퓨터시스템은 커널모드사용자모드 라는 이중모드로 나뉘어 있다. 여러 장치들을 작동시키거나 시스템에 설정되어있는 여러 데이터들을 조작하는 권한은 커널모드에서 실행되는 프로그램만 가지고 있다. 사용자모드에서 위와 같은 수행이 필요한 경우 시스템 콜을 통해 커널모드에 요청하여 수행할 수 있다. 시스템콜은 운영체제가 제공하는 서비스에 대한 인터페이스를 제공하는 것이기도 하다.

예제

시스템 콜이 어떻게 사용되는지 예제를 통해 알아보자.
한 파일로부터 데이터를 읽어서 다른 파일로 복사하는 프로그램을 작성한다고 가정해보자.

입력 파일과 출력 파일의 이름이 필요하므로 파일의 이름을 입력받는 I/O 시스템 콜이 필요하다.
두개의 파일 이름이 얻어지면 입력 파일을 오픈하고 출력 파일을 생성한 후 오픈한다. 파일 오픈에 시스템 콜이 필요하며, 시스템 오류가 발생하면 처리해야한다.

프로그램이 입력 파일을 오픈하려고 할 때, 그 이름을 갖는 파일이 존재하지 않거나 그 파일에 대한 접근이 금지되어있는 경우가 있을 수 있다. 이런 경우 프로그램은 에러메세지를 출력하고 비정상적으로 종료해야하는데 에러메세지 출력과 비정상적인 종료 모두 시스템 콜을 통해 이루어진다.

출력 파일을 생성하는데 동일한 이름을 가진 파일이 이미 존재하는 경우가 있다. 이런 상황에서는 프로그램을 중단하거나 기존 파일을 삭제한 후, 새로운 파일을 생성할 수도 있다. 프로그램 중단, 기존 파일 삭제, 새로운 파일 생성 모두 시스템 콜을 통해 이루어진다.

두 파일이 준비되면 입력 파일로부터 읽어서(시스템 콜), 출력 파일에 기록(시스템 콜)하기를 반복한다. 각 읽기와 쓰기는 가능한 여러가지 오류 상황에 대한 정보를 반환(시스템 콜)해야한다. 읽는 중에 프로그램이 파일의 끝에 도달하거나 하드웨어 오류가 발생할 수 있다. 쓰는 중에 출력 장치에 따라 여러가지 오류들이 발생할 수 있다.

전체 파일이 복사된 후, 프로그램은 두 개의 파일을 닫고(시스템 콜) 콘솔 또는 윈도우에 메세지를 기록(시스템 콜)하고 프로그램을 정상적으로 종료(시슽템 콜)하게 된다.

응용프로그래밍 인터페이스

위 예제에서 보듯이 간단한 프로그램이라도 시스템 콜을 아주 많이 사용하게 된다. 하지만 대부분의 응용 프로그램 개발자들은 시스템 콜을 직접 사용하는 것이 아니라 응용 프로그래밍 인터페이스(API)를 통해 프로그램을 설계한다. API는 응용프로그래머가 사용 가능한 함수의 집합을 명시한다. API를 구성하는 함수들은 응용프로그래머를 대신하여 실제 시스템을 콜을 호출한다. 예를 들면 Windows의 함수 CreateProcess() 는 실제로 Windows 커널의 NTCreateProcess() 시스템콜을 부른다. 왜 실제 시스템 콜을 사용하지 않고 API를 사용할까? 이는 프로그램의 호환성과 관련이 있다. 각각 다른 운영체제는 고유의 시스템 콜을 가지는데 다른 운영체제가 같은 API를 지원한다면 같은 API 함수를 사용하여 프로그램을 작성할 수 있을 것이다. 게다가 실제 시스템 호출은 종종 애플리케이션 프로그래머가 사용할 수 있는 API보다 더 자세하고 작업하기 어렵다.

시스템 콜을 처리하는데 있어서 중요한 또 다른 요소는 런타임 환경(RTE) 이다. 런타임 환경이란 특정 프로그래밍 언어로 작성된 응용 프로그램을 실행하는 데 필요한 전체 소프트웨어 모음이다. 런타임 환경은 운영체제가 제공하는 시스템 콜에 대한 연결고리 역할을 하는 시스템 콜 인터페이스를 제공한다. 이 시스템 콜 인터페이스는 API 함수의 호출을 가로채어 필요한 운영체제 시스템 콜을 부른다. 각 시스템 콜에는 번호가 할당되고 시스템 콜 인터페이스는 이 번호에 따라 색인화된 테이블을 유지한다. 시스템 콜 인터페이스는 의도하는 시스템 콜을 부록 시스템 콜의 상태와 반환 값을 돌려준다.

호출자는 시스템 콜이 어떻게 구현되고 실행 중 무슨 작업을 하는지 아무것도 알 필요가 없다. 호출자는 단지 API를 준수하고 시스템 콜의 결과로서 운영체제가 무엇을 할 것인지만 이해하면 된다. 운영체제 인터페이스에 대한 대부분의 자세한 내용은 API에 의해 프로그래머로부터 숨겨지고 런타임 환경에 의해 관리된다.

시스템 콜을 통해 운영체제에 매개변수를 전달하기 위한 방법은 세가지가 있다.
가잔 간단한 방법은 매개변수를 레지스터 내에 전달하는 것이다. 그러나 어떤 경우는 레지스터보다 더 많은 매개변수가 있을 수 있다. 여런 경우 매개변수는 메모리 내의 블록이나 테이블에 저장되고, 블록의 주소가 레지스터 내에 매개변수로 전달된다. 혹은 프로그램에 의해 스택에 넣어져서 운영체제에 의해 꺼내진다. 블록 방법이나 스택방법은 전달되는 매개변수들의 개수나 길이를 전달하지 않는다.


운영체제 구조

모놀리식 구조

운영체제를 구성하는 가장 간단한 구조는 구조가 아예 없는 것이다. 커널의 모든 기능을 단일 파일에 넣는 것이다. 모놀리식 구조라고 하는 이 방법은 운영체제를 설계하는 일반적인 기술이다. 이 구조를 가진 운영체제의 예로 UNIX가 있다. 모놀리식의 단순성에도 불구하고 이 구조는 구현이나 확장이 어렵다. 그러나 시스템 콜 인터페이스에 오버헤드가 거의 없고 커널 안에서의 통신 속도가 빠르다는 장점이 있다. 모놀리식 커널의 단점에도 불구하고 속도와 효율성 덕분에 이 구조를 여전히 UNIX, Linux, Windows 운영체제에서 발견할 수 있다.

밀접한 결합 vs 느슨한 결합

시스템의 한 부분을 변경하면 다른 부분에 광범위한 영향을 줄 수 있으므로 모놀리식 접근법은 밀접하게 결합된 시스템으로 불린다. 모놀리식 구조의 대안으로 느슨하게 결합된 시스템을 설계할 수 있다. 이러한 시스템은 특정 기능을 가진 개별적이며 작은 구성요소로 나뉘고 모든 구성요소가 합쳐져 커널을 구성한다. 모듈 방식의 장점은 한 구성요소의 변경이 해당 구성요소에만 영향을 미치고 다른 구성요소에는 영향을 미치지 않으므로 시스템 구현자가 시스템의 내부를 자유롭게 생성하고 변경할 수 있다는것이다.

계층적 접근

시스템은 다양한 방법으로 모듈화 될 수 있는데 그 중 한가지가 계층적 접근 방식이다. 이 방식에서는 운영체제가 여러개의 층으로 나누어진다. 최하위 층(0층)은 하드웨어이고 최상위 층(N층)은 사용자 인터페이스이다. 운영체제 각 층(1층 ~ N-1층)은 데이터와 이를 조작하는 연산으로 구성된다.
각 층은 자료구조와 상위층에서 호출할 수 있는 루틴의 집합으로 구성된다. 해당 층 하위층에 대한 연산을 호출할 수 있다.
계층적 접근 방식의 주된 장점은 구현과 디버깅의 간단함이다. 층들은 단지 자신의 하위층들의 기능들만을 사용할 수 있다. 이러한 접근 방식은 시스템의 검증과 디버깅 작업을 단순화 한다. 운영체제 첫번째 층은 하드웨어만을 사용하여 이 층의 기능을 구현하고 하드웨어는 정확하다고 가정하기 때문에 나머지 층에서 첫번째 층에 대해 아무런 신경을 쓰지 않고 디버깅 할 수 있다. 첫번째 층의 디버깅이 끝나면 두번째 층을 디버깅하는 동안 첫번째 층이 정확하게 동작한다고 가정할 수 있으며 이러한 과정이 반복된다. 만약 디버깅 중 어느 층에서 오류가 발견되면 그 하위의 층은 이미 디버깅 되었기 때문에 오류는 반드시 그 층에 있다. 따라서 시스템을 계층으로 나누면 시스템의 설계나 구현이 간단해진다.
각 층은 하위층으로부터 제공된 연산들만 사용해 구현된다. 한 층은 이러한 연산이 어떻게 구현되는지 알 필요가 없고 다만 이러한 연산들이 무엇을 하는지만 알면된다. 그러므로 각 층은 특정 데이터구조, 연산, 그리고 하드웨어의 존재를 상위층에 숨기게 된다.
이런 시스템의 성능은 각 층을 통과하는 오버헤드로 인해 좋지 못하다. 그러나 어느 정도의 계층화는 현대 운영체제에서 일반적이다.

마이크로 커널

모놀리식 구조는 확장할 수록 관리하기 힘들어진다. 이에 마이크로커널 접근 방식을 이용한 Mach라 불리는 운영체제가 개발되었다. 이 방식은 덜 중요한 구성요소들을 커널에서 제거하고 사용자 수준 프로그램들로 구현하는 방법이다. 그로 인해 커널은 더욱 작아진다. 어느 서비스가 커널에 있어야할지에 대한 정의는 없다. 그러나 일반적으로 마이크로 커널은 통신 설비와 최소한의 프로세스, 그리고 메모리 관리를 제공한다. 마이크로 커널의 주 기능은 프로세스 간에 통신을 제공하는 것이다. 통신은 메세지 전달에 의해 제공된다. 예를 들면 만일 클라이언트 프로그램이 파일에 접근하기를 윈한다면 파일은 반드시 파일서버와 상호작용해야한다 클라이언트 프로그램과 서비스는 결코 직접 상호작용하지 않는다 오히려 그들은 마이크로커널과 메시지를 교환함으로써 간접적으로 상호작용한다.
마이크로커널 접근법의 장점은 운영체제의 확장이 쉽다는 것이다. 모든 새로운 서비스는 사용자 공간에 추가되며, 따라서 커널을 변경할 필요가 없다. 커널을 꼭 변경해야할 때는 작은 커널이기 때문에 변경할 대상이 비교적 적은 경향이 있다. 결과적으로 만들어진 운영체제는 한 하드웨어로부터 다른 하드웨어로 이식이 쉽다. 마이크로커널은 서비스 대부분이 커널이 아니라 사용자 프로세스로 수행되기 때문에 더욱 높은 보안성과 신뢰성을 제공한다. 만일 한 서비스가 잘못되더라도, 운영체제의 다른 부분은 아무런 영향을 받지 않기 때문이다.

마이크로커널은 가중된 오버헤드 때문에 성능이 나쁘다. 두 개의 사용자 서비스가 통신해야 하는 경우 별도의 주소 공간에 서비스가 존재하기 때문에 메시지가 복사되어야한다. 또한 운영체제는 메시지를 교환하기 위해 한 프로세스에서 다른 프로세스로 전환해야한다. 메시지 복사 및 프로세스 전환과 관련된 오버헤드는 마이크로커널 기반 운영체제의 성장에 가장 큰 걸림돌이었다.

모듈

운영체제를 설계하는 데 이용되는 최근 기술 중 최선책은 적재가능 커널 모듈(loadable kernel modules, LKM) 기법이다. 이 접근법에서 커널은 핵심적인 구성요소들을 가지고 있고 부팅 또는 실행 중에 부가적인 서비스들을 모듈을 통하여 링크할 수 있다. 이러한 유형의 설계는 Linux, Mac OS X, Solaris, Windows 등의 현대 UNIX를 구현하는 일반적인 방법이다.
설계의 주안점은 커널은 핵심 서비스를 제공하고 다른 서비스들은 커널이 실행되는 동안 동적으로 구현하는 것이다. 서비스를 동적으로 링크하는 것은 새로운 기능을 직접 커널에 추가하는 것보다 바람직하다. 수정 사항이 생길 때마다 커널을 다시 컴파일하지 않아도 되기 때문이다. CPU 스케줄링과 메모리 관리 알고리즘은 커널에 직접 구현하고 다양한 파일 시스템은 적재가능 모듈을 통하여 구현할 수 있다.

커널의 각 부분이 정의되고 보호된 인터페이스를 가진다는 점에서 계층 구조를 닮았다. 그러나 모듈에서 임의의 다른 모듈을 호출할 수 있다는 점에서 계층 구조보다 유연하다. 중심 모듈은 단지 핵심 기능만을 가지고 있고 다른 모듈의 적재 방법과 모듈들과 어떻게 통신하는지 안다는 점에서는 마이크로 커널과 유사하다. 그러나 통신하기 위하여 메세지 전달을 호출할 필요가 없기 때문에 더 효율적이다.

하이브리드

사실 엄격하게 정의된 하나의 구조를 채택한 운영체제는 거의 존재하지 않는다. 대신 다양한 구조를 결합하여 성능, 보안 및 편리성 문제를 해결하려는 혼용 구조로 구성된다. 예를 들어, Linux는 운영체제 전부가 하나의 주소 공간에 존재하여 효율적인 성능을 제공하기 때문에 모놀리식 구조이다. 그러나 모듈을 사용하기 때문에 새로운 기능을 동적으로 커널에 추가할 수 있다. 역시 성능상의 이유로 Windows도 모놀리식 구조라고 할 수 있다. 그러나 사용자 모드 프로세스로서 실행되는 분리된 서브시스템을 지원하는 등 전형적인 마이크로커널의 형태를 유지하고 있다. Windows 시스템은 또한 동적으로 적재가능 커널 모듈(LKM)도 지원한다.


운영체제 부팅

커널을 적재하여 컴퓨터를 시작하는 과정을 시스템 부팅 이라고 한다. 하드웨어는 커널의 위치 그리고 커널을 적재하는 방법을 어떻게 알 수 있을까? 대부분의 시스템에서 부팅과정은 다음과 같다.

  1. 부트스트랩 프로그램 또는 부트 로더라고 불리는 작은 코드가 커널의 위치를 찾는다.
  2. 커널이 메모리에 적재되고 시작된다.
  3. 커널이 하드웨어를 초기화 한다.
  4. root 파일 시스템이 마운트된다.

부트스트랩 프로그램은 ROM 이라 불리는 비휘발성 저장장치에 저장되어있기 전원이 꺼져도 내용이 사라지지 않으며 전원을 켰을때 가장 먼저 실행된다.
부트스트랩 프로그램은 다양한 작업을 수행한다. 커널 프로그램이 포함된 파일을 메모리에 적재하는 것 외에도 진단을 실시하여 메모리와 CPU를 점검하고 장치 시스템 상태를 확인한다. 진단을 통과하면 프로그램은 부팅 과정을 계속 진행할 수 있다. 부트스트랩은 CPU 레지스터, 장치 컨트롤러 및 메인 메모리를 초기화 할 수 있다. 그 후 운영체제를 시작하고 root 파일 시스템을 마운트 한다. 바로 이 시점에서 시스템이 실행 중이라고 말할 수 있다.

일반적으로 PC와 같은 작은 시스템의 경우 ROM에 있는 부트 로더는 더 단순한 기능을 가지고, 커널을 메모리로 올려줄 부트 프로그램은 디스크에 둔다. 부트 로더가 부트 프로그램을 메모리에 올려 실행시키면 부트 프로그램이 커널을 올려 실행시켜주는 방식을 취한다.

profile
JavaScript TypeScript React Next.js

0개의 댓글