Fascination
article thumbnail

Chapter 02. 들어가기 전에

누워서 보는 운영체제 이야기 - 김주균 교수님


# 2.1 OS의 목적

- 운영체제

  • 사용자와 컴퓨터 사이의 가교 역할을 한다고 하였는데, 이것은 사용자가 컴퓨터를 보다 편리하게 사용할 수 있도록 해 주어야 한다는 것을 의미
  • 하드웨어가 가지는 능력을 십분 발휘 되도록 제어한다고 하였는데, 이것은 컴퓨터 시스템의 자원들을 효율적으로 사용될 수 있게 해야 한다는 말. 즉, 사용자의 편리성과 자원의 효율적 사용이 그 목적임
  • 효과적인 점검과 개발이 가능하고, 새로운 기능들이 추가될 수 있도록 만들어져서 사용자에게 보다 나은 서비스를 제공할 수 있도록 만들어져야 할 것
  • 다른 각도에서 더 설명하자면 운영체제를 사용하는 사람들의 입장에서 보면 사용하기에는 쉽고 편리하며 배우기에는 쉽고 신뢰할 수 있으면서 빠른 것. 만드는 사람의 입장에서는 운영체제 설계, 유지, 보수가 쉽고 적응성이 좋으며 오류 없이 효율적이어야 한다는 점

 

 

# 2.2 몇 가지 상식

1) 부팅

  • 전원 버튼이 눌러져 커널이라고 불리는 운영체제의 일부가 메모리에 올라와 실행되어 장치들을 준비시키고, 각종 레지스터 값을 초기화하고 나서 사용자의 입력을 받을 준비를 마치는 단계
  • 부트 프로그램 또는 부츠트랩 로더(Bootstrap Loader)라는 프로그램이 대게 ROM에 저장되어 있어 전원이 켜지면 제일 먼저 실행되도록 하는데, 이것의 역할은 커널을 찾아 메모리에 올린 후 커널을 실행시켜 주는 것
  • PC와 같은 작은 시스템에서는 ROM에 있는 부츠트랩 로더는 더 단순한 기능만을 가지게 하고, 커널을 메모리에 올려줄 부트 프로그램은 따로 디스크를 가지고 있어서 부츠트랩 로더가 먼저 부트 프로그램을 메모리에 올려 실행시키면 부트 프로그램이 커널을 올려 실행시켜주는 방식을 취함

 

2) 레지스터(Register)

  • 메모리보다 빠른 기억 장치이지만 크기가 작아서 시스템과 사용 목적에 따라 8비트, 16비트, 32비트 등의 크기를 가짐
  • 데이터, 주소, 조건 코드 레지스터: 데이터, 주소, 조건 코드 등을 저장

최상위 관점에서의 컴퓨터 구성 요소

  • PSW(Program Status Word) 레지스터: CPU의 현재 상태 정보를 저장(조건 코드, 인터럽트 가능/불가능, 현재 실행 모드 등)
  • 인터럽트: 1bit로 표현되며  예를 들어 1이면 가능 0이면 불가능이라고 정의하여 사용
  • 현재 실행 모드: 유저모드 or 커널모드

 

3) 명령어 처리: instruction cycle

  • 명령어(instruction): assembler level
  • 제일 먼저 명령어를 실행하면 fetch → decode(어떤 명령어인지 분석) → execute(실행) → 인터럽트가 있는지 확인
  • 한 프로그램의 실행이 끝나고 인터럽트가 있는지 확인하는 것
  • 인터럽트 허용: 인터럽트가 있으므로 확인하고 처리
  • 인터럽트 차단: 인터럽트가 없으므로 저장할 게 있다면 저장하고 다시 앞으로 돌아감

 

 

# 2.3 인터럽트 (Interrupt)

▶ 컴퓨터 시스템에서 존재하는 각 자원(장치)들의 현 상태를 파악하는 방법 - 폴링, 인터럽트

▶ 폴링(Polling)

  • CPU가 일정한 시간 간격을 두고 각 자원들의 상태를 주기적으로 확인하는 방식
  • 단점 1. 자원들은 폴링 신호를 받은 후 자신의 상태나 원하는 바를 CPU에게 알려주게 될 텐데, 이 경우의 문제는 폴링의 간격을 적절히 정해야 하는 문제와 각 자원들은 직전 폴링 이후 변화된 자신의 상태를 다음번 폴링 때까지는 알 수 없다는 점
  • 단점 2. 아무 일이 없었는데도 CPU는 폴링에 일정량의 시간을 들여야 하는 부담 발생
  • ex) 교수님께서 강의하실 때 10분마다 수강생 한 명 한 명에게 질문 있니?라고 물어보시는 경우

