운영체제: 1. 컴퓨터 시스템 구조

B급 코더·2021년 12월 28일
0

1. 운영체제와 커널

현대 운영체제는 매우 복잡하다. 하지만 시대가 흘러도 그 역할은 바뀌지 않는다.
운영체제는 크게 2가지의 기능이 있다.

  • 컴퓨터 시스템 내부의 자원을 효율적으로 관리
  • 컴퓨터 시스템을 편리하게 이용하기 위한 인터페이스 제공

자원 관리자로서의 기능과 사용자를 위해 친절하게 인터페이스까지 제공한다.
여기서 '자원'은 컴퓨터 시스템을 구성하는 모든 요소를 뜻하며, CPU, 메모리, 입출력 장치 등 모든
하드웨어와 소프트웨어 자원을 의미한다.

그렇다면 운영체제는 어디에 위치해 있길래 모든 시스템 자원을 관리하고 제어하는 것인가?
사실 운영체제도 디스크에 저장되어 있다가 실행할 시기가 오면, 메모리에 올라와서
부팅을 시작한다. 운영체제는 우선순위가 높을 뿐 하나의 소프트웨어이기 때문이다. 누군가 수 만 line의
C언어 코드로 하드웨어 & 소프트웨어 자원에 접근하여 그것을 관리할 수 있는 기능을 만들어 둔 것이다.

보통 하드웨어 윗단에 위치해 있다고 많이들 말하지만, 엄밀히 따지면 디스크에 저장되어 있다가
Power가 ON으로 바뀌면 메모리에 올라와서 실행되어 사용자 인터페이스를 제공해준다.
하지만 운영체제의 용량은 매우 크다. 큰 덩어리 한 놈이 메모리에 모두 올라와 실행하기에는 매우 비효율적이다.
그렇다면 어떻게 해야하는가?
정말 필요한 것들만 메모리에 올리면 된다.
우리가 비행기에 탈때도 정말 필요한 짐만 좌석에 휴대할 수 있지, 나머지 무거운 짐들은 화물수송을 통해
현지에서 받아볼 수 있듯이 말이다.
이렇듯 메모리에 올라간 운영체제의 특정 '부분'을 '커널'이라고 부른다.

커널은 아래와 같은 기능을 수행한다.

  • 프로세스 관리
  • 스케쥴링
  • 메모리 관리
  • 입출력 관리
  • 네트워크 * 파일 시스템 관리

2. 시스템 콜
1장에서 운영체제와 커널에 대해서 알아보았다. 설명 했듯이, 커널은 시스템 자원을 관리하는 기능을 수행한다.
이번 장에서는 입출력 관리를 중점적으로 보겠다. 입출력은 우리가 키보드에 무언가를 입력했을 때,
마우스로 무언가를 클릭했을 때, 파일을 열때 등등 대부분의 사용자의 명령 상황에서 발생한다.
그런데 cpu가 어떤 작업을 수행하다가 다른 장치에서 무언가를 가져와야하는 상황에선 우리의 커널이 무슨 일을 하는 것인가?
우리가 사용하는 대부분의 프로그램은 외부의 특정 자원을 불러와서 작업을 처리해야하는 상황들이 많이 발생한다.
예를 들어, 한글 프로그램이 실행되다가 내가 예전에 작성했던 문서를 불러오고 싶다면?
혹은 파이썬 코드가 수행되는 와중에 pandas의 read_csv() 함수를 만난다면?

이럴 때, 우리는 '운영체제에게 하드웨어 자원에 접근해서 무언갈 가져와줘!' 라고 말해줘야한다.
이때 명령어로 커널을 불러오는 상황을 '시스템 콜' 이라고 하며,
명령어는 '시스템 콜 인터페이스' 라고 부른다.
그런데 시스템 콜을 왜 사용해야하는지 의문을 가질 수 있다.
운영체제는 자원관리 외에 보안기능도 수행하고 있다. 허가 받지 않은 악의적 사용자가 마음대로
하드웨어 자원에 접근하여 시스템에 악영향을 줄 수 있기 때문이다. 따라서 허락받은 사용자만 하드웨어
자원에 접근할 수 있도록 막아둔다.

