1. 멀티쓰레드란?
  2. 멀티 프로세스와 멀티 쓰레드의 차이점
  3. 멀티 쓰레딩의 장점
  4. 멀티 쓰레딩의 문제점

멀티 쓰레드란?

  • 하나의 프로세스를 다수의 실행 단위(즉, 쓰레드)로 구분하여 자원을 공유하고 자원의 생성과 관리의 중복성을 최소화하여 수행 능력을 향상시키는 것을 멀티 쓰레딩이라고 한다.
  • 하나의 프로그램이 동시에 여러 개의 일을 수행할 수 있도록 해주는 것이다.

멀티 쓰레드를 사용하는 이유는?

앞서 설명한 것처럼 프로세스를 이용하여 동시에 처리하던 일을 쓰레드로 구현할 경우 메모리 공간과 자원 소모를 줄일 수 있게 된다.

쓰레드 간의 통신이 필요한 경우에도 별도의 자원을 이용하는 것이 아니라 전역 변수의 공간 또는 동적으로 할당된 공간인 Heap 영역을 이용하여 데이터를 주고 받을 수 있는 통신을 할 수 있다.

그렇기 때문에 프로세스 간 통신 방법에 비해 쓰레드 간의 통신 방법이 훨씬 간단하다. 심지어 쓰레드의 문맥교환은 프로세스의 문맥교환과는 달리 캐시 메모리를 비울 필요가 없기 때문에 더 빠르다.

따라서 앞에서 언급한 것처럼 시스템의 처리량이 향상되고 자원 소모가 줄어들어 자연스럽게 프로그램의 응답 시간이 단축된다.[즉, 프로그램이 빨라진다.]

이러한 장점 때문에 여러 프로세스로 할 수 있는 작업들을 하나의 프로세스에 여러 쓰레드로 나누어 수행하는 것이다.

멀티 프로세스와 멀티 쓰레드의 차이점

  • 멀티 프로세스 : [데이터, 힙, 스택] 영역 모두를 비공유한다.
  • 멀티 쓰레드 : [데이터, 힙, 스택] 영역 중 스택 영역만 비공유하고 데이터와 힙 영역은 공유한다.

멀티 쓰레딩의 장점

프로세스 생성은 앞에서 언급한 것과 같이 많은 시간과 자원을 소비한다. 이러한 단점을 최소화 시킨 일종의 경량화된 프로세스인 쓰레드를 만들게 된 것이다.

  • 멀티 쓰레드에서 쓰레드 간 스택 영역만 비공유하고 데이터 영역과 힙 영역은 공유한다.
  • 쓰레드의 생성 및 컨텍스트 스위칭은 프로세스의 생성 및 컨텍스트 스위칭보다 빠르다.
  • 멀티 쓰레드 컨텍스트 스위칭 시 데이터 영역과 힙 영역을 올리고 내릴 필요가 없다.
  • 데이터 영역과 힙 영역을 통해 데이터 교환이 가능하다.
  • 쓰레드 사이에서의 데이터 교환에는 특별한 기법이 필요하지 않다.

멀티 쓰레딩의 문제점

멀티 프로세스 기반으로 프로그래밍할 때는 프로세스 간 공유하는 자원이 없기 때문에 동일한 자원에 대해 접근하는 일이 없었지만 멀티 쓰레딩을 기반으로 프로그래밍할 때는 이 부분을 신경써줘야 한다.

서로 다른 쓰레드가 데이터 영역과 힙 영역을 공유하기 때문에 어떤 쓰레드가 다른 쓰레드에서 사용중인 변수나 자료구조에 접근하여 엉뚱한 값을 읽어오거나 수정할 수 있다.

그렇기 때문에 멀티 쓰레딩 환경에서는 동기화 작업이 필요하다.

동기화란?

시스템의 한정적인 자원에 여러 쓰레드가 동시에 접근해서 사용하려고 하면 문제가 발생할 수 있다. 이런 문제를 방지하기 위해 쓰레드들에게 하나의 자원에 대한 처리 권한을 주거나 순서를 조정해주는 기법이다.

동기화를 통해 작업 처리 순서를 컨트롤하고 공유 자원에 대한 접근을 컨트롤 하는 것이다. 하지만 이로 인해 병목 현상이 발생하여 성능이 저하될 가능성이 높다. 그러므로 과도한 락(lock)으로 인한 병목 현상을 줄여야 한다.

공유 자원이 아닌 부분은 동기화 처리를 할 필요가 없다. 즉, 동기화 처리가 필요한 부분에만 synchronized 키워드를 통해 동기화 하는 것이다.

불필요한 부분까지 동기화를 할 경우 현재 쓰레드는 락(lock)을 획득한 쓰레드가 종료하기 전까지 대기해야 한다. 그렇게 되면 전체 성능에 악영향을 미치게 된다.

즉, 동기화를 하고자 할 때는 메소드 전체를 동기화 할 것인가 아니면 특정 부분만 동기화할 것인가를 고민해야 한다.

임계영역

둘 이상의 쓰레드(또는 프로세스)가 공유 자원에 동시에 접근하면 문제가 발생할 수 있다. 이런 문제를 일으킬 수 있는 코드 블록을 임계 영역이라고 한다. 다시 말해 임계 영역은 배타적 접근 권한(한 순간에 하나의 쓰레드만 접근)이 요구되는 공유 리소스(전역변수, static 변수 등)에 접근하는 코드 블럭을 의미한다.

동기화 방법

여러 쓰레드들은 자원을 공유하고, 프로세스간 메시지를 전송하면서 간혹 문제가 발생할 수 있다. 즉, 공유된 자원에 여러 프로세스, 쓰레드가 접근하면서 문제가 발생한다.

그래서 동기화를 통해 작업의 처리 순서와 공유 자원에 대한 접근을 컨트롤 하는 것이다.

Mutex/Semaphore/Monitor
3가지가 존재하고 모두 운영체제의 동기화 기법이다.
프로세스의 동기화 기법
쓰레드의 동기화 기법

뮤텍스

뮤텍스는 Mutual Exclusion의 약자로 공유 자원의 데이터를 여러 쓰레드가 접근하는 것을 막는 것이다.

뮤텍스의 쓰레드 동기화 방법은 임계 영역에 들어가기 위해 이 뮤텍스 객체를 가지고 있어야 들어갈 수 있다. 즉, 임계 영역을 가진 쓰레드들의 Running time이 서로 겹치지 않게 각각 단독으로 실행되게 하는 기술이다.

일종의 자물쇠 역할을 한다. 임계 영역에 들어간 쓰레드가 뮤텍스를 이용해 임계영역에서 본인이 나올 때까지 다른 쓰레드가 못 들어오게 내부에서 자물쇠로 잠근다.

fitting romm을 예로 들어 설명하겠다.

fitting room은 ‘사용중’ 표시를 하는 대신에 사용자들에게 열쇠를 지급한다. 열쇠를 지급받은 사용자는 fitting room에 들어가서 옷을 갈아입는다. 열쇠가 없는 사람들은 기다린다. 옷을 다 갈아입은 사람은 열쇠를 카운터에 반납한다. fitting room을 사용할 사람은 카운터로 가서 열쇠를 받아서 사용하면 된다.

이것이 바로 대략적인 뮤텍스에 대한 설명이다.
fitting room = 임계 영역
열쇠 = mutex
사용자 = 쓰레드

아래 그림을 보며 정리하자.

임계 영역(그림의 Protected Resource)에 진입하길 원하는 3개의 고양이(쓰레드)가 있다. 임계 영역에 접근하기 위해서는 뮤텍스라는 열쇠가 필요하다. 뮤텍스를 얻어야 임계 영역에 진입할 수 있다.

한 쓰레드(고양이)가 뮤텍스(열쇠)를 획득했다. 이제 이 쓰레드(고양이)는 임계 영역에 진입할 수 있게 된다.

진입한 쓰레드는 임계 영역을 처리한다. 이 때 다른 쓰레드들은 아무일도 못하고 쉬게 된다. 뮤텍스가 반환될 때까지 쉬면서 기다릴 수 밖에 없다.(Block 상태를 의미한다.)

임계 영역에 있는 쓰레드가 빠져나와 뮤텍스를 반납한다. 쉬던 쓰레드들이 이제 뮤텍스를 가질 수 있으며 이 쓰레드들 중 하나가 뮤텍스를 가지게 될 것이다. 그리고 지금까지의 과정을 반복한다.

세마포어

세마포어 역시 뮤텍스와 비슷한 역할을 하지만 세마포어는 동시 접근 동기화가 아닌 접근 순서 동기화에 더 관련있다.

내가 운영하는 별다방이 있다고 하자. 좌석은 테이블이 5개 있다. 각 테이블에는 한명의 손님만 사용할 수 있다고 가정

손님이 와서 빈 테이블을 보면 찾아서 앉고 시간을 보내고 집에 간다. 이 카페는 5개의 테이블만큼 손님을 받을 수가 있다. 5개의 테이블이 가득 차면 다음 손님은 기다려야 한다.

옷가게 fitting room에서는 한 명만 이용할 수 있었지만, 카페에서는 5 테이블을 이용할 수 있다. 이 점이 뮤텍스와 세마포어의 차이점이다.

카페가 임계 영역(critical section)이 되고, 손님은 쓰레드가 된다. 5개의 테이블이 바로 세마 포어이다. 뮤텍스가 5개가 되는 상황으로 이해할 수 있다. fitting room으로 바꾸면 최대 5명이 동시에 이용할 수 있는 큰 fitting room이 된다.

여기서는 개인의 권리가 없는 이상한 fitting room이 된다. 아무튼 이런 fitting room을 이용하려면 5개의 열쇠가 필요하다. 5개의 열쇠 즉, 5개의 뮤텍스가 바로 세마포어가 된다. 그래서 세마포어는 뮤텍스와 다르게 count와 관련된 사항이 중요하다.

세마포어는 다음의 세 가지 원자적인 연산만을 지원한다.

  • initialize, decrement, increment
  • initialize : 세마포어 초기화(음이 아닌 정수값으로 초기화)
  • decrement : 프로세스를 블록시킬 수 있다.
  • increment : 블록되었던 프로세스를 깨울 수 있다. 이 세마포어를 카운팅 세마포어 또는 범용 세마포어라고 한다. 세마포어의 값에 따라 운영체제는 프로세스가 즉시 자원을 사용할지, 자원이 다른 프로세스에 의해 사용 중인걸 알게 될 경우에는 일정 시간을 기다려야 한다.
    프로세스가 자원을 사용하는 동안에는 세마포어 값을 변경함으로써 다른 프로세스들이 기다리게 해야 한다.
  • 프로세스간 메시지를 전송하거나 고유 메모리를 통해 특정 데이터를 공유하게 될 경우 공유 자원에 여러 프로세스가 접근하면서 문제가 발생하기 때문에 하나의 프로세스만 공유 자원에 접근 가능하도록 설정할 때 세마포어를 사용한다.
  • 이진 세마포어 : 0 또는 1의 값을 가지는 세마포어

위 그림은 뮤텍스를 설명하는 그림이다. 세마포어는 다수의 뮤텍스로 이해할 수 있다고 했으므로 다음 그림처럼 표현할 수 있다.

임계 영역으로 들어갈 수 있는 열쇠인 세마포어가 3개이다. 어떤 쓰레드가 세마포어를 얻으면 남은 세마포어의 수는 2개이다. 또 어떤 쓰레드가 세마포어를 얻어서 진입했다. 남은 세마포어의 수는 1개이다. 또 어떤 쓰레드가 세마포어를 얻어서 진입했다. 그러면 남은 세마포어의 수는 0개이다. 또 어떤 쓰레드가 임계영역에 진입하려 할 때, 더이상 남은 세마포어가 없기 때문에 임계영역 내에 어떤 쓰레드가 빠져나오면서 세마포어를 반납할 때까지 기다려야 한다.

