본문으로 바로가기

하드 디스크의 이해

category 리눅스/Linux 일반 2013. 6. 20. 04:08

디스크의 구조를 파악한 후 내용을 살펴보도록 하자.

하드디스크

데이터는 가장 외곽에서 부터 안쪽으로 저장된다.

하드디스크 내부구조

디스크의 기하학

디스크 장치에 대해서는 물리적인 디스크 구조를 인식해야 할 필요가 있다. 리눅스에서는 fdisk 를 이용해서 이 구조를 살펴 볼 수 있다.

하드 디스크의 두가지 접근 방식 (Addressing Mode)

하드 디스크의 라벨을 통해 확인할 수 있다.

CHS 와 LBA

CHS 방식

고전방식으로 데이터를 읽고 쓰는 하나의 섹터(물리적인 디스크 접근의 최소 단위)의 위치를 정하기 위해서는 세개의 값, 즉 실린더 번호(외부에서 세어봐서 몇번째 트랙인가), 헤드 번호(어떤 헤드가 읽기/쓰기용 디스크면인가), 그리고 섹터 번호(트랙 안의 몇 번째 블록인가)를 정해 주면 된다. 실린더는 디스크의 외측부터 순서대로 번호가 붙여져 있다.

실제로 Linux의 디바이스 드라이버가 CHS 값을 계산하여 디스크 장치와 데이터 간의 정보교환을 실행하므로 유저가 CHS를 신경 쓸 필요는 없다. 다만 디스크 파티션 작성을 할 때에는 당시 'MS-DOS 환경'과 리눅스간의 환경을 공통으로 맞추는데 디스크 파티션을 실린더 단위로 구분할 필요가 생겼다. 이로인해 리눅스에서도 파티션을 생성할 때 파티션의 시작 위치와 종료 위치를 실린더 번호로 지정하게 되었다.

fdisk 의 -l 옵션을 이용하여 실린더 번호 정보를 확인할 수 있다.

fdisk -l /dev/sda

  Disk /dev/sda: 32.2 GB, 32212254720 bytes
  255 heads, 63 sectors/track, 3916 cylinders
  Units = cylinders of 16065 * 512 = 8225280 bytes
  Sector size (logical/physical): 512 bytes / 512 bytes
  I/O size (minimum/optimal): 512 bytes / 512 bytes
  Disk identifier: 0x00060d5a

     Device Boot      Start         End      Blocks   Id  System
  /dev/sda1               1        1275    10240000   83  Linux
  /dev/sda2            1275        1913     5120000   83  Linux
  /dev/sda3            1913        2168     2048000   82  Linux swap / Solaris
  /dev/sda4            2168        3917    14048256    5  Extended
  /dev/sda5            2168        2295     1024000   83  Linux
  /dev/sda6   *        2296        3917    13022208   83  Linux

2행의 정보를 보면 '헤드 255, 섹터 63, 실린더 3916' 를 확인할 수 있다. 디스크 장치의 실린더, 헤드, 섹터의 정보를 종합해서 CHS 혹은 기하학 정보라고 부른다.

LBA 방식

현재의 하드 디스크는 LBA(Logical Block Addressing, 논리 블록 어드레스) 방식으로 접근하는 것이 보통이다. 이것은 하드 디스크의 전체 섹터에 0부터 일련번호(섹터번호)를 붙여서 이 번호를 이용하여 접근하는 섹터를 지정하는 단순한 방식이다.

섹터 번호와 물리적 섹터 위치를 짝 지우는 일은 하드 디스크의 컨트롤러에 내장된 펌웨어가 담당한다. 보통은 섹터 번호가 작을수록 디스크 외측의 영역을 사용하도록 되어 있다. 그리고 파티션의 시작 위치와 종료 위치도 섹터 번호로 지정한다.

fdisk의 -lu 옵션을 이용해 LBA 방식의 진짜 파티션 모습을 확인할 수 있다.

