개발 공부
[운영체제][OSTEP] 하드 디스크 드라이브
현대 하드 디스크 드라이브는 어떻게 데이터를 저장하고 접근하는지, 디스크 스케줄링은 어떻게 성능을 개선시킬 수 있는지에 대해 공부해봅니다.

![[운영체제][OSTEP] 하드 디스크 드라이브](https://www.datocms-assets.com/66479/1686988115-ostep.jpg?auto=format&w=860)
1️⃣ 인터페이스
드라이브는 읽고 쓸 수 있는 매우 많은 수의 섹터(512 byte 블럭)들로 이루어져 있음.
디스크 위의 n개의 섹터들은 섹터들은 0부터 n-1까지의 이름이 붙어 있음.
➡️ 디스크를 섹터들의 배열로 볼 수 있으며 0부터 n-1이 드라이브의 주소 공간이 됨.
멀티 섹터 작업도 가능함.
➡️ 사실 많은 파일 시스템들이 한 번에 4KB(또는 그 이상)를 읽거나 씀.
하지만 드라이브 제조사는 하나의 512 byte 쓰기만 원자적(즉, 온전히 모두 완료되거나, 온전히 모두 실패함)이라고 보장함.
➡️ 갑작스럽게 전력 손실이 발생한다면 대량의 쓰기 중에 일부만 완료될 수 있음.(때로는 이런 현상을 찢긴 쓰기(torn write)라고 부름.)
디스크 드라이브 사용자들은 몇 가지를 가정하지만 그것들이 인터페이스에 직접적으로 명시되어 있지는 않음.
➡️ Schlosser와 Ganger는 이것을 디스크 드라이브의 "계약 불문율"이라고 부름.
구체적으로는 다음과 같음.
드라이브의 주소 공간에서 가깝게 배치되어 있는 두 개의 블럭을 접근하는 것은 멀리 떨어져 있는 두 개의 블럭을 접근하는 것보다 빠르다고 가정
연속적인 청크의 블럭을 접근하는 것(순차 읽기 또는 쓰기)이 가장 빠르며 어떤 랜덤 접근 패턴보다 매우 빠르다는 것
2️⃣ 기본 구조
현대 디스크의 주요 요소들을 이해해 보자.
플래터(platter)
원형의 딱딱한 표면을 갖고 있는 구조물
➡️ 대체적으로 단단한 물질(알루미늄 등)로 만들어짐.
자기적 성질을 변형하여 데이터를 지속시킴.
디스크는 하나 또는 그 이상의 플래터를 가짐.
각각은 2개의 표면(surface)을 가짐.
드라이브의 전원이 나가더라도 비트를 드라이브에 영구적으로 저장하기 위해 얇은 자성층이 입혀져 있음.
회전축(spindle)
플래터들이 이 회전축으로 고정되어 있음.
모터와 연결되어 있어, 드라이브에 전원이 들어간 경우 플래터를 일정한 (고정된) 속도로 회전시킴.
회전 속도는 분당 회전 수(rotation per minute, RPM)로 측정
➡️ 일반적인 값은 7,200 RPM에서 15,000 RPM 사이
💡 대체로 우리가 관심을 갖는 것은 플래터가 한 바퀴 회전할 때 걸리는 시간
➡️ 예를 들어, 10,000 RPM의 경우 6ms
트랙(track)
플래터의 각 표면에 위치한 동심원 하나
이 동심원을 따라 배치되어 있는 섹터들 위에 데이터가 부호화됨.
표면에는 수많은 트랙들이 서로 촘촘하게 붙어 있음.
➡️ 수백 개의 트랙들이 모여야 사람의 머리카락 두께 정도가 됨.
디스크 헤드(disk head)
표면 위를 읽거나 쓸 때에 필요한 장치
디스크의 자기적 패턴을 감지하거나(읽거나) 변형을 유도하는(쓰는) 기계적 장치
각 표면마다 하나씩 존재
디스크 헤드는 디스크 암(disk arm)에 연결되어 있음.
➡️ 이를 통해 헤드가 원하는 트랙 위에 위치하도록 이동시킬 수 있음.
3️⃣ 간단한 디스크 드라이브
다음과 같은 간단한 디스크를 고려해보자.
하나의 트랙으로만 이루어짐.
트랙에 12개의 섹터가 있음.
각 섹터는 512 byte의 크기(섹터의 보편적인 크기)를 가짐.
주소 영역은 0부터 11까지로 이루어짐.
디스크 헤드는 현재 섹터 6번에 위치해 있음.

단일 트랙 지연 시간: 회전 지연
블럭 0번을 읽는다고 가정했을 때, 디스크가 이 요청을 어떻게 처리해야 할까?
위에서 가정한 간단한 디스크의 경우 처리할 일이 많지 않음.
➡️ ✅ 디스크 헤드가 아래에 원하는 섹터가 위치하기를 기다리면 됨.
💡 이러한 기다림은 현대 드라이브에서도 흔하게 발생하며 I/O 서비스 시간에서 중요한 요소이기 때문에 회전형 지연(rotational delay, 때로는 회전 지연(rotation delay))이라는 특별한 이름을 가짐.
만약 예시의 디스크가 한 바퀴를 다 회전하는 데 걸리는 회전 지연이 R이라고 하면 디스크는 6에서 시작하여 0에 위치하기 위해서는 R/2이 필요함.
트랙이 하나 있을 때의 최악의 경우는 섹터 5번을 읽거나 쓸 때가 될 것임.
➡️ 거의 한 바퀴를 다 돌아야 요청을 처리할 수 있게 됨.
멀티 트랙: 탐색 시간
현대 디스크들은 수백만 개의 트랙을 갖고 있음.
따라서 다음과 같이 아주 조금 더 현실적인 디스크 표면을 고려해보자.
세 개의 트랙으로 이루어짐.
헤드는 가장 안쪽의 트랙(섹터 24번부터 35번까지)에 위치
그 다음 트랙은 다음의 섹터 집합(12번부터 23번까지)을 가짐.
가장 바깥쪽의 트랙에는 선두의 섹터들(0번부터 11번까지)이 존재

이때, 섹터 11번을 읽는 경우를 살펴보자.
이 읽기 요청을 처리하기 위해서 드라이브는 디스크 암을 먼저 올바른 트랙 위에 위치시킴.
➡️ 💡 이 과정을 탐색(seek)이라고 함.
회전과 더불어서 탐색은 가장 비싼 디스크 동작 중 하나임.
탐색은 여러 단계로 되어 있다는 것에 유의
가속 단계: 디스크 암이 움직이기 시작하는 단계
활주 단계: 디스크 암이 최고 속도로 움직이는 단계
감속 단계: 디스크 암의 속도가 줄어드는 단계
안정화 단계: 정확한 트랙 위에 헤드가 위치하게 되는 단계
✅ 드라이브가 정확한 트랙 위에 위치했는지 확실하게 해야 하기 때문에 안정화 시간(settling time)은 매우 중요하며 0.5에서 2ms 정도로 오래 걸림.
탐색 이후에 디스크 암은 올바른 트랙 위에 헤드를 위치시킴.
탐색 과정에서 암이 원하는 트랙 위로 이동을 하는 동안에 당연하게 플래터 역시 회전함.
예시에서 3개의 섹터만큼 이동했다고 가정하면 섹터 9번이 디스크 헤드 아래로 막 지나가고 있기 때문에 약간의 회전 지연 후에 전송을 완료할 수 있음.
섹터 11번이 디스크 헤드를 지나가게 되면 I/O의 마지막 단계인 전송이 이루어져 표면 위의 데이터를 읽거나 쓰게 됨.
💡 이제 I/O 시간의 전체 윤곽이 그려짐: 탐색과 회전 지연 동안 기다린 후 전송
그 외의 세부 사항
하드 드라이브 동작에 대한 몇 가지 흥미로운 내용을 살펴보자.
[트랙 비틀림(track skew)]
많은 드라이브는 트랙 비틀림이라 불리는 기술을 채용하여 트랙의 경계를 지나서 순차적으로 존재하는 섹터들을 올바르게 읽을 수 있게 함.
위의 예시에서 살펴본 디스크의 경우 트랙 비틀림은 다음과 같이 나타낼 수 있음.

한 트랙에서 다른 트랙으로 전환하는 경우에, 바로 인접한 트랙으로 전환되는 경우에도 디스크의 헤드를 다시 위치시키기 위한 시간이 필요
➡️ 이와 같은 비틀림이 없다면 헤드가 다시 다음 트랙으로 넘어 갔을 때 다음에 읽어야 하는 블럭이 이미 헤드를 지나친 경우 다음 블럭을 접근하기 위해 거의 한 바퀴에 해당하는 회전 지연을 감수해야 함.
[멀티 구역(multi-zoned) 디스크 드라이브]
플래터 바깥 측에 공간이 더 많다는 구조적인 이유 때문에 바깥 측 트랙들에는 안쪽 트랙들보다 더 많은 섹터들을 갖고 있음.
➡️ 이러한 트랙들을 흔히 멀티 구역 디스크 드라이브라고 부름.
디스크들은 여러 구역으로 나뉘어 있으며 한 구역은 표면 위에 연속적으로 존재하는 트랙들의 집합임.
각 구역은 트랙 당 같은 수의 섹터들을 가지고 있고, 바깥 측 구역은 안쪽 구역보다 더 많은 수의 섹터들을 갖고 있음.
[캐시(cache)]
역사적인 이유로 때로는 트랙 버퍼(track buffer)라고도 부름.
일반적으로 8 또는 16MB 정도의 작은 크기의 메모리로 드라이브가 디스크에서 읽거나 쓴 데이터를 보관하는 데 사용함.
➡️ 예를 들어, 디스크에서 하나의 섹터를 읽을 때 드라이브가 그 트랙 위의 모든 섹터를 다 읽어서 캐시에 저장할 수도 있음.
이렇게 하면 같은 트랙의 섹터에 대한 이후의 요청에 빠르게 응답 가능
쓰기의 경우 드라이브는 메모리에 데이터가 기록된 시점에 쓰기가 완료되었다고 할지, 디스크에 실제로 기록되었을 때 완료가 되었다고 할지를 정할 수 있음.
➡️ 전자는 write-back 캐싱(또는 즉시 보고(immediate reporting))이라고 부름.
➡️ 후자는 write-through라고 부름.
⚠️ 때로는 write-back 캐싱을 사용할 경우 드라이브가 "더 빠른"것처럼 보이지만 위험할 수 있음.
➡️ 만약 파일 시스템이나 응용 프로그램이 정확함을 위해 데이터를 특정 순서로 디스크에 기록해야 한다고 할 때 write-back을 사용하면 문제가 될 수 있음.
4️⃣ I/O 시간 계산
추상화된 디스크 모델을 만들었으니 간단한 분석을 통해 디스크의 성능을 구할 수 있음.
세 개의 항으로 이루어진 다음의 식을 통해 I/O 시간을 나타낼 수 있음.
T_I/O = T_seek + T_rotation + T_transfer
드라이브 간의 비교를 쉽게 하기 위해 주로 사용되는 I/O의 속도(rate, R_I/O)는 시간을 사용하여 다음의 식과 같이 간단하게 나타낼 수 있음.
R_I/O = Size_transfer / T_I/O
다음의 예시를 통해 이를 더 살펴보자.
먼저 다음과 같은 상황을 가정하자.
두 개의 워크로드가 있음.
하나는 랜덤 워크로드로 디스크에 4KB의 작은 읽기 요청을 발생시킴.
💡 랜덤 워크로드는 DBMS과 같은 많은 중요 응용 프로그램에서 흔하게 사용됨.
다른 하나는 순차 워크로드로서 헤드의 이동 없이 디스크에 연속되어 있는 여러 개의 섹터를 단순히 읽음.
💡 순차 접근 패턴 역시 흔하기 때문에 마찬가지로 중요한 워크로드임.
추가적으로 디스크 드라이브에 대한 몇 가지 가정을 더해보자.
Seagate 사의 디스크들을 예로 사용
하나는 Cheetah 15K.5로 고성능 SCSI 드라이브
다른 하나는 용량을 위해 주로 쓰이는 Barracuda
상세 명세는 다음과 같다.
Cheetah 15K.5 Barracuda
─────────────────────────────────────────
용량 300GB 1TB
RPM 15,000 7,200
평균 탐색 시간 4ms 9ms
최대 전송량 125MB/s 105MB/s
플래터 4 4
캐시 16MB 16/32MB
연결 방식 SCSI SATA
위에 나타난 명세는 디스크 드라이브 시장의 두 가지 중요한 요소를 잘 요약하고 있다.
"고성능" 드라이브 시장
가능한 빠르게 회전하도록 설계
낮은 탐색 시간과 빠른 데이터 전송 속도
"용량" 위주의 시장
바이트당 가격이 가장 중요한 측면
드라이브 속도는 낮지만 주어진 공간에 가능한 많은 비트를 저장
먼저 랜덤 워크로드의 경우를 살펴보자.
랜덤한 디스크의 위치에서 4KB씩 읽기가 발생한다고 했을 때 Cheetah에서 각 읽기가 얼마나 오래 걸릴지를 다음의 식처럼 계산할 수 있다.
T_seek = 4ms(제조사가 명시하고 있는 평균 시간)
T_rotation = 2ms
T_transfer = 30µs(4KB / 125MB/s)
⚠️ 전체 탐색(표면의 한쪽 끝에서 반대편 끝까지 이동)은 대체로 두 배에서 세 배 가량 더 긴 시간이 필요하다는 것에 유의하자.
평균 회전 지연의 경우 15,000 RPM = 250 RPS이므로 한 번의 회전은 4ms가 걸리고 평균적으로 디스크는 반 바퀴를 회전할 것이므로 평균 회전 지연 시간은 2ms가 된다.
위의 식에 따라 계산하면 결과는 다음과 같다.
Cheetah 15K.5
T_I/O: 약 6ms
R_I/O: 약 0.66MB/s
Barracuda
T_I/O: 약 13.2ms(두 배 이상 느림.)
R_I/O: 약 0.31MB/s
다음으로 순차 워크로드를 살펴보자.
➡️ 아주 긴 시간의 전송 전에 한 번의 탐색과 회전이 있다고 가정하자.
전송할 데이터의 크기를 100MB라고 하면 계산 결과는 다음과 같다.
Cheetah 15K.5
T_I/O: 약 800ms
R_I/O: 약 125MB/s
Barracuda
T_I/O: 약 950ms
R_I/O: 약 105MB/s
💡 위에 나타나듯 I/O 속도는 드라이브의 최고 전송 속도와 거의 비슷하게 된다.
이 예시에서 다음과 같은 중요한 사실을 알 수 있다.
✅ 랜덤 워크로드와 순차 워크로드의 드라이브 간 성능 차이가 큼.
Cheetah의 경우 거의 200배 이상 차이, Barracuda의 경우 300배 이상 차이
✅ "성능" 위주의 드라이브와 저사양의 "용량" 위주의 드라이브 간의 성능 차이가 상당히 큼.
팁: 디스크를 순차적으로 사용하자
데이터를 디스크로 전송하거나 전송받을 때에는 가능하면 순차적인 방식으로 해야함.
순차적으로 전송하는 것이 불가능하면 최소한 큰 청크 단위로 데이터를 전송할 수 있는 방법을 생각해야 함.
➡️ 청크의 크기가 클수록 더 좋음.
만약 I/O가 작은 임의의 크기 단위로 처리된다면 I/O의 성능은 극적으로 나빠질 것임.
5️⃣ 디스크 스케줄링
I/O의 비용이 크기 때문에 역사적으로 운영체제는 디스크에게 요청되는 I/O의 순서를 결정하는 데에 중요 역할을 담당했음.
➡️ I/O 요청이 주어졌을 때 디스크 스케줄러는 요청을 조사하여 다음에 어떤 I/O를 처리할지 결정
✅ 각 작업의 길이가 얼마나 될지 알 수 없는 작업 스케줄링과 다르게 디스크 스케줄링의 경우, 디스크 요청 작업이 얼마나 길지를 꽤 정확히 예측할 수 있음.
➡️ 요청의 탐색과 회전 지연의 정도를 예측하면 각 요청이 얼마나 오래 걸릴지 디스크 스케줄러가 알 수 있음.
➡️ (greedy 방식으로) 처리할 수 있는 가장 빠른 요청을 선택할 수 있음.
💡 이와 같이 디스크 스케줄러는 SJF(shortest job first, 최단 작업 우선)의 원칙을 따르려고 노력함.
SSTF: 최단 탐색 시간 우선
최단 탐색 시간 우선(shortest-seek-time-first, SSTF) 또는 최단 탐색 우선(shortest-seek-first, SSF)이라고 불림.
초기의 디스크 스케줄링 접근 방법이 사용함.
💡 SSTF는 트랙을 기준으로 I/O 요청 큐를 정렬하여 가장 가까운 트랙의 요청이 우선 처리되도록 함.
⚠️ 하지만 다음과 같은 이유로 SSTF가 완벽한 방법은 아님.
드라이브의 구조는 호스트 운영체제에 공개되어 있지 않으며 운영체제는 그저 블럭들의 배열로만 인식함.
➡️ ✅ 운영체제가 SSTF를 사용하는 대신 가장 가까운 블럭 우선(nearest-block-first, NBF) 방식을 사용하여 해결할 수 있음.
기아 현상(starvation)에 대한 문제
➡️ 현재 헤드가 위치하고 있는 안쪽 트랙에만 지속적으로 요청이 발생된다면 순수한 SSTF 방식에서는 다른 트랙에 있는 요청들은 완전히 무시됨.
엘리베이터(SCAN 또는 C-SCAN이라고도 함)
이 알고리즘은 트랙의 순서에 따라 디스크를 앞뒤로 가로지르며 요청을 서비스함.
설명에 앞서 디스크를 한 번 가로지르는 것을(밖 ➡️ 안 또는 안 ➡️ 밖) 스위프(sweep)라고 부르자.
💡 즉, 어떤 요청이 이번 스위프에 이미 지나간 트랙에 대해 들어온다면 바로 처리되지 않고 다음 번 스위프에(반대 방향) 처리되도록 큐에서 대기함.
SCAN은 몇 가지 변종이 있는데 모두 비슷하게 동작함.
F-SCAN
스위프하는 동안에는 큐를 동결시킴.
➡️ 디스크를 스위프하는 동안 새로운 요청이 도착하면 다음 번 서비스 될 큐에 삽입
현재 요청과 가까이 있지만 늦게 도착한 요청들의 처리를 지연시켜 멀리 떨어져 있는 요청에 대한 기아 현상을 없앰.
C-SCAN(Circular SCAN)
디스크를 한 방향으로 스위프하는 대신 밖에서 안으로 그리고 다시 안에서 밖으로 스위프함.
🛗 이와 같은 동작 방식으로 인해 엘리베이터 알고리즘이라고 불림.
⚠️ 하지만 SCAN과 그와 유사한 기법들 또한 최고의 스케줄링 기술은 아님.
➡️ SJF의 원칙을 지키기 위해 최선을 다하지 않기 때문(회전을 무시)
SPTF: 최단 위치 잡기 우선
최단 위치 잡기 우선(shortest positioning time first, SPTF) 또는 최단 접근 시간 우선(shortest access time first, SATF)이라고 불림.
아래의 예시를 통해 살펴보자.

헤드가 현재 가장 안쪽 트랙의 섹터 30번 위에 위치해있을 때 스케줄러는 다음의 요청을 처리하기 위해 중간 트랙의 섹터 16번으로 이동해야 할까 아니면 바깥 트랙의 섹터 8번으로 이동해야 할까?
➡️ "상황에 따라 다르다."
상황에 의존적인 이유는 탐색에 걸리는 이유는 탐색에 걸리는 시간과 회전에 걸리는 시간이 다르기 때문임.
이 예시에서 탐색 시간이 회전 지연보다 더 크다면 SSTF 및 그 변종이 잘 동작하게 된다.
하지만 탐색이 회전보다 훨씬 빠른 경우 더 먼 탐색을 통해 바깥 트랙에 있는 8번의 요청을 먼저 처리하는 것이 나음.
💡 현대 드라이브들은 앞서 본 것과 같이 탐색과 회전이 거의 비슷하므로(요청이 무엇이냐에 따라 다르긴 함) SPTF가 유용하고 성능을 개선시킬 수 있음.
⚠️ 하지만 트랙의 경계가 어디인지, 현재 디스크 헤드가 어디에 있는지(회전의 관점에서)를 정확히 알 수 없기 때문에 운영체제에서 이것을 구현하기는 매우 어려움.
➡️ SPTF는 드라이브 내부에서 실행
다른 스케줄링 쟁점들
디스크 스케줄링은 현대 시스템에서 어느 부분이 담당해야 할까?
예전 시스템의 경우 운영체제가 모든 스케줄을 결정했음.
➡️ 대기 중인 요청들의 집합을 살펴보고 운영체제가 최선의 요청을 선택하여 디스크에게 명령을 내림.
✅ 현대 시스템에서 디스크는 대기 중인 여러 개의 요청들을 수용할 수 있으며 복잡한 내부 스케줄러를 자체적으로 갖고 있음.
➡️ 스케줄러가 디스크 컨트롤러 내부에 존재하므로 헤드의 정확한 위치 등 필요한 정보들을 알 수 있어서 SPTF 방식을 정밀하게 구현할 수 있음.
💡 따라서 운영체제의 스케줄러는 최선이라고 보이는 몇 개의 요청을 선택하여 모두 디스크에 내려 보냄.
디스크는 상세한 트랙 배치 정보와 헤드의 위치에 대한 내부 지식을 사용하여 최선의 (SPTF) 순서로 정렬함.
디스크 스케줄러가 수행하는 중요한 또 다른 관련 작업은 I/O 병합(I/O merging)임.
위의 SPTF 예시 그림에서 블럭 33번, 8번, 34번을 읽는 연속된 요청이 있다고 하면 스케줄러는 블럭 33번과 34번 요청을 병합하여 두 블럭 길이의 요청으로 만듦.
➡️ 병합된 요청을 반영하기 위하여 스케줄러는 해당 요청들을 재배치함.
💡 디스크로 내려보내는 요청의 개수를 줄이면 오버헤드를 줄일 수 있기 때문에 운영체제에서 병합은 특히 중요
마지막으로, 디스크로 I/O를 내려보내기 전에 시스템은 얼마나 기다려야 할까?
처리할 요청이 있는 한 디스크가 유휴 상태가 되지 않도록 하는 것
➡️ 작업 보전(work-conserving) 방식
하지만 예측 디스크 스케줄링(anticipatory disk scheduling) 연구에 따르면 때로는 잠시 기다리는 것이 더 좋다는 것을 보임.
➡️ 작업 비보전(non-work-conserving) 방식
➡️ 기다리다 보면 새로운 "좀 더 좋은" 요청이 디스크에 도달할 수 있음.
➡️ 전체적인 효율이 좋아질 수 있지만, 언제 얼마나 기다리는 것을 결정하는 것은 까다로울 수 있음.