인터럽트 (Interrrupt)

  • 각 자원들이 능동적으로 자신의 상태 변화를 CPU에게 알리는 방식
  • CPU는 따로 시간을 들이지 않아도 되고, 자원들은 상황이 발생하면 즉시 알려 처리할 수 있으니 폴링보다 훌륭한 방식
  • 오늘날 거의 대부분의 시스템에서 채택하여 사용
  • ex) 수업 중 질문 있는 학생이 손을 들고 질문하는 것

▶ 인터럽트의 종류

  • 하드웨어 인터럽트: 하드웨어 자원 즉, 장치 또는 주변 장치들로부터의 인터럽트
  • 소프트웨어 인터럽트: CPU 스스로 자신에게 인터럽트를 해야 할 때가 있는데, 이것은 실행 중인 명령어 때문에 생기는 일이므로 소프트웨어 인터럽트라고 함 ex) 명령어 오류(0으로 나눔, 다른 사용자 주소 참조 등), 시스템 호출
  • 트랩: CPU 스스로 자신에게 인터럽트 하는 것 

 

1) 인터럽트의 처리 시기

  • 명령어 실행을 마친 후
  • 트랩은 실행 중에 처리됨

 

2)  인터럽트 처리 과정

  • 장치가 인터럽트 신호를 CPU에게 보냄
  • CPU는 실행 중인 명령어를 완료시키고 인터럽트 신호를 확인
  • 현재 수행 중인 프로그램의 상태 정보를 시스템 스택에 저장 (상태 정보란 PSW, PC의 내용) → 인터럽트를 처리하는 것도 OS안의 프로그램인데, 이 프로그램이 실행되면 시스템의 각종 레지스터들을 사용해서 실행이 될 것임. 그전에 레지스터 안에 있던 내용들이 다 없어지게 되는데 인터럽트가 끝난 후 이전 내용에 이어서 실행해야 하므로 인터럽트 당한 시점의 내용을 저장해야 하는 것
  • PC에 인터럽트 처리 루틴의 시작 주소를 넣음으로써 처리 루틴이 실행됨
  • 처리 루틴은 먼저 CPU에 있는 레지스터들의 값을 저장한 후 필요한 처리 루틴 실행
  • 인터럽트 처리가 끝나면 이전에 저장해 둔 레지스터들의 값을 다시 레지스터로 원위치 시킴
  • PSW와 PC의 저장된 값을 원래대로 재저장
  • 인터럽트가 걸린 다음 명령어부터 실행 재개 

인터럽트 처리 과정

▶ (a)

  • (a)에서 처리기 내의 각 레지스터 값들은 인터럽트 직전의 상황을 나타냄
  • PC 값이 N+1인 것은 현재 실행 중인 명령어가 N번지에 있던 것
  • 스택 포인터의 값 T의 메모리 위치를 보면 제어 스택(Control Stack)의 맨 아래(Bottom)이고 저장되어 있는 것이 아무것도 없었다는 것을 알 수 있음
  • 인터럽트의 처리 과정에서 이러한 레지스터 값들을 저장해야 하므로 그림처럼 범용 레지스터가 4개였다고 가정한다면 그것들과 PC에 있는 값을 제어 스택에 저장해야 함
  • 제어 스택에 정보를 저장하고 스택 포인터 값을 T-5로 바꾼 다음, PC에는 서비스 루틴의 시작 번지인 Y를 넣어주면 Y번지의 명령어가 실행되는 즉, 서비스 루틴이 실행됨
  • 처리기 내의 범용 레지스터들은 이전 값들을 이미 제어 스택에 저장해 두었으므로 지금부터 처리기 루틴에 의해 얼마든지 사용 가능

▶ (b)

  • 서비스 루틴의 마지막 명령어를 실행하게 되면 (b)와 같이 사용자 프로그램이 다시 실행될 수 있는 환경을 만들어야 함
  • 스택 포인터의 값(T-5)으로 스택에 보관해 두었던 값들을 찾아 범용 및 PC 레지스터에 복구하고 스택 포인터 값은 빠진 만큼 다시 T로 조정됨
  • PC에 바뀌어 들어간 값인 N+1번지의 명령어를 실행하면 자연스럽게 사용자 프로그램의 실행이 계속될 수 있는 것

 