fdisk -lu /dev/sda

  Disk /dev/sda: 32.2 GB, 32212254720 bytes
  255 heads, 63 sectors/track, 3916 cylinders, total 62914560 sectors
  Units = sectors of 1 * 512 = 512 bytes
  Sector size (logical/physical): 512 bytes / 512 bytes
  I/O size (minimum/optimal): 512 bytes / 512 bytes
  Disk identifier: 0x00060d5a

     Device Boot      Start         End      Blocks   Id  System
  /dev/sda1            2048    20482047    10240000   83  Linux
  /dev/sda2        20482048    30722047     5120000   83  Linux
  /dev/sda3        30722048    34818047     2048000   82  Linux swap / Solaris
  /dev/sda4        34818048    62914559    14048256    5  Extended
  /dev/sda5        34820096    36868095     1024000   83  Linux
  /dev/sda6   *    36870144    62914559    13022208   83  Linux

각 파티션의 시작점과 종료점이 섹터 번호로 표시된다. 실제 파티션은 이 섹터 번호로 구분된 범위가 된다. 제 1파티션은 2048섹터 부터 시작하므로 0~1047섹터는 파티션으로 사용되지 않음을 알 수 있다. 디스크의 처음에 MBR이 있고, 그 바로 뒤의 영역에 GRUB의 스테이지 1.5가 기록된다. 위의 경우에서는 0섹터가 MBR이고, 스테이지 1.5는 그 뒤의 2047섹터까지의 영역을 사용하고 있음을 알 수 있다.

현재 하드 디스크는 LBA 방식으로 접근하므로 거기에 맞추어 파티션 시작 위치와 종료 위치를 지정한다. 이것은 전혀 문제가 없는 방식이지만, fdisk 커맨드는 오래된 CHS 방식 디스크도 대응하기 때문에 디폴트로 CHS 방식 디스크 정보를 표시한다.

이때 LBA 방식의 디스크에 대해 가공의 기하학 구조(헤드 수, 섹터 수, 실린더 수)를 설정하여 파티션의 시작, 종료 위치의 섹터 번호를 무리하게 실린더 번호와 바꾸려고 한다. 더욱이 파티션 종료 위치가 가공의 기하학 실린더 종료 위치에 잘 맞지 않았을 경우, '파티션 1은 실린더 경계에서 종료되지 않았습니다'와 같은 메시지가 표시된다. 이는 '파티션이 실린더 단위로 나위어 졌다는 전제로 움직이는 OS로 이 디스크를 사용하면 문제가 생길 수 있습니다'라는 의미의 경고이다. 리눅스만 사용하는 디스크에게는 불필요한 경고일 뿐이다.

파티션 테이블

파티션의 시작/종료 위치 정보는 파티션 테이블을 참조한다. 정확하게 표현하자면 , 0섹터에 있는 MBR의 446~509바이트 부분이 파티션 테이블이다. MBR의 0~455바이트는 부트스트랩 로더(bootstrap loader), 즉 서버 가동 시작 때 BIOS가 읽어 와서 실행하는 GRUB 스테이지 1이 기록되어 있다.

1섹터의 사이즈는 512바이트이므로 510~511바이트(0바이트가 첫 부분이므로 511바이트)의 2바이트가 남는데, 여기는 0xAA55라는 값이 기록된다. 이 값이 다른 디스크는 MBR이 망가져 있다고 보면 된다.

파티션 테이블의 사이즈는 64바이트 밖에 없으므로 많은 정보를 담을 수는 없다. 대표적인 정보로서 각 파티션에 대한 'CHS 방식으로 표현한 파티션의 시작 위치와 종료위치', 'LBA 방식으로 표현한 파티션 시작 위치와 섹터 수'를 기록한다(종료 위치의 섹터 번호는 기록하지 않는다. 시작위치에 섹터 수를 더해서 계산).

파티션의 시작/종료 위치의 정보가 두 가지 방식으로 기록되는 데는 역사적 이유가 있다. LBA 방식 디스크에는 실제로 CHS 방식의 파티션 정보가 사용되지 않는다.

아래는 MBR의 446바이트부터 66바이트만큼(파티션 테이블 혹은 마지막 2바이트)을 hexdump 명령어로 덤프 출력한 것이다. 옵션을 붙여 LBA 방식의 파티션 정보만 10진수로 표시하고 있다.

# hexdump -s 446 -n 66 -e '8/1 "%02x " 2/4 "%10d " "\n"' /dev/sda

00 20 21 00 83 fe ff ff        2048   20480000

