사실 다른 장 먼저 해야하는데 내일 이 장을 하기 때문에 멍을 안 때리기 위해 정리한다.
36장 I/O Devices
영속성에 들어가기 전에, 입력/출력 장치의 개념을 알고 어떻게 I/O를 시스템에 통합하는지에 대해 알아보자.
CPU는 메모리 버스를 통해 주요 메모리와 연결된다.
몇몇 디바이스들은 일반적인 I/O 버스를 통해 연결된다.
버스란?
CPU와 램, 입출력 장치들 간의 정보를 제공할 수 있게 하는 데이터 경로이다.
I/O 버스란?
CPU와 입출력 장치를 연결하는 데이터 경로이다.
입출력 포트, 인터페이스, 디바이스 컨트롤러. 이 세 가지 하드웨어 요소를 통해 I/O 기기와 연결되어 있다.
표준 장치
구성요소
- 내부 구조 (internals) : 시스템에게 제공하는 장치에 대한 추상화를 정의한다. ex) 마이크로 컨트롤러(CPU), 메모리, 다른 하드웨어에 특화된 칩.
- 하드웨어 인터페이스 : 시스템 소프트웨어가 동작 제어할 수 있도록 한다.
총 3가지 레지스터로 구성되어 있다.(단순화된 장치 한정)
- 상태 레지스터 : 현재 상태 확인
- 명령 레지스터 : 특정 동작 수행하도록 한다.
- 데이터 레지스터 : 장치에 데이터를 보내거나 받는다.
3가지 레지스터들을 읽거나 씀으로써, 운영체제는 장치의 동작을 제어할 수 있다.
while (STATUS == BUSY)
; // 장치가 바쁜 상태가 아닐 때까지 대기
데이터를 DATA 레지스터에 쓰기
명령어를 COMMAND 레지스터에 쓰기
(그러면 장치가 명령어를 실행한다)
While (STATUS == BUSY)
; // 요청을 처리하여 완료할 때까지 대기
장치가 운영체제 대신 특정 동작을 할 때, 운영체제와 장치 간의 상호 동작 과정 방식
총 4단계.
1. polling
: 장치의 상태 레지스터를 반복적으로 읽어서 명령의 수신 가능 여부를 확인한다.
간단하고 제대로 작동하지만, 매우 비효율적이다.
다른 CPU를 양도하지 않고, 장치가 동작을 완료하는 동안 계속 루프를 돌면서 장치 상태를 체크하기 때문.
즉, CPU 시간이 고작 디바이스 기다리는데에 낭비된다.
2. 운영체제가 데이터 레지스터에 어떤 데이터를 전달한다.
ex) 장치가 디스크면 디스크 블럭에 해당하는 데이터 전달하고 여러 번의 쓰기 수행.
이렇게 데이터 전송에 메인 CPU가 관여하는 경우를 programmed I/O라고 부른다.
3. 운영체제가 명령 레지스터에 명령어를 기록한다.
4. 운영체제는 디바이스가 처리를 완료했는지를 확인하는 폴링 반복문을 돌면서 기다린다.
polling 사용 비용 피하는 법
어떻게 하면 자주 폴링을 하지 않으면서, 운영체제가 장치 상태도 확인하고, 장치를 관리하는 CPU의 오버헤드를 줄일 수 있을까?
인터럽트 사용!
디바이스를 폴링하는 대신 운영체제는 입출력 작업을 요청한 프로세스를 블록 시키고 CPU를 다른 스로세스에게 양도한다.
장점: CPU와 디스크가 시간 흐름표의 중간 부분에서 적절히 활용되도록 해준다.
그러나, 인터럽트는 항상 최적의 해법은 아니다.
대부분 작업이 한 번의 폴링만으로 끝날 정도로 매우 빠른 장치의 경우 -> 인처럽트 때문에 시스템이 느려진다.
문맥 교환은 매우 비싼 작업이기 때문. (다른 프로세스로의 교환)
결론,
디바이스 빠름 -> poll
디바이스 느림 -> interrupt 사용하자.
DMA를 이용한 효율적 데이터 이동
표준 방식은 데이터 양도 고려해야함.
데이터가 많을 경우, programmed I/O(PIO)를 사용하면 CPU가 또 다시 단순 작업 처리에 소모된다.
CPU가 너무 많은 시간을 데이터를 디스크에서 또는 디스크로 이동하는 데 사용한다.
CPU를 더 효율적으로 활용하는 방법이 없을까?
DMA(Direct Memory Access) 사용!
:직접 메모리 접근 방식
시스템 내에 있는 특수 장치. CPU의 간섭 없이 메모리와 장치 간에 전송을 담당한다.
메모리 상의 데이터 위치와 전송할 데이터 크기를 알고, 메모리의 데이터를 복사한다.
(운영체제가 DMA 엔진에 메모리 상의 데이터 위치와 전송할 데이터의 크기와 대상 장치를 프로그램함.)
DMA가 일할 동안 CPU는 다른 일을 할 수 있다.
데이터 카피가 끝나면, DMA가 인터럽트를 일으켜 I/O를 디스크에서 시작시킨다.
OS가 디바이스와 상호작용하는 방법
- I/O 명령 : 특정 디바이스 레지스터에게 데이터 전달하는 방법. ex) x86의 in과 out 명령어
- memory mapped I/O : 하드웨어가 장치의 레지스터들이 마치 메모리 상에 존재하는 것처럼 만든다. os는 특정 레지스터를 접근하기 위해 해당 주소를 load(읽기) 또는 store(쓰기)를 하면 된다. 하드웨어는 load/store 명령어가 주 메모리를 향하는 대신 장치로 연결되도록 한다.
IDE 디스크 드라이버
IDE 디스크는 시스템에 4개의 레지스터로 이루어진 단순한 인터페이스를 제공한다.
- Control register : 저장장치에 접근 명령
- Command block registers
- Status register
- Error register
이들은 ( x86의) in과 out 입출력 명령어를 사용해 특정 "I/O 주소들"을 읽거나 씀으로써 접근 가능하다.
장치와 상호작용하기 위한 기본 프로토콜
- 장치가 준비될 때까지 대기.
- Command 레지스터에 인자 값 쓰기. 섹터 수, 섹터들의 논리블럭 주소(LBA), 드라이브 번호 를 Command 레지스터에 기록.
- Command 레지스터에 읽기/쓰기를 전달하기. READ-WRITE 명령어를 Command 레지스터에 기록한다. 쓰기 명령어의 경우, 데이터를 전송한다. 데이터 포트에 데이터를 기록한다.
- 인터럽트 처리
- 에러 처리
디바이스의 상호작용
OS는 어떻게 다른 종류의 인터페이스와 상호작용할까?
ex) 파일 시스템을 SCSI 디스크와 IDE 디스크, USB 키체인 드라이버 위에서 작동시키려 함.
추상화 덕분!
SCSI에 대한 read, IDE에 대한 read 등 어떠한 종류의 단일 인터페이스도 모두 캡슐화로 정보를 은닉한다.
37장 Hard Disk Devices
요즘은 SSD가 보편적이지만, 과거의 기법을 배워보자.
디스크의 주요 요소
- Platter : 자기적 성질을 변형하여 데이터를 지속시킨다. 비트(1001...)을 저장한다. 디스크는 하나 또는 그 이상의 플래터를 가지고 있으며 각각은 2개의 표면을 가진다.
- Spindle: 플래터를 회전시킨다.
- Disk arm과 Disk head : 읽기와 쓰기 동작을 한다. arm이 회전으로 인해 생기는 양력으로 뜨면 head가 플레터의 자성체를 읽는다. arm을 통해 헤드가 원하는 트랙 위에 위치하도록 이동시킬 수 있다.
- RPM : 분당 회전 수. 회전 속도 단위이다.
- 섹터 : 데이터를 읽고 쓴느 단위이다. (512 byte)
- 트랙: 각 표면에 동심원을 따라 배치되어 있는 섹터들 위에 데이터가 부호화되는데, 이때의 동심원 하나를 말한다.
단일 트랙 지연 시간 : 회전 지연
간단한 디스크는 디스크 헤드 아래에 원하는 섹터가 위치하기를 기다리기만 하면 요청을 처리할 수 있다.
만약 한 바퀴를 다 회전하는 데 걸리는 회전 지연이 R이라면, 디스크가 같은 트랙의 반대편 숫자를 읽기위해 걸리는 시간은 R/2가 걸릴 것이다. 즉, 읽거나 쓰려는 디스크 헤드가 반대편 숫자에 위치하기 위한 시간이다.
헤드와 암은 멈춰 있고 트랙이 돈다. 그러면 아래의 예시로는 헤드가 읽는 섹터의 순서가 6->7->8->9->10->... ->5가 될 것이다.
여기서 0섹터를 읽는데 까지의 시간이 R/2가 걸리는 것이다. 가장 오래 걸리는 섹터는 5가 된다.
Seek Time
: 트랙끼리 탐색하는 시간이다.
지연 시간은 같은 트랙 안에서 발생하는 시간이라면, 탐색 시간은 다른 트랙에서 발생한다.
탐색 단계
가속 -> 지나가는 비용 -> 속도를 줄여 목적지 탐색 -> 안정화 시간(settling time). 목적지에 안착.
Track skew (트랙 비틀림)
: 트랙의 경계를 지나 순차적으로 존재하는 섹터들을 올바르게 읽을 수 있게 함.
한 트랙에서 다른 트랙으로 전환하는 경우에,
바로 옆의 트랙으로 전환되는 경우에도 다음 섹터를 읽으려고 하면,
디스크의 헤드를 다시 위치시키기 위한 시간이 필요하다.
이와 같은 비틀림이 없다면 이동 시간이 있는 동안 다음에 읽어야 하는 블럭이 이미 헤드를 지나쳤을 수도 있기 때문에 다음 블럭을 접근하기 위해 거의 한 바퀴에 해당하는 회전 지연을 감수해야한다.
그래서!
다음 트랙의 섹터 위치를 살짝 뒤로 미뤄서, 헤드가 도착할 때까지 기다리게 만드는 것이 트랙 비틀림이다.
안쪽 트랙과 바깥쪽 트랙의 위치를 몇 도 비틀어 설계. 23섹터와 24의 위치가 멀어지게 안쪽 트랙의 섹터 번호들을 조금 뒤쪽으로 밀어둔다. 22->23-> ..... -> 24 순으로 읽게 된다.
멀티 구역
: 바깥 트랙의 섹터 크기를 안쪽 트랙의 길이와 비례하게 배치하는 것.
바깥 트랙이 안쪽 트랙보다 더 길기 때문이다.
캐시(Track buffer)
: 작은 크기의 메모리. 디스크에서 읽거나 쓴 데이터를 보관하는 데 사용.
요청을 빠르게 처리하도록 도와준다.
캐싱의 종류
초록: Write-Back. 메모리에 데이터가 기록된 시점에 쓰기가 완료되었다고 함.
파랑: Write-Through. 디스크에 실제로 기록되었을 때 완료가 되었다고 함.
I/O 시간 계산
입출력 시간 = 탐색 시간 + 회전 시간 + Disk에서 Memory로 이동하는 시간
입출력 비율 = 전달하려는 데이터 크기 / 입출력 시간
즉, 전달할 데이터의 크기가 있으면 비율을 통해 입출력 시간을 구할 수 있다.
Random 워크로드로 4KB의 작은 읽기 요청을 발생시켰을 때와 Sequential 워크로드로 헤드의 이동 없이 연속되어 있는 여러 개의 섹터를 단순히 읽었을 때를 비교해보자. 순차 워크로드에서 전송할 데이터의 크기는 100MB로 가정한다.
Cheetah 15K.5 | Barracuda | |
Capacity | 300 GB | 1TB |
RPM | 15,000 | 7,200 |
Average Seek | 4ms | 9ms |
Max Transfer | 125 MB/s | 105 MB/s |
Platters | 4 | 4 |
Cache | 16 MB | 16/32 MB |
Connects via | SCSI | SATA |
계산을 하면,
풀이 방법 | Random Write | Sequential Write | |
탐색 시간 | Average Seek과 동일 | 4ms | 4ms |
회전 시간 | 60RPM = 1RPS(초당 회전수) 1회전 시간 (초) = 1 / RPS 1회전 시간 (ms) = 1000 / RPs |
15000 RPM(=250RPS = 1000/250 ms = 4ms /1 rotation) / 2 = 2ms | 15000 RPM / 2 = 2ms |
전송 시간 | 전송할 데이터 크기 / 최대 전송 가능 데이터 크기(Max Transfer) | 4KB / 125(MB/s) = 30us | 100MB / 125(MB/s) = 800ms |
I/O 시간 | 탐색 시간 + 회전 시간 + 전송 시간 | 4ms + 2ms + 30us = 6ms | 4ms + 2ms + 800ms = 806ms |
I/O 비율 | 전송할 데이터 크기 / I/O 시간 | 4KB / 6ms = 0.66MB/s | 100MB / 800ms = 125MB/s |
이렇게 계산할 수 있다!
Performance냐 성능이냐, Random 이냐 Squencial이냐 잘 고르자.
디스크 스케줄링의 종류로는
- FCFS : 요청이 들어온 순서대로 처리하는 가장 단순한 방식.
- SSTF: 현재 헤드 위치에서 가장 가까운 요청을 먼저 처리함.
- SCAN (전형적인 엘리베이터 알고리즘) : 헤드가 한 방향으로 이동하며 요청을 처리한 뒤, 끝까지 가면 방향을 반대로 바꿈.
- C-SCAN : 한 방향으로만 스캔하고 끝까지 가면 반대 방향은 건너뛰고 다시 처음부터 스캔.
- C-Look : C-SCAN과 유사하지만, 실제 요청이 있는 트랙까지만 가고, 가장 안쪽 요청까지 갔다가 다시 처음으로 점프.
이 있다.
디스크의 헤드 이동을 최적화하여 평균 접근 시간을 줄이기 위한 방법이다.
I/O 병합
33, 8, 34 요청이 왔으면 33과 34번 요청을 병합하여 두 블럭 길이의 요청으로 만든다. 스케줄러는 해당 요청들을 재배치한다.
이렇게 하는 이유는, 디스크로 내려보내는 요청의 개수를 줄이면 오버헤드를 줄일 수 있기 때문이다.
'학교 공부' 카테고리의 다른 글
[데이터베이스] 트랜잭션 (0) | 2025.06.12 |
---|---|
[데이터베이스] Index와 B+Tree (0) | 2025.06.12 |
[운영체제] 파일 시스템 (1) (3) | 2025.06.08 |