모니터

Mutex(Lock)와 Condition Variables(Queue라고도 함)을 가지고 있는 Synchronization 메카니즘이다. 예를 들어 자바에서 모든 객체는 Object 클래스를 상속 받는다. 이 Object 클래스에는 wait(), nofityAll(), nofity() 메소드를 가지고 있는데 이게 바로 Condition Variables 역할이라고 보면 된다.

고로 모든 자바 객체는 Monitor를 가지고 있다. 자바에서는 Mutual Exclusion 해결을 위한 구현체로 Synchronization 키워드가 있다. 예들 들어, Synchronization가 메소드에 선언되어 있고, 쓰레드 A가 이미 Lock을 획득해서 Critical Section(메소드, 임계영역)을 수행중이라고 가정하자. 쓰레드 B가 동일한 메소드를 수행하기 위해 해당 Object의 Lock을 획득해야 할 것이다. 이 Lock이 반환될 때까지 대기를 해야하는데 그 때 사용되는게 바로 Monitor이다.

쓰레드 A가 Lock을 반환하면 쓰레드 B는 기다렸다가 Lock을 획득하게 된다. 그리고 Critical Section인 메소드를 수행할 수 있게 된다. 물론 Synchronized 키워드를 사용했을 때 자동적으로 수행되는 내부 동작이고, 별도로 명시적인 Monitor를 구현할 수도 있다. 그 외 Monitor의 다른 정의로는 공유자원에 안전하게 접근하기 위해 Mutual Exclusion가 랩핑된 Thread-Safe한 클래스, 객체, 모듈들을 의미하기도 한다.

하지만, 위의 모니터에 대한 설명은 무슨 말인지 이해가 가지 않는다. ㅜㅜ 이해가지 않으니 차이점이라도 알고 넘어가자.

우선 뮤텍스 / 모니터 / 세마포어는 개념적으로 차이가 있다.

전자(뮤텍스, 모니터)는 상호 배제를 함으로써 임계구역에 하나의 쓰레드만 들어갈 수 있다.

상호 배제란?

여기서 등장하는 상호 배제는 무엇일까??
상호 배제는 한 프로세스가 공유 자원을 접근하는 임계 영역 코드를 수행하고 있으면 다른 프로세스ㅡㄹ은 공유 자원을 접근하는 임계 영역의 코드를 수행할 수 없다는 조건이다.

  • 임계 영역을 보호하기 위한 개념
  • 둘 이상의 프로세스(혹은 쓰레드)가 공유자원에 대해 동시에 읽거나 쓰는 것을 방지하기 위한 기법
  • 상호 배제 기법에는 뮤텍스, 세마포어, 모니터, 메시지 전달 등의 기법이 있다.

후자(세마포어)는 하나의 쓰레드만 들어가거나 혹은 여러 개의 쓰레드가 들어가게 할 수도 있다.

Q. 뮤텍스와 모니터의 차이는?

  • 가장 큰 차이는 뮤텍스다른 프로세스(애플리케이션) 간에 동기화를 위해 사용한다. 반면에 모니터는 하나의 프로세스(애플리케이션)내에 다른 쓰레드들 간에 동기화를 위해 사용한다.
  • 또한, 뮤텍스는 보통 운영체제 커널에 의해서 제공되는 반면에 모니터는 프레임워크나 라이브러리 그 자체에서 제공된다.
  • 따라서 뮤텍스는 무겁고(heavy-weight) 느리며(slower) 모니터는 가볍고(light-weight) 빠르다(faster).

Q. 세마포어와 모니터의 차이는?

  • Java에서는 모니터를 모든 객체에게 기본적으로 제공하고 있는 반면 C에서는 모니터를 사용할 수 없다.
  • 세마포어는 카운터라는 변수 값으로 프로그래머가 상호 배제나 정렬의 목적으로 사용시 매번 값을 따로 지정해줘야 하는 등 조금 번거롭다.
  • 반면, 모니터는 이러한 일들이 캡슐화(encapsulation)되어 있어서 개발자는 카운터값을 1 또는 0으로 주어야 하는 고민을 할 필요없이 synchronized, wait(), nofity() 등의 키워드를 이용해 조금 더 편하게 동기화를 할 수 있다.

Q. 뮤텍스와 세마포어의 차이는?

세마포어는 뮤텍스가 될 수 있지만
뮤텍스는 세마포어가 될 수 없다.

(뮤텍스) 세마포어 -> 가능
(세마포어) 뮤텍스 -> 불가능

세마포어는 소유할 수 없는 반면
뮤텍스는 소유할 수 있고 소유자가 이에 책임을 진다.

뮤텍스는 1개만 동기화가 되지만
세마포어는 하나 이상을 동기화할 수 있다.

Example

변기가 하나뿐인 화장실에서는
앞의 사람이 볼일을 마치고 나와야 다음 사람이 들어갈 수 있다.
이렇게 한번에 오직 하나만 처리할 수 있는 대상에 사용하는 것이 뮤텍스이다.

변기가 세개인 화장실에서는 동시에 세 사람이 볼일을 볼 수 있고
이 세 사람 중 아무나 한명이 나오면 다음 사람이 들어가서 볼일을 볼 수 있다.
이렇게 동시에 제한된 수의 여러 처리가 가능하면 세마포어를 사용한다.

만약 변기 세개짜리 화장실의 각 변기에 대해 뮤텍스를 사용한다면
대기 중인 사람은 각 변기 앞에 줄을 서는 것이고
이렇게 되면 옆 칸이 비어도 들어가지 못하게 된다.

만약 변기 세개를 묶어서 뮤텍스를 사용한다면
변기 수에 관계없이 무조건 한명만 사용할 수 있게 된다.

변기 - 동기화 대상(즉,공유 자원)
사람 - 동기화 대상에 접근하는 쓰레드

뮤텍스와 세마포어의 목적은 특정 동기화 대상이 이미 특정 쓰레드에 의해서 사용중일 경우 다른 쓰레드가 해당 동기화 대상에 접근하는 것을 제한하는 것으로 동일하지만 관리하는 동기화 대상이 몇 개인가에 따라 차이가 생기게 된다.

참고

Comment and share

메모리 관점에서 본 쓰레드

이를 보기 전에 먼저 메모리 관점에서 본 프로세스편을 보고 오기를 추천한다.

각각의 프로세스는 메모리 공간에서 독립적으로 존재한다.

위의 그림은 프로세스를 구성하는 메모리 공간의 모습이다. 각각의 프로세스는 자신만의 이런 메모리 구조를 가진다. 프로세스 A, B, C가 존재한다면 각각의 프로세스는 모두 위와 같은 구조의 메모리 공간을 가진다.

독립적인 만큼 다른 프로세스의 메모리 공간에 접근할 수도 없다. A가 B의 메모리 공간에 접근하게 된다면 재앙이 발생할 수도 있다.
예를 들면, Chrome이 windows 메모리 공간에 접근한다면 안전성이 보장되지 않는 문제가 발생할 것이다.

운영체제의 메모리 공간에 접근하여 뭔가를 변경한다면 심각한 문제가 발생할 수 있다.(물론 운영체제의 메모리 공간에 접근하는 것은 원천적으로 불가능하다.) 그러므로 프로세스의 안전성을 보장하기 위해서는 프로세스는 각각 독립된 메모리 공간을 가져야 한다.

그렇다면 프로세스 A에서 연산한 결과를 프로세스 B가 받아서 사용하고 싶다면 어떻게 해야할까??

IPC

(inter process communication)

A의 메모리 공간에 B가 직접 접근하지 못하기 때문에 프로세스간의 통신을 하는 특별한 방법이 존재하는데 메일슬롯, 파이프 등이 바로 프로세스 간의 통신 즉, IPC의 예라고 할 수 있다.

IPC에 대한 설명은 프로세스부분을 참고하면 된다.

여기서 중요한 점은 프로세스는 독립적인 메모리 공간을 지니기 때문에 IPC를 통하지 않고서는 통신할 수 없다는 사실이다. 그리고 프로세스가 여럿이 병렬적으로 실행되기 위해서는 필연적으로 Context Switching이 발생할 수밖에 없다.

1
2
독립적인 메모리 공간으로 컨텍스트 스위칭이 발생한다.
프로세스 간 통신하기 위해서는 IPC가 필요하다.

위의 두 가지 프로세스가 지니는 문제점을 한 번에 해결할 수 있는 녀석이 쓰레드이다.

메모리 공간에서의 쓰레드

위 그림은 프로세스와 쓰레드의 메모리 구조의 차이점을 보여주는 그림이다. 왼쪽의 프로세스는 앞에서 설명했고, 오른쪽의 쓰레드의 메모리 구조를 확인해보자.

쓰레드는 프로세스 안에 존재하는 실행 흐름이다. 메모리 구조 역시 그러하다. 특이한 점은 쓰레드는 프로세스의 heap, stack, code 영역 등을 공유한다는 사실이다. 각각의 프로세스가 독립적인 stack, heap, data, code 영역을 가진 반면에 한 프로세스에 속한 쓰레드는 stack 영역을 제외한 메모리 영역은 프로세스의 메모리 영역을 공유해서 사용한다.

쓰레드가 code 영역을 공유하기 때문에 한 프로세스 내부의 다른 쓰레드들은 프로세스가 가지고 있는 함수를 자연스럽게 호출할 수 있다.

뿐만 아니라 쓰레드는 data, heap 영역을 공유하기 때문에 IPC 없이도 쓰레드 간의 통신이 가능하다. 동일한 프로세스 내부에 존재하는 쓰레드 A, B가 통신하기 위해 heap 영역에 메모리 공간을 할당하고 두 쓰레드가 자유롭게 접근한다고 생각하면 된다.

쓰레드는 프로세스처럼 스케줄리으이 대상이다. 이 과정에서 컨텍스트 스위칭이 발생한다. 하지만 쓰레드는 공유하고 있는 메모리 영역 덕분에 컨텍스트 스위칭이 발생하는 오버헤드가 프로세스에 비해 작다.

쓰레드란?


동일한 일을 하는 부분이 많은 프로세스를 여러 개 생성하는 것은 메모리 낭비이다. 그래서 프로세스는 하나만 생성하고 Program Counter만 여러 개 생성한다. 즉, CPU 수행 단위를 여러 개를 두는 것이며 이를 쓰레드라고 한다.

각각 다른 Instruction을 수행하려면 Program Counter 값이 존재해야 하고, 메모리에 어떤 레지스터 값들을 세팅해야 할텐데, 쓰레드마다 Program Counter 값과 레지스터 값들이 들어가는 것이다. 이렇게 쓰레드를 생성하여 쓰레드마다 다른 부분의 코드를 실행할 수 있게 하면 된다.

  • 쓰레드란 프로그램(프로세스) 실행의 단위이며 하나의 프로세스는 1개 이상의 쓰레드로 구성이 가능하다.

  • 하나의 프로세스를 구성하는 쓰레드들은 프로세스에 할당된 메모리, 자원 등을 공유한다.

  • 프로세스와 같이 실행, 준비, 대기 등의 실행 상태를 가지며 실행 상태가 변할 때마다 쓰레드 문맥 교환(context switching)을 수행한다.

  • 각 쓰레드별로 자신만의 스택레지스터를 가진다.

  • 특징

    • 스레드는 프로세스 내에서 Stack만 따로 할당받고, 프로세스의 Code, Data, Heap 영역은 공유한다.
    • 스레드는 한 프로세스 내에서 동작되는 여러 실행의 흐름으로, 프로세스 내의 주소 공간이나 자원들(힙 공간 등)을 같은 프로세스 내에 스레드끼리 공유하며 실행된다.
    • 같은 프로세스 안에 있는 여러 스레드들은 같은 힙 공간을 공유한다. 반면에 프로세스는 다른 프로세스의 메모리에 직접 접근할 수 없다.
    • 각각의 스레드는 별도의 레지스터와 스택을 갖고 있지만, 힙 메모리는 서로 읽고 쓸 수 있다.
    • 한 스레드가 프로세스 자원을 변경하면, 다른 이웃 스레드(sibling thread)도 그 변경 결과를 즉시 볼 수 있다.

