테마
보조기억장치와 입출력 2
보조기억장치와 입출력에서 본 저장 장치를 여러 개 묶어 쓰는 방법과, CPU가 이런 장치와 데이터를 주고받는 방법을 이어서 다룬다. 여러 디스크를 하나처럼 묶는 RAID(Redundant Array of Independent Disks, 독립 디스크 중복 배열)를 먼저 보고, 장치와 CPU 사이를 잇는 컨트롤러와 드라이버, 세 가지 입출력 방식을 정리한다.
RAID: 여러 디스크를 하나처럼
RAID는 여러 디스크를 묶어 하나의 큰 디스크처럼 쓰면서 속도나 안정성을 높이는 기술이다. 묶는 방식에 쓰는 기법은 세 가지다.
- 스트라이핑(striping): 데이터를 조각내 여러 디스크에 나눠 쓴다. 여러 디스크를 동시에 읽고 쓰므로 빠르다.
- 미러링(mirroring): 같은 데이터를 두 디스크 이상에 똑같이 복사한다. 한 디스크가 고장 나도 다른 쪽으로 읽는다.
- 패리티(parity): 여러 데이터 조각으로 검사용 값을 계산해 둔다. 조각 하나가 사라져도 나머지 조각과 패리티로 되살릴 수 있다.
RAID 5·6의 분산 패리티는 패리티를 한 디스크에 몰지 않고 모든 디스크에 돌아가며 나눠 저장한다는 뜻이다. 패리티 디스크 하나에 쓰기가 몰려 느려지는 것을 막는다.
| 수준 | 방식 | 최소 디스크 | 쓸 수 있는 용량(n개) | 견디는 고장 |
|---|---|---|---|---|
| RAID 0 | 스트라이핑 | 2 | n개 전부 | 없음 |
| RAID 1 | 미러링 | 2 | 1개분 | n−1개 |
| RAID 5 | 스트라이핑 + 분산 패리티 1개 | 3 | n−1개 | 1개 |
| RAID 6 | 스트라이핑 + 분산 패리티 2개 | 4 | n−2개 | 2개 |
| RAID 10 | 미러링한 쌍을 스트라이핑 | 4 | 절반 | 쌍마다 1개 |
RAID 5는 용량 손해가 적지만, 작은 데이터를 고칠 때마다 옛 데이터와 옛 패리티를 읽어 새 패리티를 계산해 써야 하므로 작은 쓰기가 느리다. RAID 10은 패리티를 계산하지 않고 두 디스크에 똑같이 쓰기만 하므로 쓰기가 빠르다. 디스크가 고장 나면 짝 디스크의 내용을 그대로 복사하면 되므로, 나머지 디스크를 모두 읽어 계산해야 하는 RAID 5보다 복구가 단순하다. 대신 용량은 절반만 쓴다. 그래서 데이터베이스 서버에서 자주 고른다. 그리고 RAID는 백업이 아니다. 실수로 지운 파일이나 랜섬웨어(ransomware, 파일을 몰래 암호화하고 풀어 주는 대가로 돈을 요구하는 악성 프로그램)에 암호화된 파일은 모든 디스크에 똑같이 반영되기 때문이다.
장치 컨트롤러와 장치 드라이버
CPU는 디스크·키보드·네트워크 카드를 직접 다루지 않는다. 장치마다 붙은 장치 컨트롤러(device controller)가 중간에서 통신을 맡는다. 컨트롤러 안에도 CPU처럼 레지스터(register)라 부르는 작은 저장 칸이 있다. 명령을 받는 레지스터와 상태를 알리는 레지스터, 데이터를 잠시 담는 버퍼를 가지고 있다. 장치는 CPU보다 훨씬 느리므로, 컨트롤러가 버퍼에 데이터를 모아 속도 차이를 흡수한다.
장치 드라이버(device driver)는 운영체제가 특정 컨트롤러를 다룰 수 있게 해 주는 소프트웨어다. 블록(block)은 디스크가 한 번에 읽고 쓰는 정해진 크기의 데이터 묶음이다. 커널(kernel)은 "블록 읽기" 같은 공통 요청만 알고, 그 요청을 해당 컨트롤러의 레지스터에 무엇을 써야 하는지로 바꾸는 일은 드라이버가 한다. 새 장치를 꽂으면 드라이버를 설치해야 하는 이유가 이것이다.
입출력 방식: 폴링, 인터럽트, DMA
CPU가 장치와 데이터를 주고받는 방식은 세 가지다. 두 가지 질문으로 나누면 차이가 보인다. 장치가 일하는 동안 CPU가 기다리는가, 그리고 데이터를 CPU가 직접 옮기는가다.
- 폴링(polling): CPU가 컨트롤러의 상태 레지스터를 반복해서 읽으며 끝났는지 확인한다. 구현이 단순하지만, 느린 장치를 기다리는 동안 CPU가 다른 일을 못 하고 시간을 버린다.
- 인터럽트(interrupt) 기반 입출력: CPU는 명령만 보내고 다른 일을 한다. 장치가 일을 끝내면 컨트롤러가 인터럽트로 알리고, 그때 CPU가 결과를 처리한다. 기다리는 시간을 다른 프로그램에 쓸 수 있다. 다만 데이터가 많으면 CPU가 컨트롤러 버퍼와 메모리 사이에서 데이터를 한 조각씩 직접 옮겨야 해서 여전히 CPU 시간을 쓴다.
- DMA(Direct Memory Access, 직접 메모리 접근): DMA 컨트롤러가 CPU 대신 장치와 메모리 사이의 데이터를 직접 옮긴다. CPU는 "어느 장치에서 메모리 몇 번지로 얼마만큼"만 알려 주고, 전송이 다 끝나면 인터럽트 한 번만 받는다. 디스크 블록(disk block)처럼 큰 데이터를 옮길 때 CPU 부담이 가장 적다.
다이어그램 로딩…
| 방식 | CPU가 기다리는가 | CPU가 데이터를 옮기는가 | 알맞은 경우 |
|---|---|---|---|
| 폴링 | 기다린다 | 옮긴다 | 곧 끝나는 아주 빠른 장치 |
| 인터럽트 | 안 기다린다 | 옮긴다 | 키보드처럼 가끔 적게 오는 입력 |
| DMA | 안 기다린다 | 안 옮긴다 | 디스크·네트워크의 큰 전송 |
폴링이 늘 나쁜 것은 아니다. 인터럽트가 오면 CPU는 하던 일의 상태를 저장하고 처리 루틴으로 넘어갔다가 다시 돌아와야 하므로 매번 비용이 든다. 그래서 요청이 아주 빨리 끝나는 장치에서는 잠깐 폴링하는 편이 더 빠를 수 있다. 실제 운영체제는 상황에 따라 둘을 섞어 쓴다. 인터럽트 처리 과정은 CPU의 동작 2에서 다룬다.
면접 포인트
- "RAID 5와 10 차이는?" → 패리티로 용량 효율(n−1) vs 미러링+스트라이핑으로 쓰기 성능·단순 복구(용량 절반). RAID는 백업이 아니다.
- "장치 드라이버가 하는 일은?" → 커널의 공통 요청을 그 장치 컨트롤러의 레지스터에 쓸 내용으로 바꾼다.
- "폴링·인터럽트·DMA 차이는?" → CPU가 기다리느냐, 데이터를 직접 옮기느냐. DMA는 끝날 때 인터럽트 한 번.