+) 문맥 교환(context switching)

  • 현재까지 하던 일에서 잠시 다른 일을 해야 할 때, 작업대가 같은 곳이어야 한다면 우리는 현재까지 모양이나 내용을 그대로 가까운 어딘가에 보관했다가 나중에 보관되었던 상태대로 일을 이어갈 것
  • 여기서 작업대가 CPU라면 그때 하고 있던 일의 상태가 문맥이 되고 이것은 상태 정보를 포함한 처리기 레지스터들의 값들이 될 것
  • 프로세스 상태 블록(Process Control Block)의 전부 또는 일부분을 문맥이라고 보면 됨

 

3) 중첩된 인터럽트 처리

다중 인터럽트의 처리

  • 중첩되는 인터럽트의 처리는 두 가지 방식이 있음
  • 순차처리: 인터럽트를 처리하는 동안에 발생하는 인터럽트는 현재 처리가 끝난 뒤 바로 처리하는 방식이며 차례대로 하나씩 해 준다는 의미. 인터럽트끼리의 중요도(우선순위)가 같을 때 사용
  • 중첩처리: 중첩이 가능하도록 현재 처리 중인 인터럽트를 잠시 접어두고 또 다른 인터럽트로 실행을 옮길 수 있도록 하는 방식. 인터럽트끼리의 중요도(우선순위)가 다를 때 사용
  • 중첩처리의 경우 순차처리에 비해 제어스택의 사용량이 증가함 → 순차처리는 재저장 후 다음 인터럽트를 실행하기 때문에 제어 스택에서 이전 인터럽트를 위해 쓰였던 공간을 비우고 다음 인터럽트를 위해 저장할 내용을 새로 저장하지만 중첩 처리는 공간을 비우지 않기 /때문에 더 높은 우선순위의 인터럽트를 위한 공간이 추가적으로 필요함

 

 

# 2.4 기억장치의 계층적 구조(Stroage Hierarchy)

  • 방대한 양의 프로그램들과 데이터들을 저장하기 위해 용량은 커야 하고, 동시에 처리 속도를 높이기 위해서는 빠른 저장장치가 요구됨
  • 따라서 요구되는 상황에 맞춰 적절히 저장 장치를 계층적으로 구성하는 타협이 필요하여 위와 같은 그림이 구성됨
  • 그림에서 상위 계층에 있을수록 속도와 가격은 올라가고 하위 계층으로 내려갈수록 용량은 커지게 됨
  • CPU에 의해 처리될 명령어나 데이터가 위에 있을수록 시스템의 성능은 좋아짐 → CPU에 의한 저장 장치로의 접근 횟수가 계층의 아래에 있을수록 적은 것이 더 좋다는 것
  • 계층 사이의 화살표는 프로그램 또는 데이터가 이동됨을 나타내는데 평소에는 하위 계청에 저장되다가 실행 시에 적당량이 상위 계층과 하위 계층으로 번갈아 교체되면서, CPU는 최대한 상위 계층으로 접근하도록 만들어 처리 속도를 높임
  • CPU에 의해 실행되기 위해서는 반드시 주기억 장치에 있어야 함
  • 메모리에 있는 프로그램의 일부분이 다시 캐시로, 그중에서 실행될 명령어는 처리기 레지스터에 적재되어 실행 과정을 밟음
  • 메모리 관리를 어떻게 하느냐에 따라 시스템의 성능이 좌우됨
  • 계층적 구조의 설계는 계층 간에 데이터가 이동되는 데 드는 부담(저장 장치 사이의 입출력에 드는 비용)을 동반하게 되지만 상위 계층의 접근을 통해 얻게 되는 처리 시간의 절약이 이 부담을 보상할 수 있다면 매력적인 방법

 

 

# 2.5 I/O 방식

▶ 입출력 장치에는 컨트롤러와 입출력 데이터를 저장할 버퍼가 있음

* 버퍼는 컨트롤러 안에 있고 컨트롤러는 입출력 장치의 보드에 부착되어 있음