쓰레드를 생성하여 프로세스를 수행했을 때의 장점

  1. 응답시간이 줄어든다.(Responsiveness)

하나의 프로세스 안에 여러 쓰레드를 두게 되면 즉, 다중 쓰레드로 구성된 프로세스 내에서는 하나의 서버 쓰레드가 blocked(or waiting)상태인 동안에도 동일한 프로세스 내의 다른 쓰레드가 실행되어 보다 빠른 처리가 가능하다. 웹 브라우저에서 네트워크를 통해 웹 페이지를 읽어올 때 읽어오는 작업이 오래 걸리기 때문에, 웹 브라우저 상태가 blocked 상태가 된다. 하나의 쓰레드가 데이터를 읽어오는 동안에 다른 쓰레드가 읽어온 데이터를 화면에 뿌려주는 역할을 하면 되는 것이다.

  1. 시스템의 자원소모가 줄어든다.(Resource Sharing로 인해)

하나의 프로세스 안에 여러 개의 쓰레드를 두면 메모리 낭비를 줄일 수 있다. 비슷한 작업에 대해서는 리소스를 공유한 결과이다.

  1. Economy

생성하고 switch 하는 면에서 프로세스보다 오버헤드가 적고 빠르다. 이를 경제적이라고 표현한다.[아직 정확한 원리를 잘 모른다.]

  1. Utilization of MP(MultiProcessor) Architectures

쓰레드를 생성하게 되면 병렬성을 높일 수 있다. 이는 CPU가 여러 개인 컴퓨터에서만 얻을 수 있는 장점이지만 쓰레드를 통해 이 장점을 취할 수 있다.

프로세스와 쓰레드의 차이

프로세스는 운영체제로부터 자원을 할당받는 작업의 단위이고
쓰레드는 프로세스가 할당받은 자원을 이용하는 실행의 단위이다.

  • 프로세스는 실행중인 프로그램으로 디스크로부터 메모리에 적재되어 CPU의 할당을 받을 수 있는 것을 말한다.

  • 하지만 프로세스의 생성은 많은 시간과 자원을 소모한다.

  • 쓰레드는 프로세스의 실행단위라고 위에서 설명했다.

  • 한 프로세스 내에서 동작하는 여러 실행 흐름으로 프로세스 내의 주소 공간이나 자원 등을 공유할 수 있다.

  • 이 경우 각각의 쓰레드는 독립적인 작업을 수행해야 하기 때문에 각자의 스택PC 레지스터 값을 갖고 있다.

쓰레드마다 스택을 독립적으로 할당하는 이유

스택은 함수 호출시 전달되는 인자, 복귀 주소값 및 함수 내에서 선언하는 변수 등을 저장하기 위해 사용되는 메모리 공간으로 스택 메모리 공간이 독립적이라는 것은 독립적인 함수 호출이 가능하다는 것이고 이는 독립적인 실행 흐름이 가능하게 한다.

따라서 독립적인 실행 흐름을 위한 최소 조건으로 독립된 스택을 할당한다.

쓰레드마다 PC Register를 독립적으로 할당하는 이유

PC 값은 쓰레드가 명령어의 어디까지 수행했는지를 나타내게 된다. 쓰레드는 CPU를 할당받았다가 스케쥴러에 의해 다시 선점당한다. 그렇기 때문에 명령어가 연속적으로 수행되지 못하고 어느 부분까지 수행했는지 기억할 필요가 있다. 따라서 PC 레지스터를 독립적으로 할당하는 것이다.

쓰레드의 장점

  • 쓰레드는 프로세스보다 생성 및 종료시간, 쓰레드간 전환시간이 짧다.
  • 쓰레드는 프로세스의 메모리 자원 ㄷ으을 공유하므로 커널의 도움 없이
    상호 간에 통신이 가능하다.
  • 프로세스 간의 통신 방법에 비해 쓰레드의 간의 통신 방법이 훨씬 간단하다.[별도의 자원을 이용하는 것이 아니라 전역 변수의 공간을 이용하여 데이터를 주고 받을 수 있다.]

쓰레드의 단점

  1. 동기화 문제

앞에서 언급한 쓰레드의 장점 중에서 쓰레드 간의 통신시 데이터를 주고 받는 방법은 메모리 공간을 공유하여 데이터 세그먼트, 즉 전역변수를 이용한다고 했다. 그런데 공유하는 전역 변수를 여러 쓰레드가 함께 사용하려면 충돌하는 문제가 발생한다.

따라서 쓰레드 간에 통신할 경우에는 충돌 문제가 발생하지 않도록 동기화를 통하여 문제를 해결해야 한다.

  1. 멀티 쓰레드를 이용할 때 주의 깊게 설계해야 한다.
  2. 프로그램 디버깅이 어렵다.(왜지?)
  3. 단일 프로세서 시스템에서는 효과를 기대하기 어렵다.

쓰레드의 종류

  1. kernel Thread(커널 쓰레드)

프레소스 내 스레드가 여러 개라는 것을 OS가 알고 있는 쓰레드이다. 그래서 커널이 쓰레드 스케쥴링을 맡아서 하게 된다.

  1. User Thread(유저 쓰레드)

라이브러리 차원에서 지원되는 쓰레드이다. 프로세스 안에 쓰레드가 여러 개 있다는 것을 OS가 모르기 때문에 커널 입장에서는 하나의 일반적인 프로세스로 인식된다.

참고

Comment and share

프로그램과 프로세스의 개념

쓰레드를 먼저 알기 전에 프로그램과 프로세스의 개념을 알아보자.

프로그램이란 디스크 내의 실행 가능한 파일을 말하며,
프로세스란 현재 실행 중인 프로그램을 말하며 태스크(task)라고도 부른다.
프로세스는 시스템 작업의 기본단위로 모든 운영체제는 프로세스 개념을 바탕으로 동작한다.
그리고 이 프로세스를 구성하는 요소들을 프로세스의 문맥(process contexts)라고 한다.

예를 들어, 윈도우 환경에서 엑셀 프로그램이 있을 때 이를 실행하여 엑셀창(윈도우)를 띄우면 이는 프로세스가 된다.

  • 특징
    • 프로세스는 각각 독립된 메모리 영역(Code, Data, Stack, Heap의 구조)을 할당받는다.
    • 기본적으로 프로세스당 최소 1개의 스레드(메인 스레드)를 가지고 있다.
    • 각 프로세스는 별도의 주소 공간에서 실행되며, 한 프로세스는 다른 프로세스의 변수나 자료구조에 접근할 수 없다.
    • 한 프로세스가 다른 프로세스의 자원에 접근하려면 프로세스 간의 통신(IPC, inter-process-communication)을 사용해야 한다.
      ex) 파이프, 파일, 소켓 등을 이용한 통신 방법 이용

프로그램이 실행되면 위 그림과 같이 메모리 공간에 프로세스가 존재한다.

  • stack : 지역변수 할당과 함수 호출 시 전달되는 인자값들을 저장하기 위한 공간
  • heap : C의 malloc, calloc와 C++,Java의 new를 통한 동적 할당을 위해 존재하는 공간
  • data : 전역 변수나 static 변수의 할당을 위해 존재하는 공간
  • code : 프로그램을 실행시키면 실행파일 내에 존재하는 명령어가 메모리 상에 올라가야 프로그램을 동작시킬 수 있다. 이 명령어들을 위해 존재하는 공간(쉽게 말해 소스코드가 올라간다고 생각하면 됨)

이렇게 프로그램을 실행시키기 위해서는 메모리에 process를 위한 공간이 필요하다. 메모리 공간을 기준으로 process를 살펴본 결과이다. 이는 나중에 쓰레드와 비교할때에도 중요한 의미를 갖는다. 특정 프로그램이 실행되기 위해서는 즉, process가 되려면 메모리에 위 그림과 같은 할당이 필수적으로 이루어져야 한다. 메모리가 할당된 다음에 CPU에 의해서 처리된다.

여기서 프로세스의 문맥이란 무엇을 말하는가?

  1. CPU 수행 상태를 나타내는 하드웨어 문맥. Program Counter 등의 register들 값을 포함한다.
    PC는 다음에 수행할 명령어의 위치에 대한 정보(주소값)을 담고 있다.
    CPU가 할당되었을 때, PC가 가리키는 부분부터 수행해나가면 되는 것이다.
    즉, 프로세스가 실행될 부분이라고 생각할 수 있다.
  1. 프로세스의 주소 공간을 포함한다.
    즉, code, data, stack 각각의 공간에 어떠한 값이 들어있는가를 나타내는 주소값을 포함한다.
    현재 변수의 값을 얼마인가? 메모리에는 어떤 내용들이 담겨져 있는가? 스택에는 어느 내용까지 쌓여있는가? 등에 대한 정보를 모두 담고 있다.
  1. 프로세스 관련 커널 자료구조를 포함한다.
    PCB(Process Control Block), Kernel Stack 등을 포함한다.
    프로세스가 하나 시작될 때마다 운영체제에서는 그 프로세스를 관리하기 위해 PCB를 생성한다.
    커널의 주소 공간에서 data에 해당하는 부분에 PCB를 저장해둔다.
    이는 운영체제가 이 프로세스를 어떻게 관리하는지, 어떻게 다루고 있는지 파악할 때 사용된다.
    시스템 콜이 발생할 때, 어떤 프로세스가 시스템 콜을 했는지 알기 위해 커널 주소 공간의 Stack에는 시스템 콜을 한 프로세스의 커널 스택 값을 저장하고 있다.

운영체제가 문맥(context)을 알고 있어야 하는 이유

현재의 컴퓨터는 time sharing, multitasking 체제이다. 즉, 하나의 CPU가 여러 개의 프로세스를 동시에 수행한다. 짧은 속도로 여러 개의 프로세스를 수행해야 하는 CPU의 입장에서는 이전에 수행하고 있던 프로세스가 어디까지 진행되었는지를 알아야 할 필요가 생긴다.(그래야 효율적이다.) 그 어디까지 진행되었는지에 대한 값을 문맥 즉, context에 저장하고 있기 때문에 이 문맥을 CPU가 알고 있어야 한다.

프로세스 제어 블록(Process Control Block, PCB)

프로세스 제어 블록이란 프로세스를 관리하기 위해 필요한 프로세스 요소들의 자료구조이다.

운영체제가 현재 CPU 제어권을 다른 프로세스에게 넘겨줄 때 실행중인 프로세스의 정보를 PCB에 저장한다. CPU 제어권을 다시 넘겨받은 경우 PCB에 저장되어 있던 정보를 불러와 추후 작업을 실행한다.

PCB는 주로 다음과 같이 구성된다.

  • 프로세스 식별자(Identifier)
  • 프로세스 상태(State)
  • 우선순위(Priority)같은 스케줄링에 대한 정보
  • 메모리 포인터(Memory pointer)
  • CPU 수행에 관련된 프로그램 카운터(Program counter), 각종 register 값
  • I/O 상태정보(I/O status information)
  • code, data, stack에 대한 정보

프로세스 상태

