운영체제는 컴퓨터 부품들을 관리하고, 내가 개발한 프로그램이 올바르게 실행되도록 돕는다.
프로그램에 실행에 마땅히 필요한 요소들을 가리켜 시스템자원, 혹은 줄여서 자원이라고한다. 지금까지 학습한 CPU, 메모리, 보조기억장치, 입출력장치등의 컴퓨터 부품들은 모두 자원이라고 볼 수 있고, 모든 프로그램은 실행되기 위해 반드시 자원이 필요하다.
여기서 실행할 프로그램에 필요한 자원을 할당하고, 프로그램이 올바르게 실행되도록 돕는 특별한 프로그램이 바로 운영체제이다. 운영체제도 인터넷 브라우저, 게임과 같은 프로그램이지만, 매우 특별한 프로그램이기 때문에 컴퓨터가 부팅 될 때 메모리 내 커널 영역에 따로 적재 되어 실행된다. 커널 영역을 제외한 나머지 영역, 사용자가 이용하는 응용 프로그램이 적재되는 영역을 사용자 영역이라고 한다.
운영체제는 실행할 프로그램을 메모리에 적재하고, 더 이상 실행되지 않는 프로그램을 메모리에서 삭제하며 지속적으로 메모리 자원을 관리한다.
운영체제는 사용자의 프로그램이 하드웨어 상에서 어떻게 작동하는지를 사용자보다 더 먼저, 더 자세히 알고 있다.
그리고 운영체제는 딱딱한 하드웨어가 아닌 프로그램이기 때문에 사용자와 대화 할 수 있고, 현재 하드웨어의 상태는 어떠한지, 사용자의 코드가 어떻게 실행되었는지, 하드웨어 상에 어떤 문제가 있었는지 등을 사용자에게 상세히 알려줄 수 있고, 이를 통해 사용자는 문제 해결의 실마리를 찾을 수 있다.
운영체제에서 매우 중요한 커널에 대해 알아보고, 응용 프로그램이 운영체제로 부터 어떻게 도움을 받으며 실행되는지를 이해하기 위해 이중모드와 시스템 호출이라는 개념을 알아본다.
자원에 접급하고 조작하는 기능, 프로그램이 올바르고 안전하게 시랳ㅇ되게 하는 기능이 운영체제의 핵심 서비스에 속한다. 이러한 운영체제의 서비스를 담당하는 부분을 커널이라고 한다.
운영체제가 제공하는 서비스 중 커널에 포함되지 않는 서비스도 있는데, 대표적으로 사용자 인터페이스가 있다.
운영체제가 제공하는 사용자 인터페이스 종류에는 그래픽 유저 인터페이스(GUI, Graphical User Interface)와 커맨드 라인 인터페이스(CLI, Command Line Interface)가 잇다.
이러한 사용자 인터페이스는 운영체제가 제공하는 서비스지만, 이는 그저 컴퓨터와 상호작용하기 위한 통로일 뿐 커널에 속하느 기능은 아니다. 실제로 같은 커널을 사용하더라도 사용자 인터페이스는 다를 수 있다.
운영체제는 사용자가 실행하는 응용 프로그램이 하드웨어 자원에 직접 접근하는 것을 방지하여 자원을 보호한다.
운영체제는 응용 프로그램들이 자원에 접근하려고 할 때 오직 자신을 통해서만 접근하도록 자원을 보호한다. 비유하자면 운영체제는 응용 프로그램의 자원접근을 대행하는 일종의 문지기 역할을 하는셈이다.
이러한 문지기 역할은 이중 모드로써 구현된다. 이중 모드란 CPU가 명령어를 실행하는 모드를 크게 사용자 모드와 커널 모드로 구분하는 방식이다.
사용자 모드는 운영체제 서비스를 제공받을 수 없는 실행모드이다. 즉, 커널영역의 코드를 실행할 수 없다. 일반적인 응용 프로그램은 기본적으로 사용자모드로 실행된다.
반면 커널 모드는 운영체제 서비스를 제공받을 수 있고, 커널영역의 코드를 실행할 수 있다. 자원에 접근하는 명령어를 비롯한 모든 명령어를 실핼 할 수 있다. 운영체제는 커널모드로 실행되기 때문에 자원에 접근 가능하다.
사용자 모드로 실행되는 프로그램이 자원에 접근하는 운영체제 서비스를 제공받으려면 운영체제에 요청을 보내 커널 모드로 전환되어야 한다. 이때 운영체제 서비스를 제공받기 위한 요청을 시스템 호출이라고 한다.
시스템 호출은 일종의 인터럽트이다. 정확히는 소프트웨어직은 인터럽트로, 입출력장치에 의해 발생하는 것이 아닌 특정 명령어에 의해 발생하는 것으로, 소프트웨어 인터럽트라고 한다.
학습할 운영체제의 핵심 서비스는 프로세스 관리, 자원 접근 및 할당, 파일시스템 관리이다.
실행중인 프로그램을 프로세스라고 한다. 일반적으로 하나의 CPU는 하나의 프로세스만 실행할 수 있기에 CPU는 이 프로세스들을 조금씩 번갈아가며 실행한다.
이때 각 프로세스는 상태도, 사용하고자 하는 자원도 다양하며, 실행 시점도 다 다르다. 그래서 운영체제는 다양한 프로세스를 일목요연하게 관리하고 실행할 수 있어야 한다.
모든 프로세스는 실행을 위해 자원을 필요로 하고, 운영체제는 프로세스들이 사용할 자원에 접근하고 조작함으로써 프로세스에 필요한 자원을 할당해준다. 컴퓨터의 네가지 핵심부품을 어떻게 관리하고, 결과적으로 어떤 기능을 제공하는지 알아보자
CPU
운영체제는 프로세스들에 공정하게 CPU를 할당하기 위해 어떤 프로세스부터 CPU를 이용하게 할 것인지, 얼마나 오래 CPU를 이용하게 할지를 결정할 수 있어야 한다. 이를 CPU 스케줄링이라고 한다.
메모리
운영체제는 새로운 프로세스가 적재될 때마다 어느 주소에 적재해야 할지를 결정해야 한다.
입출력장치
인터럽트 서비스 루틴은 운영체제가 제공하는 기능으로 커널 영역에 있다. 입출력장치가 발생시키는 인터럽트도 마찬가지이다. 입출력장치가 CPU에 하드웨어 인터럽트 요청 신호를 보내면 CPU는 하던 일을 잠시 백업한 뒤 커널 영역에 있는 인터럽트 서비스 루틴을 실행하고, 운영체제는 인터럽트를 처리하는 프로그램, 즉 인터럽트 서비스 루틴을 제공함으로써 입출력 작업을 수행한다.
파일을 열고, 생성하고, 삭제하기도 하며, 폴더로 관리하기도 한다. 당연해 보이는 이런 파일 시스템도 운영체제가 지원하는 핵심 서비스이다.
실행중인 프로그램을 프로세스라고 한다. 프로그램은 실행 되기 전까지는 그저 보조기억장치에 있는 데이터 덩어리일 뿐이지만, 보조기억장치에 저장된 프로그램을 메모리에 적재하고 실행하는 순간 그 프로그램은 프로세스가 된다. 그리고 이 과정을 '프로세스를 생성한다'라고 표현한다.
사용자가 보는 앞에서 실행되는 프로세스를 포그라운드 프로세스라고 하고, 사용자가 보지 못하는 뒤편에서 실행되는 프로세스는 백그라운드 프로세스라고 한다.
백그라운드 프로세스 중에서 사용자와 상호작용하지 않고 정해진 일만 수행하는 백그라운드 프로세스를 유닉스 체계에선 데몬이라 부르고, 윈도우에서는 서비스라고 부른다.
CPU 자원은 한정되어 있고, 모든 프로세스가 CPU를 동시에 사용할 수는 없다. 운영체제는 빠르게 번갈아 수행되는 프로세스의 실행 순서를 관리하고, 프로세스에 CPU를 비롯한 자원을 배분한다. 이를 위해 운영체제는 프로세스 제어 블록(PCB)를 이용한다.
PCB는 프로세스와 관련된 정볼르 저장하는 자료구조이다. 해당 프로세스를 식별하기 위해 꼭 필요한 정보들이 저장된다. PCB는 커널영역에 생성되며, 운영체제는 수많은 프로세스들 사이에서 PCB로 특정 프로세스를 식별하고 처리하는데 필요한 정보를 판단한다.
PCB는 프로세스 생성 시에 만들어지고 실행이 끝나면 폐기된다. PCB에 담기는 정보들은 아래와 같다.
하나의 프로세스에서 다른 프로세스로 실행 순서로 넘어갈때 기존 프로세스는 프로그램 카운터를 비롯한 각종 레지스터 값, 메모리 정보 등 지금까지의 중간정보를 백업해야한다. 이러한 중간 정보, 즉 하나의 프로세스 수행을 재개하기 위해 기억해야 할 정보를 문맥이라고 한다.
이처럼 기존 프로세스의 문맥을 PCB에 백업하고, 새로운 프로세스를 실행하기 위한 문맥을 PCB로부터 복구하여 새로운 프로세스를 실행하는 것을 문맥교환이라고 한다.
하나의 프로세스는 사용자 영역에 크게 코드 영역, 데이터 영역, 힙 영역, 스택 영역으로 나뉘어 저장된다.
코드 영역은 텍스트 영역이라고도 부른다. 이곳에는 기계어로 이루어진 명령어가 저장된다. 코드 영역에는 데이터가 아닌 CPU가 실행할 명령어가 담겨 있기 때문에 쓰기가 금지되어 읽기 전용 공간이다.
데이터영역은 잠깐 썼다가 없앨 데이터가 아닌 프로그램이 실행되는 동안 유지할 데이터가 저장되는 공간이다. 이런 데이터로는 전역 변수가대표적이다.
코드 영역과 데이터 영역은 크기가 변하지 않고, '크기가 고정된 영역'이라는 점에서 정적 할당 영역이라고도 부른다. 반면 힙 영역과 스택 영역은 실행과정에서 크기가 변할 수 있어 동적 할당 영역이라고도 부른다.
힙 영역은 프로그램을 만드는 사용자, 프로그래머가 직접 할당할 수 있는 저장 공간이다. 프로그래밍 과정에서 힙 영역에 메모리 공간을 할당 했다면 언젠가는 해당 공간을 반환해야한다.
메모리 공간을 반환하지 않는다면 할당한 공간은 메모리 내에 계속 남아 메모리 낭비를 초래하고, 이런 문제를 메모리 누수라고 한다.
스택 영역은 데이터를 일시적으로 저장하는 공간이다. 이런 데이터로는 함수의 실행이 끝나면 사라지는 매개 변수, 지역 변수가 대표적이다.
프로세스는 모두 저마다의 상태가 있고, 운영체제는 이런 프로세스의 상태를 PCB에 기록하여 관리한다. 그리고 많은 운영체제는 이처럼 동시에 실행되는 수많은 프로세스를 계층적으로 관리한다. 프로세스들의 상태와 계층적 관리에 대해 알아보자
Job이 커널에 등록된 상태. 프로세스가 생성된 상태
메모리 공간에 따라 어디로 갈지 정해진다. 메모리를 할당받으면 ready, 할당받지 못하면 suspended ready
프로세서와 필요한 자원을 모두 할당 받은 상태
열심히 작업을 실행하는 상태
소멸되기전에 Terminated에 들렀다 감.
프로세스는 실행 도중 시스템 호출을 통해 다른 프로세스를 생성할 수 있다. 이때 새 프로세스를 실행한 프로세스를 부모 프로세스, 부모 프로세스에 의해 생성된 프로세스를 자식 프로세스라고 한다.
부모 프로세스와 자식 프로세스는 엄연히 다른 프로세스이므로 각기 다른 PID를 가지고, 일부 운영체제에서는 자식 프로세스의 PCB에 부모 프로세스의 PID인 PPID가 기록되기도 한다.
부모 프로세스는 fork를 통해 자신의 복사본을 자식 프로세스로 생성해내고, 만들어진 복사본(자식)은 exec를 통해 자신의 메모리 공간을 다른 프로그램으로 교체한다.
fork와 exec는 시스템 호출이다. 부모 프로세스는 fork 시스템 호출로 자신의 복사본을 자식 프로세스로 생성하며, 자식 프로세스는 exec 시스템 호출로 자신의 메모리 공간을 새로운 프로그램으로 덮어씌어 새로운 프로그램 내용으로 전환하여 실행한다.
exec를 호줄하면 코드 영역과 데이터 영역의 내용이 실행할 프로그램의 내용으로 바뀌고, 나머지 영역은 초기화 된다.
스레드는 실행의 단위이다. 프로세스를 구성하는 실행의 흐름 단위이고, 하나의 프로세스는 여러개의 스레드를 가질 수 있다.
전통적인 관점에서 보면 하나의 프로세스는 한 번에 하나의 일만을 처리했다. 하나의 실행흐름을 가지고 한 번에 하나의 부분만 실행되는 프로세스를 가정했다. '실행의 흐름 단위가 하나'라는 점에서 이렇게 실행되는 프로세스들은 단일 스레드 프로세스라고 볼 수 있다. 하지만 스레드라는 개념이 도입되면서 하나의 프로세스가 한 번에 여러 일을 동시에 처리할 수 있게 되었다.
제어 요소 외 코드, 데이터 및 자원들은 프로세스 내 다른 스레드들과 공유한다.
여러 프로세스를 동시에 실행하는 것을 멀티프로세스, 그리고 여러 스레드로 프로세스를 동시에 실행하는 것을 멀티스레드라고 한다.
동일한 작업을 수행하는 단일 스레드 프로세스 여러개를 실행하는것과 하나의 프로세를 여러 스레드로 실행하는 것은 무엇이 다를까?
프로세스끼리는 기본적으로 자원을 공유하지 않지만, 스레드끼리는 같은 프로세스 내의 자원을 공유한다.
프로세스를 fork하여 같은 작업을 하는 동일한 프로세스 두 개를 동시에 실행하면 모든 자원이 복제되어 메모리에 적재되므로 어찌보면같은 프로그램을 실행하기 위해 메모리에 동일한 내용들이 중복해서 존재하므로 낭비가 된다.
이에 반해 스레드들은 각기 다른 스레드 ID, 레지스터 값, 스택을 가질 뿐 프로세스가 가지고 있는 자원을 공유하므로 여러 프로세스를 병행 실행하는 것보다 메모리를 더 효율적으로 사용할 수 있다. 또한 서로 다른 프로세스들은 기본적으로 자원을 공유하지 않기 때문에 서로가 남남처럼 독립적으로 실행되는 반면, 스레드는 프로세스의 자원을 공유하므로 협력과 통신에 유리하다.
프로세스의 자원을 공유한다는 특성은 때론 단점이 될 수 있다. 하나의 스레드에 무넺가 생기면 프로세스 전체에 문제가 생길 수 있다. 모든 스레드는 프로세스의 자원을 공유하고, 하나의 스레드에 문제가 생기면 다른 스레드도 영향을 받기 때문이다.
운영체제가 프로세스들에게 공정하고 합리적으로 CPU자원을 배분하는 것을 CPU 스케줄링이라고 한다. 이는 컴퓨터 성능과도 직결되는 대단히 중요한 문제이다.
프로세스들에게 공정하게 CPU를 배분하기 위해서는 각 프로세스 마다 다른 우선순위를 고려해야 한다, 우선순위가 높은 프로세스란 빨리 처리해야하는 프로세스들을 의미한다.
프로세스 종류마다 입출력장치를 이용하는 시간과 CPU를 이용하는 시간의 양에는 차이가 있다. 비디오 재생이나 디스크 백업 작업을 담당하는 프로세스와 같이 입출력 작업이 많은 프로세스를 입출력 집중 프로세스라고 하고, 복잡한 수학 연산, 컴파일, 그래픽 처리 작업을 담당하는 프로세스와 같이 CPU작업이 많은 프로세스를 CPU 집중 프로세스라고 한다.
상황에 맞게, 그리고 프로세스의 중요도에 맞게 프로세스가 CPU를 이용할 수 있도록 하기 위해 운영체제는 우선순위를 부여한다.
PCB에 우선순위가 적혀있다고는 하지만, CPU를 사용할 다음 프로세스를 찾기 위해 일일이 모든 프로세스의 PCB를 뒤적거리는 것인 비효율적이다. 그래서 운영체제는 프로세스들에게 CPU를 사용하고 싶은지, 메모리에 적재 되고 싶은지, 특정 입출력장치를 사용하고 싶은지 모두 줄을 세우고, 이 줄을 스케줄링 큐로 구현하고 관리한다.
운영체제가 관리하는 대부분의 자원은 큐로 관리하고 대표적으로 CPU를 이용하고 싶은 프로세스들의 줄인 준비 큐와 입출력장치를 이용하기 위한 대기 상태에 접어든 대기 큐가 있다.
할당 받을 자원을 스스로 반납할 때까지 사용. (빼앗기지 않음),
예)system call, I/O, Etc
타의에 의해 자원을 빼앗길 수 있음
예)할당시간 종료, 우선순위가 높은 프로세스 등장
CPU 스케줄링 알고리즘의 종류는 매우 다양하고 운영체제마다 서로 다른 스케줄링 알고리즘을 사용하고 있다. 작동 방식과 장단점을 이해하는 것이 중요하다.
Non-preemptive scheduling
스케줄링 기준
호위 효과, Convoy effect
긴 평균 응답시간(response time)
준비 큐에 삽입된 프로세스들 중 CPU 이용시간의 길이가 가장 짧은 프로세스부터 실행하는 스케줄링 방식이다. 비선점형 스케줄링이지만 선점형으로 구현될 수 있다.
선입 선처리 스케줄링에 타임 슬라이스라는 개념이 더해진 방식이다. 타임 슬라이스란 각 프로세스가 CPU를 사용할 수 있는 정해진 시간을 의미한다. 타임 슬라이스 만큼의 시간 동안 돌아가며 CPU를 이용하는 선점형 스케줄링이다.
라운드 로빈 스케줄링에서는 타임 슬라이스 크기가 매우 중요하다. 타임 슬라이스가 지나치게 크면 선입 선처리와 다를 바가 없고, 지나치게 작으면 문맥 교환에 발생하는 비용이 커 CPU는 프로세스 처리보다 전환하는 데에 힘을 다 쓸 여지가 있기 때문이다.
최단 작업 우선 스케줄링 알고리즘과 라운드 로빈 알고리즘을 합친 스케줄링 방식이다. 프로세스들은 정해진 타임 슬라이스만큼 CPU를 사용하되, CPU를 사용할 다음 프로세스로는 남아있는 작업 시간이 가장 적은 프로세스가 선택된다.
우선순위 스케줄링은 프로세스들에 우선순위를 부여하고, 가장 높은 우선순위를 가진 프로세스부터 실행하는 스케줄링 알고리즘이다.
다만, 우선순위가 높은 프로세스를 우선하여 처리하므로 우선순위가 낮은 프로세스는 실행이 계속 연기 될 수 있고, 이런 현상을 기아현상이라고 한다.
이를 방지하기 위한 대표적인 기법으로 에이징이 있다. 이는 오랫동안 대기한 프로세스의 우선순위를 점차 높이는 방식이다.
우선순위별로 준비 큐를 여러개 사용하는 스케줄링 방식이다.
프로세스의 Queue간 이동이 허용된 다단계 큐 스케줄링이다. 현재까지의 프로세서 사용 정보(패턴활용)을 통해 우선 순위 조정인 에이징 기법을 통해 기아 현상을 예방할 수 있다. 다단계 피드백 큐 스케줄링은 구현이 복잡하지만, 가장 일반적인 CPU 스케줄링 알고리즘으로 알려져 있다.
선입 선처리 스케줄링은 먼저 도착한 프로세스를 먼저 처리하는 스케줄링 알고리즘이다. 그러므로 준비 큐에 A,B,C,D 순으로 삽입된 순으로 A,B,C,D순으로 CPU를 할당받는다.