운영체제: 2강 - 프로그램의 실행

늘보·2021년 7월 6일
1

OS

목록 보기
1/25

→ Open in Slid

  • 본 글은 이전에 사용하던 영상 강의 필기 앱인 'Slid'에서 필기 했던 내용을 Velog로 옮긴 내용입니다.
  • 본 글은 이화여대 반효경 교수님 2017학년도 1학기 운영체제 강의를 기반으로 작성되었습니다.
  • 강의 링크 : http://www.kocw.net/home/search/kemView.do?kemId=1226304

I/O의 실행

*동기식 입출력(synchronous)과 비동기식(asynchornous) 입출력

 지난 강에서도 강조했지만, I/O는 OS의 kernel을 통해서만 가능하다. 따라서 사용자 프로그램이 I/O를 하기 위해서는 인터럽트를 발생시켜 CPU 제어권을 해당 사용자 프로그램으로부터 OS로 넘겨주어야 한다는 것을 인지하자.

 동기식 입출력과 비동기식 입출력을 구분하는 것은 간단하다. 동기식이란 말 그대로 입출력 작업이 끝날 때까지 기다리면서 프로그램을 진행하는 것이고, 비동기식이란 I/O device로 하여금 입출력 작업을 하도록 하고 CPU의 제어권은 다시 사용자 프로그램으로 넘어간다.

운영체제 2강 (프로그램의 실행) image

 위 사진을 참고해서 이해해보자. 어떠한 사용자 프로그램이 I/O 요청을 보내면, 해당 작업에 맞는 I/O device driver를 거쳐서 실제 하드웨어를 통해 입출력 작업을 실행한다. 동기식이든 비동기식이든, I/O가 완료되었다는 것은 CPU에게 어떻게 알려지게 될까? 지난 번에 설명한 I/O device controller라는 친구가 그 일을 담당한다. 시시콜콜한 모든 I/O들의 결과를 CPU에게 전달한다면 CPU와 같이 엄청나게 빠른 일꾼들이 자꾸 방해를 받게 되어 프로그램 속도에 좋지 않은 영향을 끼칠 것이다. 따라서 이러한 작업들을 위해 device는 device controller라는 장치를 두었고, CPU는 DMA(Direct Memory Access) controller를 두었다고 생각하면 이해가 쉬울 것이다.

아무튼 이제 동기식 입출력과 비동기식 입출력의 차이를 간단히 알아보자.

동기식 입출력

동기식 입출력(synchronous I/O)은 I/O 요청 후 입출력 작업이 완료된 후에 CPU 제어권이 사용자 프로그램에 넘어가게 되는 입출력 방식이다. 쉽게 말해 우리가 운영체제 수업을 듣고 있는 사람들의 평균 점수를 알고자 할 때, 한 사람 각각의 점수를 읽어 들인 후 이를 합산하여 수강생 수로 나눠 주어야 할 것이다. 이 때, 모든 수강생들의 성적을 읽지도 않았는데 수강생 숫자로 나눠버린다면? 당연히 그 평균 점수는 틀린 것이다. 이러한 상황이 동기식 입출력이 필요한 상황이다.

동기식 입출력을 구현하는 방법에는 두 가지가 있다. 현재 프로세스 A가 실행중이라고 하자.

(1)

  1. I/O가 끝날 때까지 CPU로 하여금 기다리게 한다.
  2. 매 시점마다 하나의 I/O만 일어날 수 있게 된다.

 그러나 이 방법은 너무 무식하지 않은가? I/O는 CPU의 처리 속도에 비해 정말 정말 x 1000 느린데 그 동안 CPU는 아무것도 하지 않고 가만히 앉아 먼 산만 바라보게 하자니 좀이 쑤셔서 안되겠다. 게다가 이렇게 되면 매 순간에 오직 하나의 I/O 작업만 일어나게 된다. 이를 보완하기 위해 (2)와 같은 방법도 존재한다.

(2)

  1. I/O가 완료될 때까지 해당 프로그램에게서 CPU 제어권을 빼앗아 온다.
  2. I/O 처리를 큐에 그 프로그램을 넣는다.
  3. 다른 프로세스B (다른 프로그램)에게 CPU 제어권을 넘겨준다.

 이렇게 되면 프로세스 여러 개가 동시에 일을 하는 것과 같은 효과를 주며, I/O 작업도 동시에 여러 개가 진행될 수 있는 환경이 마련된다.