프로세스는 실행되면서 자신의 상태가 시시각가 변한다.

  • 생성(New) : 프로세스가 생성 중
    • 프로세스가 생성되었지만 실행가능한 프로세스 집합에 소속되지 못한 상태(프로그램이 메모리에 적재되지 않은 상태)
  • 준비(Ready) : 프로세스가 설정되어 대기중
    • CPU를 할당받기 위해 준비중인 상태(즉 Queue에서 대기하고 있는 상태를 의미한다. 이는 물리적인 메모리에 적재된 상태를 말한다.)
  • 실행(Running) : 프로세스가 실행하는 중
    • 프로세스가 CPU를 할당받아 기계어 명령어를 수행중인 상태
  • 대기(Block, wait, sleep) : 프로세스가 어떤 사건이 발생하기를 기다리고 있는 상태
    • 당장 CPU를 할당해줘도 instruction을 수행할 수 없는 상태를 말한다. 디스크에서 file을 읽어와야 하는 오래 걸리는 작업을 하고 있거나 다른 프로세스의 진행을 위해 일부러 재워둔 경우에 해당한다.
  • 종료(Exit) : 프로세스가 실행 종료
    • 프로세스가 실행 종료된 상태(프로그램이 메모리에서 해제된 상태) 프로세스가 종료되면 정리하는 작업을 진행하게 되는데 이 상태에 해당한다.

프로세스 상태 전이

프로세스는 아래와 같은 상태 전이를 가지며 주로 아래와 같이 명명한다.

  • 디스패치(Dispatch) : 준비 -> 실행
  • 타임 아웃(Time out) : 실행 -> 준비
  • 대기(block) 또는 사건 준비(Event Wait) : 실행 -> 대기
  • 깨움(wake up) 또는 사건 발생(Event Occurs) : 대기 -> 준비
  • new -> ready : new 상태에서 프로세스가 생성되면 OS 커널에 존재하는 Ready Queue에 올라가게 된다.
  • ready -> running : Ready Queue에 있는 프로세스들을 OS가 프로세스 스케쥴링 알고리즘에 의해서 Running 상태로 가야할 프로세스에게 CPU를 할당한다. 그러면 프로세스가 Running 상태가 된다.
  • running -> ready : 현재 running 상태에 있는 프로세스 A보다 Ready Queue에서 대기하고 있는 프로세스 B가 우선순위가 높으면, preemptive schedule(선점형)인 경우 프로세스 A는 Ready 상태로 오게 되고 프로세스 B가 Running 상태가 되어 CPU를 할당 받게 된다.
  • running -> blocked : 현재 running 상태에 있는 프로세스 A에게 입출력(I/O) 이벤트가 발생했을 때 프로세스 A가 blocked 상태로 가게 된다.
  • running -> terminate : 프로세스 종료.

Context Switch(문맥 교환)

문맥 교환이란 CPU를 어떤 프로세스에서 다른 프로세스로 넘겨주는 과정을 말한다. System call이나 interrupt가 발생헀다고 해서 반드시 Context Switch가 발생하는 것은 아니다. 프로세스 내부에서 System call을 요청하거나 interrupt가 외부에서 들어왔어도 운영체제에서의 일을 마치고 다시 할당되었던 CPU로 넘어가게 된다.
다른 프로세스로 넘어가는 과정은 상당한 오버헤드를 발생시킨다.
cache memory에 있던 진행하던 프로세스에 대한 cache를 모두 비워줘야 하기 때문이다. 그래서 문맥 교환이 일어나는 상황은 크게 두 가지 경우이다.

Interrupt 중에서도 timer interrupt가 들어왔을 때와 I/O 요청 System Call이 들어왔을 때이다.

전환하는 동안에는 어떠한 유용한 작업도 불가능하다.

IPC

준비중.

참고

Comment and share

프로그램의 구조와 인터럽트


  • 프로그램이 CPU에서 명령을 수행하려면 수행하려는 주소 영역이 메모리에 올라가 있어야 한다. 이 때, 프로그램의 주소 영역은 크게 코드, 데이터, 스택 영역으로 구분된다.
  • 코드 영역 : 우리가 작성한 프로그램 함수들의 코드가 기계어 명렁으로 변환되어 저장되는 부분이다.
  • 데이터 영역 : 전역 변수 등 프로그램이 사용하는 데이터를 저장하는 부분이다.
  • 스택 영역 : 함수가 호출될 때 호출된 함수의 수행을 마치고 복귀할 주소 및 데이터를 임시로 저장하는 데 사용되는 공간이다.
  • 일반적으로 프로그램 내에서 발생되는 함수 호출에 필요한 복귀 주소는 각 프로그램의 주소 공간 중 스택 영역에 보관한다.
  • 반면, 인터럽트 때문에 CPU를 선점당한 위치를 저장하기 위한 공간은 OS 커널 부분에 존재하게 된다.
  • OS는 현재 실행 중인 모든 프로그램을 관리하기 위한 자료구조를 유지하고 있다.
1
2
3
4
예를 들어, A, B 두 개의 프로그램이 현재 수행중이라면
커널 어딘가에 이 두 프로그램을 관리하기 위한 자료 구조가 존재한다.

이 자료 구조를 "프로세스 제어 블록(PCB)"라 부른다.
  • PCB에는 인터럽트가 발생했을 때 그 프로그램의 어느 부분까지 수행했는지를 저장하기 위한 영역이 존재한다.
1
2
3
4
5
6
프로그램 A가 수행 중에 인터럽트가 발생하면
현재 실행 중이던 지점을 A의 프로세스 제어 블록에 저장한 후,

인터럽트 처리를 모두 마치면
프로그램 A의 프로세스 제어 블록에 저장된 주소를 복원시켜
원래 수행하던 일을 재개하게 된다.

컴퓨터 시스템의 작동 개요


  • CPU를 컴퓨터의 두뇌라고 부르지만
    CPU는 인간의 뇌처럼 스스로 생각하고 판단하는 능력을 갖추고 있지는 못하다.
    이는 CPU가 빠른 속도로 처리하는 계산 능력은 가지고 있지만,
    어떠한 작업을 수행해야 하는지에 대해 스스로 결정하는 능력이 없기 때문이다.

  • CPU는 현재 수행해야 할 메모리 주소의 명령을 있는 그대로 처리할 뿐이다.
    이 때, CPU가 수행해야 할 메모리 주소를 담고 있는 레지스터를 프로그램 카운터라고 부른다.
    즉, CPU는 매번 프로그램 카운터가 가리키는 메모리 영역의 명령을 처리하게 된다.

  • 일반적으로 조건문, 반복문, 함수 호출 등에 의한 주소 이동이 없는 이상 프로그램 카운터는 바로 다음 주소의 명령을 가리키게 되어 코드의 순차적인 수행이 이루어진다.

  • 메모리에는 사용자 프로그램과 OS가 같이 올라가 수행된다.
    이 때 CPU는 프로그램 카운터가 가리키는 메모리 위치의 프로그램을 수행하게 된다.

1
2
3
4
if 만약 프로그램 카운터가 메모리 주소 중 OS가 존재하는 부분을 가리킨다면
CPU가 커널 모드에서 수행 중이라고 이야기한다.
else
CPU가 사용자 모드에서 수행 중이라고 이야기한다.
  • CPU가 수행하는 명령에는 일반 명령특권 명령이 있다.
  • 일반 명령은 메모리에서 자료를 읽어와서 CPU에서 계산하고 결과를 메모리에 쓰는 일련의 명령들을 말한다. 이러한 일반 명령은 모든 프로그램이 수행할 수 있는 명령이다.
  • 특권 명령은 보안이 필요한 명령으로 입출력 장치, 타이머 등 각종 장치를 접근하는 명령이다.
  • 컴퓨터 시스템에서는 CPU 내에 모드 비트를 두어 특권 명령을 항상 OS만이 수행할 수 있도록 제한하고 있다.
  • 사용자 프로그램이 특권 명령의 수행이 필요한 경우 OS에게 특권 명령의 대행을 요청한다.
    이와 같은 서비스 요청을 시스템 콜이라고 한다.
  • 사용자 프로그램이 시스템 콜을 하게 되면 OS는 자신의 커널 영역에 정의된 시스템 콜 처리 코드를 수행하게 된다.
  • 프로그램이 아닌 주변 장치가 CPU에게 서비스를 요청할 때에도 시스템 콜과 비슷한 방식을 사용한다.
  • CPU는 프로그램 카운터가 가리키는 메모리 위치의 명령만 계속 수행하기 때문에 주변 장치의 상태를 지속적으로 파악할 수 없다.
    따라서, 주변 장치는 CPU의 도움이 필요한 경우 인터럽트를 사용해 CPU에게 서비스를 요청하게 된다.
  • 인터럽트를 발생시키기 위해 주변 장치는 인터럽트 라인을 세팅하고
    CPU는 매번 명령을 수행한 후 인터럽트 라인을 체크해 서비스 요청이 있는지 확인한다.
  • 인터럽트가 발생하면 CPU는 해당 인터럽트를 처리하기 위한 루틴으로 넘어가서 커널 내의 인터럽트 처리 코드를 수행한다.

프로그램의 실행


  • "프로그램이 실행되고 있다"는 것은 컴퓨터 시스템 차원에서 볼 때 크게 두 가지 중요한 의미를 갖는다.
  1. 디스크에 존재하던 실행 파일이 메모리에 적재됨을 의미한다.
  2. 프로그램이 CPU를 할당받고 기계 명령을 수행하고 있는 상태를 의미한다.

일반적인 컴퓨터 시스템의 경우 CPU는 하나밖에 없으므로 매 시점 CPU에서 명령을 수행하는 프로그램은 단 하나뿐이다. 하지만, 여러 프로그램이 짧은 시간 단위로 CPU를 나누어 쓰고, 이들 프로그램이 메모리에 동시에 적재되어 있을 수 있으므로 여러 프로그램이 동시에 실행된다는 말을 보편적으로 사용하는 것이다.

  • 실행 파일메모리에 적재될 때 실행 파일 전체가 메모리에 한꺼번에 올라가기 보다는 일부분만 메모리에 올라가고 나머지는 디스크의 특정 영역에 내려가 있는 것이 일반적이다. 이는 여러 프로그램이 공유하는 메모리 공간을 효율적으로 사용하기 위한 방법이다.

  • 프로그램의 주소 공간 중 당장 CPU의 수행에 필요한 부분은 메모리에 올려놓고 그렇지 않은 부분은 디스크 중 메모리의 연장 공간으로 사용되는 스왑 영역에 내려놓는 방식으로 운영된다.

실행 파일이 실행되어 물리적 메모리에 적재되는 모습
  • 프로그램의 주소 공간은 코드, 데이터, 스택 등으로 구성된다.
    각각의 프로그램마다 이러한 주소 공간을 별도로 가지며, 각 프로그램마다 독자적으로 존재하는 이와 같은 주소 공간을 가상 메모리 또는 논리적 메모리라 부른다.
주소 변환에 의해 프로세스의 논리적 주소 공간이 물리적 메모리에 적재되는 모습

OS도 하나의 프로그램이므로 OS 커널 역시 코드, 데이터, 스택의 주소 공간을 갖으며, 각각은 아래와 같은 역할 및 기능을 갖는다.

  • 커널의 코드

    • OS의 기능 : 아랫단의 HW 자원을 효율적으로 관리하는 일 + 윗단의 응용프로그램 및 사용자에게 편리한 서비스 제공
    • CPU, 메모리 등의 자원을 관리하기 위한 부분 + 사용자에게 편리한 인터페이스를 제공하기 위한 부분이 주를 이룬다.
    • 이 밖에도 시스템 콜 및 인터럽트를 처리하기 위한 부분을 포함한다.
  • 커널의 데이터 영역

    • 각종 자원을 관리하기 위한 자료 구조가 저장된다.
    • CPU, 메모리와 같은 HW 자원을 관리하기 위한 자료 구조뿐만 아니라 현재 수행 중인 프로그램을 관리하기 위한 자료 구조도 커널의 데이터 영역에 유지된다.
    • 이 때, 현재 수행 중인 프로그램을 프로세스라고 부른다.
    • 각 프로세스의 상태, CPU 사용 정보, 메모리 사용 정보 등을 유지하기 위한 자료구조인 PCB가 존재한다.
    • 즉, HW + SW를 포함하는 시스템 내의 모든 자원을 관리하기 위한 자료 구조를 각각 유지한다.
  • 커널의 스택 영역

    • 함수 호출 시 복귀 주소를 저장하기 위한 용도로 사용된다.
    • 커널의 스택은 일반 사용자 프로그램의 스택과 달리 현재 수행중인 프로세스마다 별도의 스택을 두어 관리한다.
    • 즉, 프로그램이 실행되어 자기 자신의 코드 내에서 함수 호출 및 복귀 주소를 유지하기 위해서는 자기 주소 공간 내의 스택을 사용하고, 시스템 콜 등 커널 내의 함수를 호출하는 경우에는 커널의 주소 공간에 존재하는 커널 스택을 사용하게 된다.