즉, 커널은 '우리에게 접근하기 위한 인터페이스를 알고 있는 녀석만 들어올 수 있어!'
라고 못박아둔 셈이다. 그리고 커널은 시스템 콜이 발생하면, CPU를
커널 모드 전환하여 자신들의 작업을 수행한다. 이때 CPU의 Mode bit이 0이면 운영체제가 작업하고 있음을 뜻하고,
그 외의 번호는 사용자 프로그램이 작업을 수행하고 있음을 뜻한다.


프로세스 A가 디스크로부터 파일을 읽어오는 명령을 실행할 때 일어나는 과정을 예로 들어보자.

  1. 시스템 콜이 발생하면, 인터럽트 라인을 세팅한다.
  2. 실행 중이던 명령어를 마치고, 인터럽트 라인을 통해 인터럽트가 걸렸음을 인지.
  3. mode bit을 0으로 바꾸고, OS에게 제어권 이양
  4. 현재 실행 중이던 프로세스의 상태 정보를 PCB에 저장한다. 그리고 Program counter에는
    레지스터에 기록된 다음에 실행할 명령어 주소를 저장한다.
  5. 시스템 콜 루틴에 해당하는 곳으로 이동하여, 시스템 콜 테이블을 참조하여 파일 읽기에 해당하는
    시스템 콜을 실행한다.
  6. 해당 루틴을 끝내면, mode bit을 1로 바꾸고 PCB에 저장했던 상태들과 PC를 복원시킴.
  7. PC에 저장된 주소로 점프하여 계속 실행

3. 인터럽트란 무엇인가?

2장 마지막에 기술된 과정을 살펴보면 '인터럽트'라는 용어가 자주 등장해서 당황했을 것이다.
인터럽트는 외부장치(디스크, 입출력 장치)가 cpu에게 작업을 완료했다고 알려주는 신호이다.
즉 시스템 콜로 하드웨어 컨트롤러에게 시킨 작업이 완료되었음을 알리는 신호이다. 이 신호는
외부장치에 탑재된 하드웨어 컨트롤러가 발생시키는 신호이다. 물론 인터럽트는 꼭 외부장치만 발생시킬 수
있는 것은 아니다. 하드웨어 뿐만 아니라 소프트웨어가 프로세스 상태로 무언가를 실행하다가
인터럽트를 걸어버릴 수도 있고, 타이머가 발생시킬 수도 있다.

*내부 인터럽트(소프트웨어 인터럽트)

  1. 하드웨어 고장: 컴퓨터 고장, 데이터 전달 과정에서의 비트 오류, 전원이 나간 경우
  2. 실행할 수 없는 명령어: 명령어를 cpu가 해석할 수 없을 때. 즉 cpu에 비트 패턴이 정의되어있지 않은 경우
  3. 명령어 실행 오류: 나누기 0을 하거나, 기타 예외가 발생할 경우
  4. 사용 권한 위배: 사용자가 운영체제만 사용할 수 있는 자원에 접근하는 경우

*외부 인터럽트(하드웨어 인터럽트)

  1. 타이머 인터럽트: 프로세스에게 cpu 작업시간이 끝났음을 알리는 신호
  2. 입출력 인터럽트: 입출력 컨트롤러가 준비가 완료되었음을 cpu에게 알리는 신호

*인터럽트 처리 과정


모든 명령어는 인출과 실행이라는 두 가지 과정을 반복해서 수행된다. 이때 인터럽트 요청이 들어오면,
특정 명령어의 실행이 모두 끝난 후에 cpu는 인터럽트 라인을 확인하고 들어온 인터럽트를 처리한다.
처리 전에 쫓겨난 프로세스는 자신의 고유 영역인 상태 레지스터(Status Register)와 프로그램 카운터(Program Counter)에
어디까지 실행되었고 현재 상태는 어떤지 저장하고 인터럽트를 처리하기 시작한다.
이때 인터럽트 백터에 접근하여 필요한 서비스가 몇번째에 위치해 있는지 확인하고, 발견했다면 인터럽트 서비스 루틴에 맞게 작업을 수행한다.
인터럽트가 완료되면, 쫓겨났던 프로세스는 다시 CPU에 올라와 상태 레지스터와 PC에 저장된 값을 복원하여
계속 작업을 수행한다.

위 사진은 인터럽트 서비스 루틴 정보가 담긴 인터럽트 백터 예시이다.