비동기식 입출력

비동기식 입출력(asynchronous I/O)의 경우는 동기식 입출력과는 다르게 I/O device로 하여금 I/O를 수행하게 한 다음, 이 작업의 결과가 앞으로 진행하게 될 과정(다른 프로세스 아님!) 에 영향을 주지 않을 때 사용한다. 그래서 일반적으로 특정 디스크에 위치한 파일에 쓰기(write)를 할 때 주로 비동기식 입출력을 사용한다. (아! 물론 쓰기가 올바르게 되었는지 확인하는 것은 중요하다. 그래서 쓰기를 할 때도 동기식 입출력을 사용하는 경우도 분명히 존재한다!)

I/O가 시작된 후 입출력이 끝나기를 기다리는 것이 아니라 CPU 제어권이 즉시 사용자 프로그램에게 넘어가게 됨. 즉, I/O와 무관한 일련의 과정들을 수행하게 됨.

DMA (Direct Memory Access)

원래 Memory에는 CPU만 접근할 수 있었다. 그래서 I/O장치들이 CPU와 통신을 해야 할 때는 인터럽트를 통해서 OS로 CPU의 제어권이 넘어간 뒤에 I/O device controller를 통해 CPU와 통신하며, Input 실행 시에는 local buffer에 저장된 데이터를 CPU가 Memory로 Copy를 하는 작업이 이루어져야 했다. 이러한 일련의 작업들은 아주 빠르고 효율적인 CPU에게는 큰 overhead로 다가온다. 우리가 키보드 하나를 누를 때마다 기껏해야 2 Bytes 정도의 Input이 들어갈 텐데 이때마다 CPU에 interrupt를 걸게 된다면 CPU 입장에서는 현재 진행하던 프로세스를 멈추고 계속해서 OS로 넘어가야 되는 상황에 빠지게 된다. CPU가 미치고 환장할 노릇이다.

 이를 해결하기 위해 DMA controller를 두었다. 메모리에 접근할 수 있는 것이 CPU뿐만 아니라 DMA로도 확장된 것이다. 그렇다면 DMA는 어떻게 I/O를 관리하는가?

 1 Byte, 혹은 2 Bytes와 같은 작은 크기의 Input이 들어올 때마다 interrupt를 거는 것이 아니라, DMA controller가 I/O local buffer에 있는 데이터를 Memory로 올려주다가, 일정 크기(여기서는 Block 단위라고 함)만큼 쌓였을 때 CPU에게 interrupt를 한 번 거는 방식으로 관리한다.

I/O 방법 (Memory Mapped I/O)

일반적인 I/O는 메모리와는 별개로 device 공간에서 이루어진다. 따라서 우리가 일반적으로 메모리에 접근하는 Instruction으로는 접근할 수 없고 별개의 Instruction이 존재하여 이를 이용하여 접근하여야 한다.

 그러나 다른 방식으로는 우리가 메모리에 접근하는 방식으로 I/O device에 접근할 수 있게 해주는 방식이 있다. 이것이 Memory Mapped I/O이다. 즉, 예를 들어 Memory Address에서 100번지에 접근한다면 그 공간이 Memory라고 한다면, 1000번지에 접근했을 때는 Device 0에 접근하는 방식으로 이루어 진다.

 요컨대 Memory Mapped I/O (메모리 맵 입출력)은 I/O와 메모리의 Address space를 구분하지 않고 하나의 Memory Address space를 이용하여 다루는 것을 말한다.

운영체제 2강 (프로그램의 실행) image

저장장치 계층 구조

저장장치는 CPU가 접근하여 처리할 수 있는지 여부에 따라 Primary 와 Secondary로 나뉜다. CPU가 직접 접근이 가능하기 위해서는 Byte 단위로 접근이 가능해야 한다. Primary는 CPU로부터 실행할 수 있다는 의미로 Executable이라고도 부르며 CPU의 register, Cache Memory, Main Memory 등이 있다. 이들은 모두 Byte 단위의 주소로 접근 가능하다는 것을 이미 알 것이다. 이와는 달리 Secondary에는 Magnetic Desk, Optical Disk가 있으며, Disk의 경우는 Byte 단위가 아니라 Sector 단위로 접근하게 된다. 다음 그림을 참조해보자.