Q. 프로세스마다 별도의 스택을 두는 이유

A. 프로세스가 함수를 호출할 때 자기 주소 영역 내부에 정의된 함수를 호출하면 자신의 스택에 복귀 주소를 저장하지만, 프로세스가 특권 명령을 수행하려고 커널에 정의된 시스템 콜을 호출하고 시스템 콜 내부에서 다른 함수를 호출하는 경우에 그 복귀 주소는 커널 내의 주소가 되어 사용자 프로그램의 스택과는 별도의 저장 공간이 필요하기 때문이다.

또한, 커널은 일종의 공유 코드로서 모든 사용자 프로그램이 시스템 콜을 통해 커널의 함수를 접근할 수 있으므로 일관성 유지를 위해 각 프로세스마다 커널 내에 별도의 스택을 두게 된다.

운영체제의 주소 공간 구조

사용자 프로그램의 사용 함수


프로그램이 사용하는 함수는 크게 사용자 정의 함수, 라이브러리 함수, 커널 함수 세 가지로 구분할 수 있다.

  1. 사용자 정의 함수란 프로그래머가 직접 작성한 함수를 뜻한다.
  2. 라이브러리 함수란 누군가 작성해 놓은 함수를 호출만 하여 사용하는 경우를 뜻한다.
  3. 커널 함수는 OS 커널의 정의된 함수를 뜻한다.

사용자 정의 함수와 라이브러리 함수는 모두 컴파일하여 실행 파일을 만들게 되면 프로그램의 코드 부분에 기계 명령 형태로 삽입된다. 따라서 이 두 함수는 프로그램이 실행될 때에 해당 프로세스의 주소 공간에 포함된다. 또한, 함수 호출 시에도 자신의 주소 공간에 있는 스택을 사용하게 된다.

커널 함수의 종류에는 사용자 프로그램이 OS의 서비스를 요청하기 위한 시스템 콜 함수와 HW/SW가 CPU의 서비스를 요청하기 위한 인터럽트 처리 함수가 있다.

  • 이와 같은 커널 함수는 OS 커널의 주소 공간에 코드가 정의된다. 즉, OS가 있는 함수를 사용자 프로그램이 호출해서 사용하는 것이다.

인터럽트


인터럽트와 관련된 주요 용어로는 인터럽트 벡터인터럽트 핸들러가 있다.

  • 인터럽트 벡터
    • 여러가지 인터럽트에 대해 해당 인터럽트 발생 시 처리해야 할 루틴의 주소를 보관하고 있는 테이블을 의미한다.
    • 일종의 함수를 가리키는 포인터라고도 할 수 있다.
  • 인터럽트 핸들러
    • 실제 인터럽트를 처리하기 위한 루틴으로 인터럽트 서비스 루틴이라고도 부른다.
    • OS 코드 부분에는 각종 인터럽트별로 처리해야 할 내용이 이미 프로그램되어 있으며, 이 부분을 인터럽트 서비스 루틴 또는 인터럽트 핸들러라고 부른다.

시스템 콜


  • 컴퓨터 시스템에서는 HW 및 SW 자원의 보안을 위해 CPU가 실행할 수 있는 명령을 일반 명령과 특권 명령으로 나누어 관리한다.

사용자 프로그램이 CPU의 제어권을 가지고 프로그램을 수행하다 보면, 입출력 등 특권 명령을 수행해야 할 필요가 잇다. 이 경우 사용자 프로그램은 OS에게 시스템 콜을 통해 특권 명령의 대행을 요청하게 된다.
사용자 프로그램이 이와 같이 특권 명령을 수행하는 커널 함수를 호출하게 되면 CPU의 제어권이 OS에게 넘어가게 된다.
이 때 하드웨어적으로 모드 비트가 1에서 0으로 자동 세팅되기 때문에 OS는 특권 명령을 수행할 수 있다.

모든 프로그램은 자신의 독자적인 주소 공간을 가지고 있으며, 프로그램이 함수 호출을 하는 경우 자신의 주소 공간 내에서 호출이 이루어지게 된다. 그러나 시스템 콜은 비록 함수 호출이긴 하지만 자신의 주소 공간을 거스르는 영역에 존재ㅐ하는 함수를 호출하는 것이다.
커널이라는 다른 프로그램의 주소 공간에 존재하는 함수를 호출하는 일이기 때문이다.

  • 시스템 콜은 주소 공간 자체가 다른 곳으로 이동해야 하므로 일반 함수를 호출하는 것과는 상이한 방법을 사용한다. 프로그램 자신이 인터럽트 라인에 인터럽트를 세팅하는 명령을 통해 이루어진다.

어떤 프로그램이 CPU를 할당받고 명령을 수행하다 중간에 CPU를 선점당하는 경우는 크게 두 가지 경우가 있다.

  1. 타이머에 의해 인터럽트 발생

특정 프로그램이 CPU를 독점하는 것을 방지하기 위한 하드웨어로 CPU 할당 시간이 만료되면 인터럽트를 발생시킨다.

이러한 타이머는 여러 프로세스가 CPU를 나누어 사용하는 시분할 시스템의 구현을 위한 필수적인 요소이다.

  1. 입출력 요청을 위한 시스템 콜

오래 거리는 입출력 작업이 완료되기까지 그 프로세스에게 CPU를 다시 할당하더라도 당장 다음 명령을 수행하지 못하는 경우가 일반적이므로 CPU를 다른 프로세스에게 이향하게 된다.

입출력을 요청해는 프로세스는 입출력 요청이 완료되어 컨트롤러가 입터럽트를 발생시킨 시점부터 다시 CPU를 얻을 수 있는 기회가 생기며 CPU를 기다리는 큐에 삽입하여 CPU 할당을 기다린다.

프로세스의 상태


프로세스의 상태는 실행, 준비, 봉쇄의 3가지로 크게 나누어 볼 수 있다.

실행

CPU를 할당받고 기계어 명령을 수행하고 있는 프로세스의 상태이다.

준비 상태

CPU만 할당받으면 당장 명령을 수행할 수 있지만 CPU가 하나밖에 없어 현재 CPU를 할당받지 못한 프로세스의 상태이다.

봉쇄 상태

CPU를 할당받더라도 명령을 수행할 수 없는 프로세스의 상태이다.
프로세스가 요청한 입출력 작업이 진행중인 경우 CPU를 할당받더라도 입출력이 끝나기 전까지는 작업을 진행할 수 없기 때문에 CPU를 할당하지 않는다.

준비상태에 있는 프로세스가 실행 상태로 변경되는 경우는 실행 상태에 있던 프로세스가 입출력 요청 등으로 봉쇄 상태가 되거나 또는 실행 상태에 있던 프로세스의 CPU 할당 시간이 만료되어 타이머 인터럽트가 발생한 경우를 들 수 있다.

  • OS는 준비 상태에 있는 프로세스들을 줄 세우기 위해 준비 큐(Ready Queue)를 두고 준비 큐의 제일 앞에 있는 프로세스에게 CPU를 할당한다.
    준비 큐에 프로세스를 줄 세우는 방법은 CPU 스케줄링 방법에 따라 달라진다.

OS는 특정 자원을 기다리는 프로세스들을 줄 세우기 위해 자원별로 큐를 두고 있다.

예를 들어 디스크에 입출력 서비스를 요청한 프로세스들은 디스크 입출력 큐(Disk I/O Queue)에 줄을 서게 된다. 그러면, 디스크 컨트롤러는 디스크 입출력 큐에 줄 서 있는 순서대로 프로세스들의 입출력 작업을 수행하게 된다. 프로세스별 입출력 작업이 완료되면 디스크 컨트롤러가 CPU에게 인터럽트를 발생시키고 그러면 인터럽트 처리 루틴에 의해 디스크 입출력이 완료된 프로세스는 입출력 큐에서 빠져나와 CPU를 기다리는 준비 큐에 줄을 서게 된다.

위에서 언급된 큐는 HW 자원을 기다리는 프로세스들을 줄 세우기 위한 것이었다.
이와 같은 큐는 SW 자원을 기다리는 경우에도 필요한데, 아래와 같다.

예를 들어 데이터에 대한 접근 권한은 SW 자원으로 분류될 수 있다.
어떠한 프로세스가 공유 데이터를 사용하고 있는 도중에
다른 프로세스가 같은 데이터를 접근하면 데이터에 대한 일관성이 훼손될 수 있다.

따라서, 공유 데이터는 매 시점 하나의 프로세스만이 접근할 수 있도록 해야 한다.
이 때 접근한다는 의미가 반드시 CPU가 그 데이터를 사용하고 있다는 의미는 아니다.

공유 데이터를 접근 중인 프로세스가 "준비 상태"나 "봉쇄 상태"로 변경된 경우에도
새롭게 CPU를 할당받은 프로세스가 동일한 데이터를 접근하게 되면
데이터의 일관성이 깨질 수 있으므로 접근을 허락해서는 안된다.

즉, 공유 데이터라는 일종의 SW 자원을 앞서 접근 중인 프로세스가 다 사용하고 반납할 때까지는 다른 프로세스가 CPU를 할당 받았다 하더라도 접근하지 않고 기다려야 하는 것이다. 여러 프로세스가 공유 데이터를 동시에 접근하려고 할 경우 공유 데이터를 기다리는 "큐"에 줄을 서게 하여 현재 그 데이터를 사용중인 프로세스가 데이터를 반납하기 전까지는 접근을 못하게 하고, 반납할 경우 큐에 줄 서 있는 순서대로 데이터의 접근 권한을 주는 방법을 사용하게 된다.

  • 위 그림처럼 프로세스의 상태 관리는 커널의 주소 영역 중 데이터 영역에 다양한 큐를 두어 이루어지게 된다.
  • 각 프로세스들이 CPU를 기다리는지, 입출력을 기다리는지 등의 정보를 커널이 총체적으로 관리하고 있다는 뜻이다.
1
2
3
4
5
6
7
8
9
예를 들어 타이머 입터럽트가 발생하면
커널은 자신의 데이터 영역에 있는 준비 큐의 정보를 참조해
다음에 어느 프로세스에게 CPU를 할당할지 결정하고
현재 실행되던 프로세스는 준비 큐의 제일 뒤로 보내진다.

준비 큐는 CPU 를 할당받기 위해 기다리고 있는 큐이므로
어떤 프로세스에게 CPU를 할당할지 결정하려면 준비 큐를 봐야 한다.
타이머 인터럽트이기 때문에 "봉쇄 상태"가 아니라 준비 큐의 가장 마지막에 재삽입하는 것이다.
입출력 요청이었을 시에는 "봉쇄 상태"로 빠져 준비 큐에 들어가지 못한다.

프로세스의 두 가지 실행 상태


하나의 프로세스가 시작되어 수행을 완료하기까지는 프로세스 자신의 주소 공간에 있는 코드만 실행되는 것이 아니라 커널의 주소 공간에 있는 코드도 실행된다.

이는 프로그램이 사용자 정의 함수나 라이브러리 함수뿐 아니라 입출력 시스템 콜 등을 통해 OS 커널의 함수도 호출하여 실행하기 때문이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
예를 들어 프로세스 A가 CPU에서 실행되고 있다고 하면
이는 자신의 주소 공간에 정의된 코드를 실행하는 것과
커널의 시스템 콜 함수를 실행하는 것으로 나누어 볼 수 있다.