4. 동기/비동기 입출력

  • 동기 입출력
    순서를 보장하는 입출력 시스템을 뜻한다. A가 B보다 먼저 입출력 요청을 했다면, A가 먼저
    입출력 작업을 수행할 수 있고, 그 다음 B가 수행된다. A가 CPU를 선점하는 동안, 시스템 콜이 발생해서
    입출력 작업을 수행할 때, A는 CPU를 빼앗기고 봉쇄(Blocked)상태로 전환한다. 그 후 B 역시
    시스템 콜이 발생하면 봉쇄상태로 전환하게 된다. 그리고 디바이스에 있는 버퍼(큐 자료구조)
    에 AB 순서대로 Task가 적재된다. 이후 인터럽트를 걸면, CPU는 AB순으로 작업을 처리된다.
    이 방식은 순서보장이 필요한 프로그램에 안정성을 더할 수 있다. 또한 프로그램 처리 순서가 매우
    직관적이며, 구현하기 쉽다. 하지만 CPU를 점유하는 프로세스가 너무 자주 바뀌어서 오버헤드가 커질 수 있다.
    [오버헤드] -> 어떤 처리를 하기 위해 들어가는 간접적인 처리시간, 메모리 사용량을 뜻함

  • 비동기 입출력
    정해진 순서와 상관없이 처리가 가능한 작업들을 먼저 수행하고, 인터럽트가 걸리면 입출력과 관련된 명령어를 처리한다.
    비동기 방식은 CPU를 아주 잠시 빼앗길 뿐, 연속적으로 작업을 처리할 수 있다.
    I/O가 발생하면, 프로세스 내에서 입출력과 관련없는 명령어를 먼저 처리하고, 입출력과 관련된 명령어는 나중에 처리한다.
    이렇게 하면, CPU를 점유하는 프로세스가 자주 바뀌지 않고, 오버헤드도 작아진다.
    히지만, 순서 보장이 필요한 프로그램이 정해진 절차대로 처리되지 못할 가능성이 존재한다.
    이러한 점 때문에, 현대 운영체제는 동기식 입출력 방식을 주로 사용한다.

5. DMA


DMA는 구르마다. 입출력 장치의 데이터를 CPU대신 메모리에 실어주는 역할을 한다.
CPU는 매우 바쁘다.
운영체제라는 악덕사장을 잘못 만나서, 자질구레한 일을 대신 처리해 줄 친구가 필요하다.
그 중 인터럽트 처리는 CPU에겐 매우 귀찮은 작업일 것이다. 매번 인터럽트 라인을 확인해야하고,
다른 중요한 일들이 많은데, 이놈들부터 처리해야 하기 때문이다. 그래서 인터럽트 일부를
대신 처리해줄 수 있는 읽기/쓰기 장치가 생겨났다. 바로 DMA 이다.

DMA는 주변장치로 부터 받아온 데이터를 메모리에 올려주는 역할을 수행한다. 이때 CPU는
DMA가 처리하는 일에 관여하지 않는다. 그저 DMA는 CPU를 대신해서 데이터를 받아와서
메모리에 실어준 후, CPU에 '야 나 다했다!' 라고 인터럽트를 걸어주면 된다. CPU 입장에선
내 일을 대신해줄 친구가 일도 잘하고, 즉각적으로 보고도 잘해주니까 이전보다 편하다고 생각할 것이다.

6. CPU 제어권이 운영체제에게 넘어가는 시점은 언제인가?

CPU의 점유권은 아래의 3가지 상황에서 운영체제에게 넘어가게 된다.

1. 인터럽트 발생 시

-> 하드웨어 장치들이 인터럽트를 요청할 때
-> timer가 정해진 시간이 지난 후에 CPU에게 제어권을 넘길 때

인터럽트를 처리하는 방법은 커널에 코드로 작성되어있다. [3장 인터럽트 처리루틴 내용 참고할 것]
그 코드를 CPU위에서 실행을 해서,
하드웨어 컨트롤러에 접근해서 데이터를 받아오는 작업을 수행하는 것이다.
즉 CPU에 올라간 커널의 코드가 실행되면 그 작업을 수행하는 것이다.

2. 시스템 콜 발생 시

시스템 콜이 발생하면, 커널모드로 바뀌면서(mode bit change to 0) 운영체제가 CPU를 점유하게 된다.

3. Exception 발생 시

profile
성장을 꿈꾸는 B급 코더입니다. 훈수는 저를 성장시킵니다.

0개의 댓글

관련 채용 정보