00 fe  ff   ff  83 fe ff ff  20482048   10240000

00 fe  ff   ff  82 fe ff ff  30722048    4096000

00 fe  ff   ff  05 fe ff ff  34818048   28096512

55 aa


부트 플래그 / 시작 위치(CHS 방식) / 파티션 ID / 종료 위치(CHS 방식) / 시작 위치(섹터 번호, LBA 방식) / 섹터 수(LBA 방식)


CHS 방식은 실제로 사용하지 않으니 의미가 없고 마지막 2바이트는 0xAA55 이다. 일단 이것으로 LBA 방식이 간단하고 편리한 방식이라는 것을 알 수 있지만 최근에는 LBA 방식의 한계가 보이기 시작했다. 시작위치의 섹터번호와 전체 섹터 수를 표시하는 숫자는 모두 4바이트, 즉 0x00000000~0xFFFFFFFF의 범위밖에 사용할 수 없다. 즉, 섹터 수가 0xFFFFFFFF를 넘는 대용량 디스크는 지원하지 않는다. 1섹터는 512바이트라는 것을 고려하여 16진수 대응의 계산기로 계산해 보면 2TB라는 답이 나온다. 그러므로 MBR 안의 파티션 테이블을 사용하는 한 2TB 이상의 하드 디스크에 파티션을 작성하는 것은 불가능하다.

이 문제를 해결하기 위해 등장한 것이 GPT(GUID Partition Table)이다. 현재는 GPT를 사용하는 하드 디스크에서 OS를 기동할 때는 UEFI라고 하는 규격에 대응한 OS가 필요하다. 또한 최근의 대용량 하드 디스크에서는 4KB 섹터가 채용된 경우도 있다.

UEFI

UEFI는 이전 시스템인 BIOS의 후속작이다. 시스템 BIOS는 설계상 메모리를 1MB밖에 사용할 수 없다. 시스템 BIOS의 설정화면이 GUI가 아니라 엄청 심플한 텍스트 기반인 이유가 여기에 있다. UEFI에서는 이런 시스템 BIOS의 제한을 없애고 많은 기능을 확장하고 있다. 그 결과, UEFI에 대응하는 서버에서는 서버 가동 때의 UEFI 설정 화면으로부터 갖가지 디바이스 설정화면을 직접 부를 수 있도록 되어 있다. 심지어 GUI 설정 화면을 가진 서버도 등장하였다.

부트스트랩 로더의 로딩 방법도 변경되어 종래의 GRUB 스테이지 1, 스테이지 1.5, 스테이지 2와 같이 단계적으로 부트 로더를 가동할 필요가 없어졌다. GPT 방식으로 작성된 'EFI 시스템 파티션'에 저장된 부트 로더를 직접 가동하는 것도 가능하다.

GPT

기존의 파티션 테이블은 0섹터의 MBR에 저장돼 있지만, GPT는 1섹터부터 33섹터에 기록하는 새로운 타입의 파티션 테이블이다. 아래 표에 기존의 파티션과의 중요 차이점을 정리해 두었다.

  기존 파티션 GPT
파티션 테이블 장소 MBR MBR 바로 뒤(디스크 끝에 복제)
최대 디스크 용량 2TB 8ZB(실제로는 무한대)
최대 파티션 수 15(SCSI 디스크의 경우) 128
파티션 라벨 파티션 ID(용도를 표시하는 ID) GUID(용도를 표시하는 ID+유니크ID)
파티션 작성 툴 fdisk parted

파티션 테이블이 망가질 때를 대비해서 같은 내용이 하드 디스크의 끝에도 기록되어 있다. GPT 헤더에는 파티션으로 이용할 수 있는 섹터의 범위가 기록되어 있다.

GPT의 구조

각 파티션 정보는'파티션 테이블 엔트리'에 기록된다. 하나의 파티션 테이블 엔트리는 128바이트이므로 1섹터(512바이트)에 4개의 파티션 정보가 기록된다. 각 파티션의 시작 섹터와 종료 섹터를 나타내는 수치는 8바이트로 기록되므로, 2TB 이상의 하드 디스크 섹터 수에도 여유를 가지고 대응할 수 있다.