전자를 사용자 모드에서의 실행 상태(User mode Running)이라 하고,
후자를 커널 모드에서의 실행 상태(User mode Running)라고 한다.

한가지 주의할 점은 시스템 콜이 수행되는 동안
프로세스 A의 코드가 아니라 OS 커널의 코드이지만
커널이 실행 상태에 있다고 하지 않고
프로세스 A가 실행 상태에 있다고 말한다.

프로세스 A 입장에서는 CPU를 OS 커널에게 빼앗긴 것으로 생각할 수도 있지만
커널의 코드가 실행되는 것이 사실상 프로세스 A가 해야 할 일을 대행하는
것이기 때문에 시스템 콜이 실행 중일 때에도 여전히 프로세스 A는 실행 상태에
있는것으로 간주한다.

다만, 프로세스 A 자신의 코드를 실행하는 것과 구분지어
이러한 상태를 프로세스 A가 커널 모드에서 실행중이라고 이야기한다.
  • 정리하자면, 프로그램이 시작되어 종료될 때까지 다양한 함수 호출을 하며 실행되는데, 이를 사용자 모드커널 모드의 실행 상태로 구분 지을 수 있다.
  • 프로그램이 사용자 정의 함수나 라이브러리 함수를 호출할 때에는 모드의 변경 없이 사용자 모드에서의 실행을 하게 되며, 시스템 콜을 하는 경우에는 커널 모드로 진입해 커널의 주소 공간에 정의된 함수를 실행하게 된다. 시스템 콜의 실행이 끝나면 다시 사용자 모드로 복귀해서 시스템 콜 이후의 명령들을 계속 실행하게 된다. 프로그램의 실행이 끝날 때에는 커널 모드로 진입해 프로그램을 종료하게 된다.

Comment and share

컴퓨터 시스템의 구조


OS 중 항상 메모리에 올라가 있는 부분은 전체 OS 중 일부분에 해당되는데, 이 부분은 커널이라고 한다.

CPU와 I/O 연산


  • 컴퓨터에서 연산을 한다는 뜻은 CPU가 무언가 일을 한다는 것을 의미한다.
  • 컴퓨터 내의 각 하드웨어 장치들에는 컨트롤러(Controller:제어기)라는 것이 붙어있다.
  • 컨트롤러는 일종의 작은 CPU로서, 컴퓨터 전체에 CPU라는 중앙 처리 장치가 있듯이, 컨트롤러는 각 하드웨어 장치마다 존재하면서 이들을 제어하는 작은 CPU라고 할 수 있다.
  • 각 장치마다 이를 제어하기 위해 설치된 장체 컨트롤러에는 장치로부터 들어오고 나가는 데이터를 임시로 저장하기 위한 작은 메모리를 갖고 있으며, 이를 로컬 버퍼라고 부른다.
  • 이 때 장치에서 로컬 버퍼로 읽어오는 일은 컨트롤러가 담당한다.
  • 데이터를 모두 가지고 왔는지 확인하는 작업은 메인 CPU가 일일이 체크하는 것이 아니라 장치에 있는 컨트롤러가 인터럽트를 발생시켜 CPU에게 보고한다.
  • 이 때 인터럽트란 컨트롤러들이 CPU의 서비스가 필요할 때 이를 통보하는 방법을 말한다.
  • 기본적으로 CPU는 매 시점 메모리에서 명령을 하나씩 읽어와서 수행한다.
  • 이 때 CPU 옆에는 인터럽트 라인이 있어서 CPU가 자신의 작업을 하던 중간에 인터럽트 라인에 신호가 들어오면 하던 일을 멈추고, 인터럽트와 관련된 일을 처리한다.
    즉, CPU는 명령 하나를 수행할 때마다 인터럽트가 발생했는지를 체크한다.

인터럽트의 일반적 기능


  • OS 커널에는 인터럽트가 들어왔을 때 해야 할 일을 미리 다 프로그래밍해서 보관하고 있다. 그 중 한가지가 인터럽트 처리 루틴이다.
  • 먼저, 로컬 버퍼에 있는 내용을 프로그램 B가 사용할 수 있도록 메모리에 전달하고, 이제 B가 CPU를 할당받을 경우 다음 명령(instruction)을 수행할 수 있음을 표시한다.
  • 인터럽트가 발생하면 CPU는 하던 일을 멈추고 인터럽트를 처리하기 위한 루틴(OS 커널 내부 코드)에 들어가서 정의된 일을 찾게 된다.
  • OS는 할 일을 쉽게 찾아가기 위해 인터럽트 벡터라는 것을 갖고 있다.

인터럽트 벡터란?
인터럽트 종류마다 번호를 정해서 번호에 따라 처리해야 할 코드가 위치한 부분을 포인터로 가리키는 자료 구조이다.

  • 실제 처리해야 할 내용은 인터럽트 서비스 루틴이라는 다른 곳에 정의된다.

인터럽트 핸들링


  • CPU에서 명령이 수행될 때에는 CPU 내부에 있는 임시 기억 장치인 레지스터에 데이터를 Read/Write 하는 작업을 수행하게 되는데, 인터럽트가 발생해 새로운 명령을 수행하면 기존의 레지스터 값들이 지워지게 되므로 레지스터 값 등 CPU 내의 하드웨어 상태를 저장해 두어야 한다.

  • OS 커널 영역에는 현재 시스템 내에서 수행되는 프로그램들을 관리하기 위한 자료구조인 프로세스 제어 블록(PCB)을 두고 있다.

    • PCB 중 일부분은 인터럽트 발생 시 프로그램의 어느 부분이 수행되던 중이었는지를 저장하기 위해 사용된다.
    • 저장되는 내용으로는 현재 수행 중이던 메모리 주소와 레지스터 값, HW 상태 등이 있다.
    • 인터럽트 수행이 끝나면 PCB에 저장된 값을 CPU 상에 다시 복원해 직전의 명령을 계속 수행할 수 있도록 한다.
  • 현재 수행 중인 프로그램의 수가 n개라고 할 때, 커널 스택은 n만큼 독립적인 공간을 둔다.

    • 즉, 인터럽트 처리 루틴으로 넘어와서 함수 호출이 이루어질 경우에는 각 프로세스별로 독자적인 커널 스택을 사용하게 된다.
    • 프로그램 A가 수행 중에 인터럽트가 발생하면 현재까지 수행된 지점을 프로세스 제어 블록(PCB)에 저장하고, 인터럽트 처리 루틴으로 와서 커널 코드를 수행하게 되며, 이 때 이루어지는 함수 호출은 프로세스 A의 커널 스택을 사용하게 된다
  • 통상적으로 인터럽트라 하면 HW 인터럽트를 의미하고, SW 인터럽트는 트랩(trap)라는 용어를 사용한다.

    • SW 인터럽트의 예로는 예외 상황(Exception), 시스템 콜(System Call)등이 있다.
    • SW 인터럽트는 HW 인터럽트처럼 컨트롤러가 발생시키는 인터럽트가 아니라 프로그램 수행 도중 직접 CPU에 인터럽트 라인을 세팅하여 발생시킨다.

입출력 구조


입출력(Input/Output)이란 컴퓨터 시스템이 컴퓨터 외부의 주변 장치들과 데이터를 주고 받는 것을 말한다. 입출력 방식에는 동기식 입출력과 비동기식 입출력이 있다.

동기식 입출력

  • 입출력 요청 후 입출력 작업이 완료된 후에야 CPU의 제어권이 그 프로그램에게 다시 넘어갈 수 있는 방식을 말한다. [호출한 프로그램에게 다시 넘어간다!]

Example

1
2
3
4
5
프로그램 A가 CPU를 할당받고 명령을 수행하다가 입출력 요청을 하게 되면 
입출력 작업이 완료될 때까지 CPU의 낭비가 초래되므로
CPU를 프로그램 A에게서 선점해 다른 프로그램 B에게 할당하게 된다.
그러면 프로그램 B가 CPU를 할당받아 명령을 수행하고, 프로그램 A의 입출력이 완료될 때까지 A에게는 CPU를 다시 할당하지 않는다.
이는 입출력이 완료될 때까지 그 프로그램에 CPU를 할당하더라도 명령을 수행하지 못하기 때문이다.
  • 이것을 프로그램 봉쇄 상태(Blocked State)로 전환시킨다고 말한다.
1
2
3
4
5
6
7
선점 시 다음과 같은 문제 발생이 가능하다.
프로그램 A가 파일의 내용을 1에서 3으로 바꾸는 입출력을 요청
그러면 A로부터 CPU를 선점해서 프로그램 B에게 할당
프로그램 B는 파일의 내용 값을 1 증가시키는 연산 실행

그러면 결과적으로
1->3->4가 아닌 1->2->3이 된다.
  • 따라서 동기식 입출력에서는 입출력 요청의 동기화를 위해 장치별로 큐(Queue)를 두어 요청 순서대로 처리하여 동기화 문제를 해결한다.

동기식 입출력 요약

  1. 동기식 입출력을 요청한 프로그램은 입출력이 완료될 때까지 다음 명령을 수행할 수 없기에 CPU가 낭비된다.
  2. CPU의 낭비를 막고 효율적인 사용을 위해 입출력이 수행되는 동안 다른 프로그램에게 CPU를 양도하여 동시에 다수의 입출력 연산이 가능하게 한다.
  3. 다수의 프로그램이 동시에 입출력 연산을 요청하는 경우 동기성을 보장하기 위해 장치마다 큐를 두어 해결한다.

동기식 입출력 과정(1)

1
2
3
4
5
6
7
8
사용자가 I/O 요청을 하면 먼저 OS의 커널로 CPU의 제어권이 넘어와서
입출력 처리와 관련된 커널의 코드가 수행된다.

이 때, 입출력을 호출한 프로세스의 상태를 봉쇄 상태로 바꾸고
입출력이 완료될 때까지 CPU를 할당받지 못하도록 한다.

입출력이 완료되면 I/O 컨트롤러가 CPU에게 인터럽트를 발생시켜 입출력이 완려되었음을 알려준다.
그러면 이 프로세스의 봉쇄 상태를 해제시켜 CPU를 할당받을 수 있는 권한디 다시 생기게 된다.

동기식 입출력 과정(2)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
프로그램 A가 실행 중에 디스크에서 어떤 데이터를 읽어오는 명령을 만나면
프로그램 A는 시스템 콜을 통해 CPU에게 S/W 인터럽트를 발생시킨다.

그러면 CPU는 지금까지 프로그램 A의 작업을 멈추고 현재 상태를 PCB에 저장 후
인터럽트에 의해 처리해야 할 커널의 루틴으로 이동한다.

처리 루틴으로 이동하면 CPU는 컨트롤러에게 입출력 연산을 요청한다.
그러면 컨트롤러는 A가 요청한 데이터를 디스크로부터 자신의 로컬 버퍼로 읽어온다.

읽어오는 동안 프로그램 A는 봉쇄 상태가 되고 CPU를 다른 프로세스에게 선점당하게 되며,
CPU는 다른 프로세스에게 할댕되어 계속 CPU가 일을 할 수 있도록 한다.

원하는 정보가 로컬 버퍼로 다 들어오면 컨트롤러는 CPU에게 인터럽트를 발생시킨다.
이 때 발생하는 인터럽트는 H/W 인터럽테에 해당한다.

프로그램 B를 수행중이던 CPU는 작업을 멈추고 현재 상태를 저장 후 인터럽트를 처리하게 된다.

인터럽트 처리 루틴은 로컬 버퍼에 있는 A가 요청한 데이터를 A의 메모리 영역으로 읽어오고, A의 봉쇄 상태를 해제시킨다.
그러면 A는 다시 CPU를 기다리는 줄에 대기하게 된다.