▶ CPU의 개입 정도에 따라 세 가지로 분류 가능

  1. 프로그램에 의한 입출력 (Programmed I/O)
  2. 인터럽트에 의한 입출력 (Interrrupt-driven I/O)
  3. 메모리에 직접 접근하는 입출력 (Direct Memory Access, DMA)

프로그램에 의한 입출력 (Programmed I/O)

  • CPU는 입력을 지시한 후, 각 워드(버퍼의 크기)가 컨트롤러의 버퍼에 입력됐는지를 계속해서 확인하는 방식
  • 단점: 인터럽트라는 수단이 필요 없는 대신 CPU가 지속적으로 완료 여부를 확인해야 함 → CPU 사용의 낭비
  • 장점: 지속적으로 확인하는 방식이기 때문에 그 I/O가 완료되면, 완료 시그널을 가지고 바로 그다음을 이어갈 수 있음
  • if I/O 완료 시 Next, else 실패 시에 Loop

인터럽트에 의한 입출력 (Interrupt-driven I/O)

  • 입력을 지시한 후, 한 워드의 입력이 이루어지는 사이에 CPU는 다른 작업에 활용되어지며, 입력의 완료 시에 인터럽트를 통해 CPU에 알리는 방식
  • 인터럽트 발생 횟수: 전체 입출력할 data양 / buffer의 크기
  • 장점: CPU의 낭비를 없앨 수 있음
  • 단점: I/O가 완료되어도 바로 다음을 진행하지 못함. 해당 인터럽트를 통해 완료임을 확인하고, 그 인터럽트를 처리한 후에야 실제로 I/O 다음 과정을 이어서 해 나갈 수 있음

메모리에 직접 접근하는 입출력 (Direct Memory Access, DMA)

  • 인터럽트에 의한 입출력의 진화 version이라고 생각하면 좋음
  • DMA를 위해서는 입출력 작업을 CPU 대신해 줄 수 있는 채널(Channel)이라는 위성 프로세서(Staatellite Processor)가 필요
  • 시스템에서 한 번이 입출력 단위를 블록(Block)이라고 부르는데 채널은 블록 단위로 CPU에게 인터럽트를 보내 처리 → 블록은 buffer보다 크기가 큼
  • CPU는 입출력할 데이터의 시작 주소와 크기 등을 채널에게 알려주고 다른 작업에 동원되며, 이때부터 입출력은 채널의 주도하에 이루어짐 → 따라서 채널을 사용하면 인터럽트 횟수는 줄어들고 CPU는 I/O의 부담으로부터 벗어남
  • CPU는 프로그램 실행을 위해서, 채널은 입출력을 위해서 각자가 메모리를 접근하게 될 경우, 똑같은 시간에 CPU와 채널이 메모리의 접근 요구를 요청한다면 → 일반적으로 속도가 빠른 CPU가 평소 메모리 접근 기회를 많이 가지기 때문에 이때 만이라도 채널에게 기회를 주어 원활한 입출력이 이루어질 수 있도록 함. 이때 채널이 CPU의 메모리 접근 사이클을 훔친다고 하여 Cycle Stealing이라 부름(채널이 직접 메모리를 액세스 함으로써 Memory Cycle Stealing)

▶ 하드웨어 구성에 따른 분류

  1. 독립적인 입출력 (Isolated I/O)
  2. 메모리 주소 지정 입출력 (Memory-mapped I/O)

독립적인 입출력 (Isolated I/O)

  • 입출력 장치들이 입출력 버스(I/O Bus)를 통해 CPU와 연결되어 있는 경우
  • 입출력은 입출력을 담당하는 명령어를 통해 실행
  • 본래 instruction set 안에 입/출력용 명령어가 따로 있기 때문에 instruction set이 커짐
  • 입출력 버스를 통해 해당 장치의 지정, 데이터의 전송, 입출력을 구분해 주는 제어(Control) 값이 전달

메모리 주소지정 입출력 (Memory-mapped I/O)

  • 입출력 장치들이 메모리와 함께 메모리 버스에 연결되어 있으며, 입출력을 위한 명령어를 따로 두어 사용하지 않고 메모리에 대한 명령어(MOVE, LOAD 등)를 사용하여 실제 입출력을 하게 되는 방식
  • 추가의 I/O 명령어는 따로 필요 없으므로 instruction set은 커지지 않음
  • I/O device에 매핑되어 있는 만큼은 실제 메모리 공간으로 사용되지 못하는 단점이 있음

 

profile

Fascination

@euna-319

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!