각 파티션에는 GUID라고 하는 고유의 라벨이 기록되며 특히 부트 로더를 저장하는 파티션에는 'EFI 시스템 파티션(ESP)'의 라벨이 붙여진다. RHEL6의 경우에는 /boot/efi에 마운트되는 파일 시스템이 ESP이다. 이 아래에 스테이지 2에 해당하는 GRUB(grub.efi)를 저장한다. 따라서 GRUB 스테이지 1과 스테이지 1.5는 필요가 없다. ESP는 VFAT 형식으로 포맷해 둔다.

GPT 형식으로 파티션을 작성할 때는 parted 명령어를 사용한다.

REHL6는 인스톨러도 GPT 대응이므로 2TB 이상의 하드 디스크에 인스톨할 때 자동으로 GPT 형식의 파티션이 생성된다.

4KB 섹터 디스크

기존의 하드 디스크는 전통적으로 1섹터의 사이즈가 512바이트로 정해져 있다. 하지만 대용량 디스크는 섹터의 사이즈가 커지므로 섹터 단위 접근의 오버헤드를 줄일수 있다. 그리고 하드 디스크 내부에서는 섹터마다 에러 체크용 정보가 저장되어 있다. 섹터 사이즈를 크게 함으로써 부가 정보가 차지하는 비율을 줄이고 기록 영역을 더욱 효과적으로 활용할 수 있게 된다.

그렇지만 하드 디스크에 접근하는 측의 서버, 하드웨어, OS는 512바이트의 섹터 크기를 전제로 설계되어 있다. 단순히 하드디스크 측의 섹터 사이즈를 크게 한다고 해결될 문제가 이니다. 이 문제를 해결하기 위해 많은 연구를 한 뒤 찾아낸 방식이 하드 디스크에 내장된 컨트롤러가 논리적으로 512바이트의 섹터를 에뮬레이션하는 방식이다.

아래의 그림은 서버에서는 기존 방식과 동일한 512바이트의 섹터가 존재하는 것처럼 보이지만, 실제로 데이터가 읽고 써질 때는 4KB 사이즈의 섹터 단위가 사용된다. 최근 몇몇 하드 디스크 제작 회사에서 이 방식을 채용하기 시작했다.

4KB 섹터 디스크의 구조

이것은 사이즈를 교환하기 위한 오버헤드가 발생하는 것처럼 보이지만 4KB 섹터 가운데 논리 섹터만을 읽고 쓰는 경우, 512바이트의 데이터를 읽고 쓰기위해 4KB 데이터의 읽고 쓰기가 발생한다. 하지만 실제로 읽고 쓰기 패턴이 4KB 섹터의 처음부터 시작해 어느 정도 큰 사이즈의 데이터를 다룰 경우에 이런 오버헤드는 사라진다.

예로 제1파티션 /dev/sda1 이 2048섹터부터 시작된다면 위 그림의 논리 섹터에서 설명한 것이라고 하면 딱 256번째의 4KB 섹터 시작점과 일치한다. 파티션의 섹터 수도 8의 배수이므로 파티션의 종료 위치도 딱 4KB섹터의 마지막 지점이 된다.

장래에 4KB 섹터를 채용한 하드 디스크가 늘어날 것을 고려해서 기본적으로 파티션의 시작 지점과 사이즈는 논리 섹터 수로 생각한 8의 배수로 준비해 두는 것이 좋다. 이 작업을 '파티션의 정렬(alignment)'이라고 한다.

parted 명령어로 GPT 형식의 파티션을 생성할 때는 기본적으로 파티션의 시작 위치와 종료 위치를 용량으로 지정한다. 이때 parted 명령어는 자동으로 파티션을 정렬하며 생성한다. 그래도 파티션이 걱정될 경우에는 'unit s' 명령어로 파티션의 시작 위치와 종료 위치를 섹터 단위로 표시/지정해 두면 된다.

※ 4KB 섹터 디스크에는 '섹터정렬(alignment of sector)'이라고 불리는 기능이 있는데, 이것이ON일 경우 파티션의 시작 위치를 '8의 배수 + 7' 섹터로 할 필요가 있다. 리눅스에서는 필요 없는 기능이므로 하드 디스크의 점퍼 스위치등으로 바꿀 수만 있다면 OFF로 설정하는 것이 좋다.