그 후 CPU는 원래 수행하던 프로그램 B의 지점으로 돌아가서 하던 업무를 계속 수행한다.

프로그램 A는 CPU를 기다리는 줄에서 기다리다 자신의 차례가 되면 CPU를 할당 받고 입출력 연산 이후의 작업을 수행하게 된다.

비동기식 입출력


  • 입출력 연산 요청 후 연산 결과를 기다리는게 아니라 CPU의 제어권을 다시 프로그램에게 곧바로 부여하는 방식이다.
  • 어떤 프로그램이 데이터를 디스크에서 읽어오라는 요청을 했을 때 보통은 읽어온 결과를 이용해서 다음 연산을 수행하지만 경우에 따라서는 그 데이터와 관련없이 수행할 수 있는 일이 있을 수 있다.
  • 비동기식 입출력에서는 그러한 작업을 먼저 수행하고, 읽어오는 데이터가 반드시 있어야 수행할 수 있는 일들은 입출력이 완료된 후에 수행하게 된다.

비동기식 입출력 과정

1
2
3
비동기식 입출력에서는 CPU의 제어권이 입출력을 요청한 프로세스에게 곧바로 다시 주어지게 되며, 입출력 연산이 완료되는 것과 무관하게 처리 가능한 작업부터 시작한다.

한편, 비동기식 입출력에서도 입출력 연산이 완료도면 동기식과 맟나가지로 인터럽트를 통해 이를 CPU에게 알려주게 된다. 그러면 그 시점부터 읽어온 데이터를 필요로 하는 명령을 수행하게 할 수 있게 된다.

DMA(Direct Memory Access)


  • 원칙적으로 메모리는 CPU에 의해서만 접근 가능한 장치이다.
    따라서, 주변 장치들이 메모리에 접근하기 위해서는 CPU에게 입터럽트를 발생시켜 CPU가 일을 대행하는 식으로만 가능하다.
    즉, 컨트롤러가 CPU에게 인터럽트를 발생시키면 CPU는 컨트롤러의 로컬 버퍼와 메모리 사이에서 데이터를 옮겨 주는 일을 하게 된다.
  • 모든 메모리 접근 연산이 CPU에 의해서만 이뤄질 경우에는 CPU의 업무가 방해를 받아 효율성이 떨어지는 문제점이 발생한다.
  • 이러한 비효율성을 극복하기 위해 CPU 이외의 메모리 접근이 가능한 장치를 하나 더 두는 경우가 많은데 이와 같은 장치를 DMA라고 부른다.
  • DMA는 로컬 버퍼에서 메모리로 읽어오는 작업을 진행한 후 CPU에게 인터럽트를 발생시켜 해당 작업의 완료를 알려준다.
  • DMA는 바이트 단위가 아니라 블록이라는 큰 단위로 데이터를 메모리로 읽어 온다.
  • 이런 방식으로 CPU에 발생하는 인터럽트의 빈도를 줄여 CPU의 효율성을 높일 수 있게 된다.

저장 장치의 구조


  • 컴퓨터 시스템을 구성하는 저장 장치는 주 기억 장치보조 기억 장치로 나눌 수 있다.

주 기억 장치

  • 보통 메모리라 부르며 전원이 나가면 저장되었던 내용이 모두 사라져 버리는 휘방성이다.
    Ex)RAM을 매체로 사용하는 경우가 대부분이다.

보조 기억 장치

  • 전원이 나가도 저장된 내용을 기억할 수 있는 비휘발성
    Ex)플래시 메모리, CD, 마그네틱 테이프 등등

보조 기억 장치의 용도는 크게 2가지로 구분된다.

  1. 파일 시스템용

    • 전원 Off시 유지해야 할 정보가 있으면 그것을 파일 형태로 보조 기억 장치에 저장한다.
    • 메모리는 휘발성이므로 비휘발성 매체인 디스크를 파일 시스템용으로 흔히 사용한다.
  2. 메모리의 연장 공간인 스왑 영역용(Swap)

    • OS는 프로그램 수행에 당장 필요한 부분만 메모리에 올려 놓고 나머지는 디스크의 스왑 영역에 내려놓게 된다.
    • 디스크에 내려놓는 일을 스왑 아웃이라고 말한다.
    • 스왑 아웃된 부분이 필요 시 다시 메모리 영역에 올린다.
    • 스왑 영역은 프로그램이 실행 시 내용을 저장했다가 프로그램이 종료될 때 삭제하는 메모리의 연장 공간으로서의 역할을 담당한다.
    • 파일 시스템처럼 비휘발성 용도로 사용되는 것과는 역할이 구분된다.

저장 장치의 계층 구조


  • 주 기억 장치와 보조 기억 장치로 나눌 수 있다.
  • 주 기억 장치 : 비쌈 / 빠름 / 용량이 적음 / 휘발성
  • 보조 기억 장치 : 저렴 / 느림 / 용량이 많은 / 비휘발성

하드웨어의 보안


  • 우리가 흔히 사용하는 OS는 여러 프로그램이 동시에 실행될 수 있는 다중 프로그래밍 환경에서 동작한다. 그러므로 각 프로그램들이 다른 프로그램의 실행을 방해하거나 서로 충돌을 일으키는 문제를 막기 위해 HW에 대한 각종 보안 기법이 필요하다.
  • HW 보안을 유지하기 위해 OS는 기본적으로 두 가지 모드의 오퍼레이션을 지원한다.

커널 모드(Kernel Mode)

  • CPU 내부의 모드 비트가 0인 상태이다.
    OS가 CPU의 제어권을 갖고 OS 코드를 실행하는 모드로서, 모든 종류의 명령을 다 실행할 수 있다.
  • 중요한 정보에 접근해 위험한 상황을 초래할 수 있는 연산은 커널 모드에서만 실행되도록 하여 보안성을 확보한다.
  • 시스템의 보안과 관련된 명령들을 특권 명령이라 하며, 모든 비트가 0일 때만 수행 가능하다. 즉, 특권 명령을 커널 모드에서 OS에 의해서만 수행이 가능하다.

사용자 모드(User Mode)

  • CPU 내부의 모드 비트가 1인 상태이다.
    일반 사용자 프로그램이 실행되며 제한적인 명령만을 수행할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
# 모드 비트 사용 이유
사용자 프로그램이 프로그램 내에서 시스템에 영향을 끼치는 연산을 수행할 수가 있다.
이 때 사용자 프로그램이 CPU를 가지고 있는 동안에는 OS가 CPU를 선점할 수 없으므로 사용자 프로그램을 감시할 수 있는 방법이 없다.
그렇기 때문에 CPU 내부에 모드 비트를 두어 사용자 프로그램을 감시할 수 있게 된다.

# 모드 비트 세팅 과정
OS가 CPU를 점유해 자신의 코드를 수행하다 사용자 프로그램에게 CPU의 제어권을 넘길 때 모드 비트를 1로 세팅해 넘긴다.

사용자 프로그램이 수행되다 HW 접근 등 보안이 필요한 중요한 명령을 수행해야 할 경우에는 시스템 콜을 통해 OS에게 서비스를 대신해 줄 것을 요청하게 된다.

인터럽트가 발생할 때에는 모드 비트가 0으로 세팅되어 OS는 서비스에 필요한 모든 종류의 명령을 수행할 수 있다.

메모리 보안


  • 여러 프로그램이 동시에 메모리에 올라가서 실행되기 때문에 하나의 사용자 프로그램이 다른 사용자 프로그램이나 OS가 위치한 메모리 영역을 침범할 수 있기 때문에 메모리 보안이 필요하다
  • 2개의 레지스터(기준 레지스터, 한계 레지스터)를 사용해서 프로글매이 접근하려는 메모리 부분이 합법적인지 체크하여 메모리 보호가 이루어진다.
  • 기준 레지스터는 어떤 프로그램이 수행되는 동안 합법적으로 접근할 수 있는 메모리 상의 가장 작은 주소를 보관한다.
  • 한계 레지스터는 그 프로그램이 기준 레지스터 값부터 접근할 수 있는 메모리의 범위를 보관한다.
  • 즉 어떤 프로그램이 실제 메모리에 올라가 있는 부분의 시작 주소와 그 프로그램의 길이를 보관해 메모리 접근 연산이 있을 때마다 하드웨어적으로 현재 접근하려는 위치가 합법적인 범위인지를 체크한다.
  • 사용자 프로그램은 기준 레지스터 + 한계 레지스터 값 사이 주소 영역만 접근이 가능하며, 이 범위 안에 없으면 불법적인 메모리 접근이므로 예외 상황이라는 SW 인터럽트를 발생하게 된다.
    예외 상황은 OS에게 SW 인터럽트를 발생시켜 CPU의 제어권을 이양시키고 OS는 그 프로그램을 강제 종료시킨다.

  • 단, 여기에서 기준 + 한계 레지스터를 통한 메모리 보호 기법은 하나의 프로그램이 메모리의 한 영역에 연속적으로 위치하는 경우에 대한 설명이다.
    그 박의 하나의 프로그램이 메모리 여러 영역에 나뉘어 위치하는 페이징 기법도 존재한다.

  • 메모리 접근 연산은 사용자 프로그램이 CPU를 가지고 있는 동안에 수행할 수 있는 연산이므로 특권 명령은 아니다.
    다만 사용자 프로그램이 메모리를 접근하기 전에 하드웨어적으로 그 접근이 합법적인지를 체크하여 메모리를 보호한다.
    이것이 OS만 수행할 수 있는 입출력 연산메모리 접근 연산과의 차이점이라 할 수 있다.

  • 사용자 모드인 경우에는 기준 + 한계 레지스터를 사용해 메모리를 보호하고
    커널 모드에서는 메모리에 무제한으로 접근이 가능하다.

[무슨 말일까…? 책으로 보면서 추후에 더 이해하자.]

CPU 보호


  • 일반적으로 CPU는 컴퓨터 내에 하나밖에 존재하지 않기 때문에 특정 프로그램이 CPU를 독점해 무한 박복문을 수행하는 등 부적절한 방법으로 독점한다면 다른 프로그램 및 OS가 CPU의 제어권을 획득할 수 있는 방법이 없게 된다.
  • OS는 CPU가 하나의 프로그램에 의해 독점되는 것을 막기 위해 타이머라는 HW를 사용한다.
  • 타이머는 정해진 시간이 지나면 인터럽트를 발생시켜 OS에게 CPU의 제어권을 이양시키는 역할응 수행한다.
  • 타이머는 시분할 시스템에서 현재 시간을 계산하기 위해서도 널리 사용된다.
    시분할 시스템이란? 여러 프로그램이 CPU의 시간을 조금씩 나누어 사용하는 시스템을 의미한다.

시스템 콜을 이용한 입출력 수행


  • 시스템 콜은 일종의 SW 인터럽트로 사용자 프로그램이 시스템 콜을 요청할 경우 트랩이 발생해 CPU에 대한 제어권이 OS로 넘어가게 된다.
  • 그러면 OS는 해당 시스템 콜을 처리하기 위한 루틴으로 가서 정의된 명령을 수행하게 된다.

Comment and share

  1. 운영체제의 정의

운영체제의 정의

  • OS는 컴퓨터 HW 바로 윗단에 설치되는 SW 이다.
  • OS는 사용자 및 다른 모든 SW와 HW를 연결하는 SW 계층으로 위상은 아래 그림과 같다.
  • 각종 SW들은 위 그림과 같이 HW와 OS가 한 몸으로 존재하는 컴퓨터 시스템 위에서 수행된다.
  • 컴퓨터 전원을 ON 했을 때 OS가 없으면 컴퓨터는 고철 덩어리에 불과하다.

