복습
- 소프트웨어 인터럽트(Trap)
- System call : 운영체제 내의 함수를 사용자 프로그램이 요청하는 것
- exception : 프로그램이 오류를 범한 경우
- ex) OS memory 영역에 접근하려는 시도 / divide by zero
- 프로그램을 강제 종료하는 등의 대응을 하게 됨
동기식 입출력과 비동기식 입출력
- I/O는 kernel을 통해서만 할 수 있음
- 두 방식 모두 I/O의 완료는 인터럽트로 알림
- I/O device의 controller가 인터럽트를 걸음
동기식 입출력 (synchronous I/O)
- I/O 요청 후 입출력 작업이 완료된 후에야 제어가 사용자 프로그램에 넘어감
- 진행 순서
- 사용자 프로그램이 I/O 요청을 운영체제 커널에 하게 되면,
- 그 I/O 장치에 맞는 device driver를 거치고
- 실제 하드웨어를 통해서 I/O 진행 (다소 오래 걸림, 시간 경과)
- 끝난 I/O 결과가 도착
- 이를 보고 사용자가 다음 작업을 진행
- 구현방법 1
- I/O가 일어나는 동안 아무것도 하지 않음
- 매 순간에 하나의 I/O 장치만 일을 함
- 구현방법 2
- I/O 요청 후 다른 프로그램에 CPU를 넘겨줌
- CPU 낭비 없음
- 다른 프로그램에서 또 I/O 요청이 있다면, 여러 개의 I/O 장치가 일할 수 있음
- I/O가 끝나면 I/O 장치의 컨트롤러가 인터럽트를 걸고, CPU가 이를 확인하고 해당 프로그램에 CPU를 줘도 됨을 알게 됨
- 일반적인 경우 이렇게 구현함 (효율적이므로)
비동기식 입출력 (asynchronous I/O)
- I/O가 시작된 후 입출력 작업이 끝나기를 기다리지 않고 제어가 사용자 프로그램에 즉시 넘어감
- 진행 순서
- 사용자 프로그램이 I/O 요청을 운영체제 커널에 함
- 실제로 I/O 작업이 진행됨 (기다리지 않고 요청만 함)
- 요청만 한 상태로 바로 CPU 제어권을 얻음, 다른 작업 진행
- I/O가 끝나면 끝났음을 알려줌
- I/O 요청만 던지고 해당 프로그램에 다시 CPU 제어권을 줌
- I/O와 관계없이 무관한 일을 실행
어떤 방식을 선택할 것인가
구현하기 나름이다
- 일반적으로 read는 synchronous I/O를 주로 함
- 그렇지 않도록 할 수도 있음
- I/O 결과를 보지 않고, 읽어온 데이터와 상관없이 할 수 있는 작업이 있다면 그것을 하도록 프로그램을 짤 수 있음 (asynchronous I/O 방식)
- write하는 경우는 asynchronous로 진행하는 것이 자연스러움
- 꼭 화면/스토리지에 잘 쓰여졌는지 확인해야 다음 작업을 할 수 있는 것은 아님
- synchronous하게 진행하고 싶을 수도 있음 → 끝났다는 인터럽트가 들어올 때까지 대기할 수도 있음
DMA (Direct Memory Access)
- 메모리에 접근할 수 있는 장치
- 원래는 메모리에 접근할 수 있는 장치가 CPU뿐
- I/O device들의 인터럽트를 모두 CPU가 처리하다보면, CPU가 효율적으로 동작하지 못함
- 따라서 메모리에 접근할 수 있는 DMA controller를 추가로 설정
- 작은 일들은 buffer에 특정 크기 (block/page)의 데이터가 쌓이면, 이것이 끝났음을 CPU에게 한 번 인터럽트를 걸어서 알려줌
- 이 과정에서 DMA가 local buffer에 있는 내용을 memory로 카피하는 식으로 진행됨
- CPU의 중재 없음
- device의 buffer storage의 내용을 메모리에 block 단위로 직접 전송
- 어느 정도 block에 해당하는 Data에 대한 I/O가 끝났으면, 인터럽트를 한번 걸어서 아까 요청한 작업이 memory까지 올라왔음을 CPU에게 보고
- 바이트 단위가 아닌, block 단위로 인터럽트를 발생시킴
- 이런 식으로 CPU가 인터럽트 당하는 빈도를 줄임 → 효율적
- 빠른 입출력 장치를 메모리에 가까운 속도로 처리하기 위해 사용
- 빠른 입출력장치는 인터럽트를 빈번하게 걸 것이므로
서로 다른 입출력 명령어
- 일반적인 I/O 방식
- CPU에서 실행할 수 있는 instruction = memory만 접근하는 instruction + I/O 장치에 접근해야 하는 instruction
- Load-Store instruction : memory만 접근하는 instruction
- 각 I/O device에 접근하려면 Load-Store instruction과는 별개의 instruction이 정의되어 있어서, 이러한 instruction으로 I/O 장치에 접근하는 것이 일반적
- I/O device도 주소가 있어서, 특정 주소에 대한 I/O 접근하는 instruction을 실행하면, 해당 device에 접근하는 것이 되는 것
- 즉, memory 접근하는 instruction과 I/O 장치 접근하는 instruction이 분리되어 있음
- Memory Mapped I/O
- I/O device에도 memory 주소를 매겨서, memory 접근하는 instruction을 통해서 I/O를 하는 방식
- I/O device에도 memory 주소의 연장 주소를 할당하는 방식
저장장치 계층 구조
CPU → Register(CPU 내) → Cache Memory(CPU 내에 있기도 함) → Main Memory → Secondary storage
- 위로 갈수록 속도가 빠름
- 위로 갈수록 용량이 적음
- Registers, Cache Memory, Main Memory는 휘발성 매체이고, 나머지는 비휘발성임
- Main Memory도 비휘발성 매체가 들어갈 수 있는 반도체가 나오고 있으나, 전통적으로는 Main memory 윗단은 휘발성, 아랫단은 비휘발성으로 구성됨
- CPU에서 직접 접근할 수 있는 메모리 스토리지 매체를 Primary라고 하고, Executable하다고 말함
- CPU가 직접 접근/처리할 수 없는 매체를 Secondary라고 함
- byte 단위 접근이 불가능, sector 단위 접근이 가능
- Executable하지 않음
- Main Memory 접근하는 데에 시간이 들기 때문에, (DRAM 접근에 약 10 ~ 100 clock cycle 소요) 이러한 속도 차이를 완충하기 위해 Cache Memory를 두고, Register로 읽어들이는 작업을 함
- 윗단의 용량이 적기 때문에, 아랫단의 내용을 모두 윗단에 올릴 수 없음
- Caching : 당장 필요한 것만 밑에서 위로 올려서 사용
- 빠른 매체로 정보를 읽어들여서 쓰는 것
- 재사용을 목적으로 함
- 처음 요청되었을 때는 반드시 밑단으로 접근해야 하므로
- 새로운 것이 들어오면 윗단에 있던 것을 쫓아내는 작업이 필요
프로그램의 실행 (Memory load)
Virtual memory / Physical memory
-
프로그램은 실행 파일 형태로 하드디스크에 저장되어 있음
-
이를 실행시키면, 이것이 메모리로 올라가서 프로세스가 되면서 실행됨
- Physical memory에 바로 올라가는 것이 아니라, 중간에 Virtual memory라는 단계를 거치게 됨
-
어떤 프로그램을 실행시키면, 그 프로그램의 Address space가 형성됨
- 0번지부터 시작하는 그 프로그램만의 독자적인 주소공간
- 각 프로그램의 주소 공간은 code / data / stack 영역으로 구성됨
- code : CPU에서 실행할 기계어 코드를 담음
- data : 전역변수 등 프로그램이 사용하는 자료구조
- stack : 함수를 호출하거나 리턴할 때 데이터를 쌓았다가 꺼내가는 용도로 사용
-
Physical memory에서 kernel 영역은 컴퓨터 부팅 후 메모리에 항상 상주해서 올라가 있음
- 프로그램들은 실행시키면 주소공간이 생겼다가, 프로그램을 종료시키면 사라짐
- 프로그램을 실행시켰을 때 만들어지는 주소공간을 Physical memory에 통채로 다 올려놓는 것이 아님
- 메모리가 낭비되기 때문
- 당장 필요한 부분에 대한 코드만 올려놓음
- 사용되지 않는 것은 쫓아냄 (Disk의 swap area에 내려놓음)
- main memory의 연장 공간으로 하드디스크를 사용하는 것을 swap area 용도로 쓴다고 말한다. 이를 virtual memory 기법이라고 하기도 한다.
즉, 프로그램마다 할당되는 주소공간은 머릿속에만 있는 주소공간이지, 실제로 어딘가에 연속적으로 할당되어 있는 것은 아니다.
쪼개져서 어떤 부분은 Physical memory에, 어떤 부분은 Swap area에 있게 된다.
따라서 프로그램마다 할당된 주소공간은 Virtual memory 라고 한다.
- swap area와 file system으로 하드디스크가 2번 그려져 있는데, 이 두 가지는 용도가 다른 것임 (관리 방법도 다름)
- file system 용도는 전원이 나가더라도 내용이 유지가 되어야 함 (비휘발성)
- swap area 용도는 전원이 나가면 의미가 없는 데이터임
- 전원이 나가면 프로세스가 종료되고, memory에 있는 내용도 사라지기 때문
- Physical memory도 0번지부터 시작하는 주소가 있음
- Virtual memory와 Physical memory의 주소는 다를 것
- 이를 주소 변환(Address translation)이라고 함
- 메모리 주소변환을 해주는 계층이 있음 : 메모리 주소변환 계층
- OS가 할 수 있는 것은 아니고, 하드웨어 장치가 해줌
- 추후 학습 예정
Kernel의 주소 공간
- Kernel도 하나의 프로그램이기 때문에 code / data / stack으로 구성되어 있음
- code 영역
- (OS 목적을 위한 코드) : 자원 관리, 편리한 서비스 제공을 위한 코드
- (OS는 언제 CPU를 얻게 되는가?) : 시스템콜, 인터럽트 처리 코드에 함수형태로 구현되어 있을 것
- data 영역
- 운영체제가 사용하는 여러 자료구조가 정의되어 있을 것
- OS는 CPU, memory, disk 등의 하드웨어를 통제함
- 이를 위해 하드웨어 종류별로 자료구조를 만들어서 관리하고 있을 것
- process들을 관리함
- 각 프로그램별 주소공간을 관리하기 위한 자료구조
- 어떤 프로그램이 CPU를 얼마나 썼는지 / 다음에는 누구에게 Memory를 얼만큼 줘야 하는지 등을 결정하려면 각 프로그램마다 운영체제가 관리하고 있는 자료구조가 필요
- 이를 PCB (Process Control Block) 라고 함
- 시스템 안에 프로그램이 하나 돌아가면, 그 프로그램을 관리하기 위한 자료구조가 OS 커널에 하나씩 만들어지는데, 이것이 PCB임
- stack 영역
- 함수 호출 / return 시 stack 영역을 활용해야 함
- 운영체제의 코드는 여러 프로그램들이 요청에 따라서 불러서 쓸 수 있음
- 사용자 프로그램들이 운영체제 커널의 코드를 불러서 함수를 실행
- 따라서 어떤 사용자 프로그램이 커널의 코드를 실행중인가에 따라, 사용자 프로그램마다 커널 스택을 따로 둠
사용자 프로그램이 사용하는 함수
함수의 종류
- 사용자 정의 함수
- 라이브러리 함수
- 자신의 프로그램에서 정의하지 않고 가져다 쓴 함수
- 자신의 프로그램의 실행 파일에 포함되어 있음
- 커널 함수
- 운영체제 프로그램의 함수
- System call = 커널 함수의 호출
- 자신의 프로그램 안에 들어있는 함수가 아닌, Kernel code 안에 들어 있는 함수
- 따라서 사용자 정의 함수, 라이브러리 함수와는 다른 영역에 있기 때문에 System call이 일반적인 함수 호출과는 다르다는 것
- 함수 호출은 Virtual memory 주소 상에서의 점프임
- OS 영역으로 가지 못하는 문제도 물론 있음
프로그램의 실행 단계
- User mode : 프로그램이 직접 CPU를 잡고 있는 상태
질문
- I/O 작업이 완료되고 interrupt를 건 후, 돌아오는 길의 device driver는 무슨 역할을 하는가? 사진 참조
- DMA의 실행 방식에 대한 보다 구체적인 이해 필요. 뭔가 그림이 제대로 안 잡힌 느낌
참고 링크
KOCW 운영체제 - 이화여대 반효경 교수 (2014-1) 4강