운영체제 2강 (프로그램의 실행) image

 위로 갈수록 용량은 작지만 속도는 빠르며, 아래로 갈수록 용량은 크지만 속도가 느리다. (아니 그러면 레지스터, 캐시 메모리 용량을 늘리면 되지 않나?) (밥이 없으면 빵을 먹으면 되지 않아?) 그리고 위로 갈수록 가격이 비싸다. 또한, 일반적으로 Primary에 속하는 저장 장치들은 휘발성의 특징을 갖고 있으며 Secondary에 속하는 저장 장치들은 비휘발성의 특성을 갖고 있다.

 이러한 이유로 Secondary 계층에 있는 저장 장치에 있는 데이터들을 전부 Primary 계층의 저장 장치로 올리지 못하기 때문에 그 일부만 올려서 프로세스 안에서 데이터 재사용시 처리 속도의 향상을 꾀하는 것을 ‘Caching’이라 한다.

운영체제 2강 (프로그램의 실행) image

그러면 우리가 실행 파일을 실행했을 때 프로그램이 실행되면 메모리에 어떻게 올라가게 될까?

이것을 이해하기 위해서 위 그림을 살펴보자. 여기서 나오는 중요한 개념이 Virtual Memory이다. 어떤 프로그램을 File System으로부터 실행하게 되면 각 실행 프로그램(프로세스)에 해당하는 Address space가 형성된다. 이것은 그 프로그램 만의 해당 ‘주소 공간’이며 각 프로그램의 실행, 즉, 프로세스 마다 만들어진다.

 Virtual Memory는 세 개의 영역으로 구성된다. 먼저 Code 영역은 실행할 기계어 코드(Binary code)를 담고 있고 이 공간에는 사용자 정의 함수든, 라이브러리 함수든 모두 포함하게 된다. 그리고 data 영역 프로그램이 사용하는 자료구조를 담고 있고, stack 영역은 프로그램이 함수 호출로 이루어져 있기 때문에 이를 위해 별도로 만들어진 스택 공간을 뜻한다.

 추가적으로, 메모리 낭비 방지를 위해 이 가상 메모리 주소 공간에서 당장 필요한 부분만 Physical memory에 올려 놓고 필요로 하지 않은 부분은 Disk에서의 Swap area로 내려 놓는다. 즉, 메인 메모리의 용량 한계를 보완하기 위해 연장 공간으로서 하드 디스크를 이용하는 것을 Swap area라고 하는 것이다. 다만, 이 주소 공간, 즉, 가상 메모리는 프로세스 독자적인 것이기 때문에 프로세스가 종료되면 자동으로 사라진다.

 각 가상 메모리는 프로세스 독자적이라고 했다. 즉, 0번지부터 시작한다. 이 가상 메모리들을 실제 메모리에 올리기 위해서는 변환이 필요하다. 프로세스 A의 주소 공간에서 0번지가 메인 메모리에서도 0번지에 올라가지는 않기 때문이다. 이것을 Address translation이라고 한다.

운영체제 커널 주소 공간

운영체제 2강 (프로그램의 실행) image

Code 영역 : 운영체제의 존재 이유는 자원 관리를 하기 위해서이다. 따라서 커널 코드에는 자원 관리를 위한 코드가 있을 것이다. 또한 OS 커널이 CPU를 점유하는 경우는 인터럽트가 걸렸을 때이다. 따라서 이러한 시스템콜이나 인터럽트를 처리하기 위한 코드도 존재할 것이다. 이외에 많은 기능들이 코드로서 존재한다.

Data 영역 : 운영체제는 직접 하드웨어를 관리하기 때문에 각 하드웨어를 관리하기 위한 자료 구조가 존재할 것인데 그것들을 추상화 하여 나타낸 모양이다. 또한, 운영체제는 각 프로세스를 관리하므로 프로세스마다 운영체제가 관리하고 있는 자료 구조도 필요할 것이다. 이것을 PCB (Process Control Block)라고 한다.

 Stack 영역 : 커널 코드 또한 함수 구조로 짜여져 있기 때문에 스택 영역이 필요하다. 각 프로세스마는 시스템콜을 통해서 커널을 실행할 수 있기 때문에, 어떤 프로세스가 커널을 호출하는지 구분해야 한다. 이를 위해 커널 스택 영역에는 프로세스 마다 커널 스택을 따로 두게 된다.

요약: 프로그램이 실행되는 과정 그림

운영체제 2강 (프로그램의 실행) image

0개의 댓글