SW가 컴퓨터 시스템에서 수행되기 위해서는 메모리에 적재되어야 한다. OS도 하나의 SW로서 전원이 켜짐과 동시에 메모리에 적재된다. 하지만, OS처럼 큰 규모가 모두 메모리에 적재된다면 한정된 메모리 공간의 낭비가 심할 것이다. 따라서 OS 중 항상 필요한 부분만 메모리에 적재하고 그렇지 않은 부분은 필요할 때 메모리에 적재 후 사용한다.

이때, 메모리에 상주하는 OS 부분을 커널(Kernel)이라 부르며 이를 좁은 의미의 OS라고도 한다. 즉, 커널은 OS 코드 중에서도 핵심적인 부분을 뜻한다.

운영체제의 기능

  • 위 그림과 같이 OS가 컴퓨터 HW와 사용자 사이에 존재하기 때문에 OS의 역할은 HW에 대한 역할과 사용자에 대한 역할로 크게 나눌 수 있다.
  • HW쪽에서는 사용자가 알기 힘든 각종 HW를 OS가 직접 관리함으로써 사용자에게는 편리한 인터페이스를 제공한다.
  • 보다 자세히 살펴보면 OS의 두가지 주요 기능은 다음과 같다.
  1. 편리한 환경 제공

편리한 환경 제공이란?
OS가 동시 사용자 및 프로그램들에게 각자 독자적으로 컴퓨터를 사용하는 것과 같은 환상을 제공하는 것을 말한다.

  1. 시스템 내 자원을 효율적으로 관리

효율적 자원 관리란?
OS를 자원 관리자라 부르기도 한다. 이때, 자원이란 CPU, 메모리, 하드 디스크 등 HW+SW 자원을 통칭한다. OS는 자원들을 효율적으로 관리해 가장 좋은 성능을 내는 역할을 담당한다. 하지만 전체적인 성능을 향상시키려다 일부 프로그램 또는 사용자가 불이익을 당할 수도 있다. 따라서 형평성 있게 분배되도록 균형자 역할도 함께 수행하는 것이 중요하다.

즉, 효율성이 가장 큰 목표이지만 이로 인해 일부가 지나치게 희생되지 않게 형평성 역시 OS가 고려해야할 목표이다. 추가적으로 OS는 사용자와 OS 자신을 보호하는 역할을 담당한다.

운영체제의 분류

  1. 동시 작업 지원
  2. 다중 사용자의 동시 지원 여부
  3. 작업을 처리하는 방식

1. 동시 작업 지원

동시 작업 지원 X -> 단일 작업용 OS
동시 작업 지원 O -> 다중 작업용 OS

  • 단일 작업용 OS는 한 번에 하나의 프로그램만 수행 가능하다.
  • OS 다중 작업을 처리할 때 여러 프로그램이 CPU와 메모리를 공유한다. 하지만 일반적으로 컴퓨터에는 CPU가 1개 존재한다.
    따라서 다중 작업용 OS라 하더라도 CPU에서는 매 순간 하나의 프로그램만 수행한다. CPU에서 번갈아 수행되지만 CPU의 처리 속도가 워낙 빨라서 여러 프로그램들이 동시에 수행되는 것처럼 보이는 것이다.
    이와 같이 CPU의 작업 시간을 여러 프로그램들이 나누어 쓰는 시스템을 시분할 시스템이라고 한다.
  • CPU와 달리 메모리의 경우에는 여러 프로그램들이 조금씩 메모리 공간을 보유하며 동시에 메모리에 올라갈 수 있다. 이처럼 메모리 공간을 분할해 여러 프로그램들을 동시에 메모리에 올려놓고 처리하는 시스템을 다중 프로그래밍 시스템이라 한다.
  • 다중 작업용 OS의 경우 프로그램을 같이 수행시키지만 사용자 입장에서는 각 프로그램에 대한 키보드 입력의 결과를 바로 화면에 보여주기 때문에 이러한 시스템을 대화형 시스템이라 한다.
  • 여러 사용자가 동시에 접속해서 쓰는 서버의 경우에도 대화형 시스템에 해당된다.

  • 다중 처리기 시스템이란 하나의 컴퓨터에 여러 개의 CPU가 설치된 경우를 뜻한다.
  • CPU가 여럿 있는 컴퓨터는 서로 다른 CPU에서 여러 프로그램이 동시에 수행될 수 있어 처리가 빠르지만 OS 입장에서는 여러 CPU를 관리하기 위해서 더욱 복잡한 메커니즘을 필요로 한다.

2. 다중 사용자의 동시 지원 여부


한 번에 한 명의 사용자만이 사용 -> 단일 사용자용 OS
여러 사용자가 동시에 접속해서 사용 -> 다중 사용자용 OS

흔히 서버라고 부르는 컴퓨터는 다중 사용자용 OS이다.

3. 작업을 처리하는 방식


OS를 분류하는 또 다른 기준은 작업을 처리하는 방식이다.

[1]. 일괄 처리(Batch Processing)

  • 작업 요청의 일정량을 모아서 한꺼번에(일괄로) 처리하는 방식을 뜻한다.

[2]. 시분할 방식(Time Sharing)

  • 여러 작업을 수행할 때 컴퓨터의 처리 능력을 일정한 시간 단위로 분할해 사용하는 것
  • 사용자의 요청에 대한 결과를 즉각적으로 얻을 수 있는 시스템을 대화형 시스템이라 한다.

[3]. 실시간(Real Time)

  • 실시간 OS는 정해진 시간 안에 어떠한 일이 반드시 종료됨이 보장 되어야 하는 시스템이다.
  • 다시 말해, 일정 시간 안에 작업이 완료되지 못할 경우에는 시스템 자체가 동작하지 않거나 큰 위험을 초래할 가능성이 있는 시스템에서 사용한다.
  • 실시간 시스템은 시간 제약의 중요성에 따라 2가지로 세분화가 가능하다.

(1). 경성 실시간 시스템(Hard Realtime System)

  • 주어진 시간을 지키지 못할 경우 매우 위험한 결과를 초래할 가능성이 있는 시스템을 말한다.
    ex)로켓, 원자로, 제어 시스템 등

(2). 연성 실시간 시스템(Soft Realtime System)

  • 멀티미디어 스트리밍 시스템과 같이 데이터가 시간을 맞추어 전달되어야 올바른 기능을 수행할 수 있는 시스템
    ex) 동영상 재생이 끊기거나 정확히 전달되지 않을 경우

경성 실시간 시스템처럼 위험한 결과를 초래하지 않는다.

운영체제의 자원 관리 기능


  • OS의 가장 핵심적인 기능은 뭐다? 자원을 효율적으로 관리하는 것이다.
    자원 = HW 자원 + SW 자원
  • HW 자원 = CPU와 메모리를 비롯해 주변 장치 및 입출력 장치 등을 의미한다.
  • 여러 프로세스들이 CPU를 효율적으로 나누어 사용할 수 있도록 관리되어야 한다.
  • 메모리 역시 시스템 내에 한정된 공간이 존재하기 때문에 서로 다른 다수의 프로세스들이 메모리를 나누어 쓸 수 있도록 해야 한다.
  • 또한 주변 장치도 OS의 관리가 필요하다.
  • CPU와 메모리는 전원 Off 시 데이터가 모두 지워지기 때문에 저장되어야 할 데이터는 보조 기억 장치파일 형태로 저장된다.
    이러한 파일들이 저장되는 방식 및 접근 권한 등에 대해서도 OS가 관리한다.
  • CPU를 관리하는 방법

    • 일반적인 컴퓨터에는 CPU가 하나밖에 없지만 프로세스는 여러 개가 동시에 실행된다.
    • 그러므로 매 시점 어떠한 프로세스에게 CPU를 할당해 작업을 처리할 것인지를 결정하는 일이 필요하다. 이러한 일을 CPU 스케줄링이라고 한다.
  • 메모리를 관리하는 방법

    • 메모리는 CPU가 직접 접근할 수 있는 컴퓨터 내부의 기억 장치이다.
    • 프로그램이 CPU에서 수행되려면 해당 부분이 메모리에 적재되어야 한다.
    • 한정된 메모리 공간에 여러 프로그램을 수용하려면 메모리에 대한 효율적인 관리 메커니즘이 필요하다.
    • 메모리를 관리하기 위해 OS는 메모리의 어느 부분이 어떤 프로그램에 의해 사용되고 있는지를 유지하는 데 이러한 정보는 Address(주소)를 통해 관리된다.
    • OS는 프로그램에게 메모리가 필요할 때 할당 해주고, 더 이상 필요하지 않을 때 회수한다.
  • 물리적 메모리를 관리하는 방식

[1]. 고정 분할 방식 : 물리적 메모리를 몇 개의 영구적인 분할로 나눈다.
- 내부 단편화 발생[내부 단편화가 뭐지…?]
- 물리적 메모리보다 큰 프로그램을 실행하지 못한다.

[2]. 가변 분할 방식 : 매 시점 프로그램의 크기에 맞게 메모리를 분할해서 사용하는 방식
- 외부 단편화 발생[이건 또 뭐니…?]
- 물리적 메모리보다 큰 프로그램을 실행하지 못한다.

[3]. 가상 메모리 방식 : 최근 거의 모든 컴퓨터 시스템에서 사용하는 메모리 관리 기법이다.
- 물리적 메모리보다 큰 프로그램을 실행할 수 있다.
- 모든 프로그램은 물리적 메모리와는 독립적으로 주소가 0부터 시작하는 자신만의 가상 메모리를 갖게 된다.
- OS는 가상 메모리의 주소를 물리적 메모리 주소로 매핑하는 기술을 이용해 주소를 변환시킨 후 프로그램을 물리적 메모리에 적재한다.
- 가상 메모리 기법에서는 물리적 메모리의 크기와 상관없이 사용할 수 있는 메모리의 크기를 충분히 크다고 가정하고 프로그래밍하는 것이 가능하다.
- 가능하게 하는 원리는 다음과 같다.

프로그램의 전체가 항상 동시에 사용되는 것은 아니다.

그러므로 현재 사용되고 있는 부분만 메모리에 적재 후, 나머지(현재 사용되고 있지 않은 부분)는 하드디스크와 같은 보조 기억 장치에 저장해 두었다가 필요할 때 메모리에 적재하는 방식을 취한다.

이 때 사용되는 보조 기억 장치의 영역을 우리는 스왑 영역(Swap Area)이라 부른다.

프로그램을 구성하는 주소 공간은 페이지라는 동일한 크기의 작은 단위로 나뉘어 물리적 메모리와 스왑 영역에 일부씩 저장된다. 이렇게 동일한 단위로 메모리를 나누는 기법을 페이징(Paging) 기법이라 한다.

[페이지와 페이징이 뭐야…?]

  • 주변 장치 및 입출력 장치는 CPU나 메모리와 달리 인터럽트(Interrupt)라는 메커니즘을 통해 관리.
  • 주변 장치들은 CPU의 서비스가 필요한 경우에 신호를 발생시켜 서비스를 요청하는데 이 때 발생시키는 신호를 인터럽트라고 한다.
  • CPU는 평소에 CPU 스케줄링에 따라 자신에게 주어진 작업을 수행하고 있다가 인터럽트가 발생하면 하던 일을 잠시 멈추고 인터럽트에 의한 요청 서비스를 수행한다.
  • 인터럽트는 요청하는 장치와 발생 상황에 따라 다양한 종류가 있기 때문에 OS는 인터럽트 종류마다 서로 다른 인터럽트 처리 루틴을 갖는다.

인터럽트 처리 루틴이란?
인터럽트가 발생했을 때 해주어야 할 작업을 정의한 프로그램 코드를 말한다.

이것은 OS 커널 내에 존재하는 코드로 CPU 스케줄링, 메모리 관리 루틴 등 다양한 기능을 위한 커널 코드 중 일부분이라 할 수 있다.

  • 인터럽트 발생시키는 예는 책 참고(63~65p)

Comment and share

  • page 1 of 1
Author's picture

VictoryWoo

기록을 통해 사람들과 공유하는 것을 좋아합니다.


Android Developer