rubrapack 매뉴얼←↑→

21 비트, 바이트, 16진수

이 부는 매뉴얼의 나머지가 딛고 선 개념을 처음부터 설명한다. 컴퓨터를 쓸 줄 안다는 것 말고는 아무것도 가정하지 않는다. 각 장의 끝에는 그 개념이 튜토리얼과 제4부의 어디에 쓰이는지 적었다.

21.1 비트#

컴퓨터가 저장하는 모든 것 - 프로그램, 사진, 이 매뉴얼, 설치 파일 - 은 결국 비트의 긴 줄이다. 비트는 값이 둘이고 0 과 1 로 쓴다(하드웨어에서는 전하가 없음과 있음, 자기의 한 방향과 반대 방향). 비트 하나는 예/아니요 질문 하나에 답할 수 있다.

비트 여럿을 모으면 더 많은 값을 구별할 수 있다. 비트 하나를 더할 때마다 가짓수가 두 배가 된다:

비트 수서로 다른 값전부
120 1
2400 01 10 11
38000 001 010 011 100 101 110 111
825600000000 ... 11111111
1665,536
324,294,967,296

n 비트로는 2 x 2 x ... x 2(n 번) = 2^n 가지 값이 있다.

21.2 2진수#

비트의 줄은 10진수 숫자를 읽듯 수로 읽을 수 있다. 10진수에서는 한 자리가 오른쪽 자리의 열 배다: 305 는 3 x 100 + 0 x 10 + 5 x 1 이다. 2진수(밑이 2)에서는 한 자리가 오른쪽 자리의 두 배다:

  bit:      1    0    1    1    0    0    1    0
  worth:  128   64   32   16    8    4    2    1
          ---        --   --              -
          128    +   32 + 16        +     2      = 178

그래서 2진수 10110010 은 10진수로 178 이다. 값이 1 인 맨 오른쪽 비트를 비트 0, 이 여덟 개 중 값이 128 인 맨 왼쪽 비트를 비트 7 이라고 부른다. 비트 k 의 값은 2^k 다.

21.3 바이트#

비트는 여덟 개씩 묶여 바이트가 된다. 바이트는 파일을 이루는 가장 작은 조각이다: 32,768 바이트인 파일 - 튜토리얼의 첫 hello.msi 크기 - 은 32,768 x 8 = 262,144 비트다. 바이트 하나는 0 부터 255 까지 256 가지 값 중 하나를 담는다.

바이트가 무엇을 뜻하는지는 전적으로 프로그램이 그것을 어떻게 읽느냐에 달렸다: 같은 바이트 01001000 이 수 72 이기도 하고, 텍스트 파일에서는 글자 H(3장)이기도 하고, 더 큰 수의 일부(2장)이기도 하고, 프로그램 안에서는 기계 명령의 일부이기도 하다. 파일은 그것을 말해 주지 않는다. 말해 주는 것은 파일의 형식이다. 이 매뉴얼의 제4부가 그런 형식의 설명이다.

21.4 16진수#

2진수는 쓰기에 길고 읽기에 어렵다. 10진수는 바이트와 줄이 맞지 않는다. 그래서 바이트는 보통 16진수(밑이 16, "hex")로 쓴다: 숫자 0-9 다음에 10 부터 15 를 뜻하는 A-F 를 쓴다. 16진수 한 자리는 정확히 4비트이므로, 바이트 하나는 정확히 16진수 두 자리다:

10진수2진수16진수10진수2진수16진수
000000810008
100011910019
200102101010A
300113111011B
401004121100C
501015131101D
601106141110E
701117151111F

바이트를 16진수로 바꾸려면 여덟 비트를 넷씩 둘로 나눠 각각 찾는다:

  10110010
  1011 0010
     B    2      -> B2

거꾸로: 16진수 B2 는 B x 16 + 2 = 11 x 16 + 2 = 178 이다. 같은 수를 세 가지로 쓰면 178, 10110010, B2.

수가 16진수임을 밝히려고 프로그래머는 앞에 0x 를 붙이거나(0xB2), 뒤에 h 를 붙이거나(B2h), 그냥 말로 밝힌다. 이 매뉴얼은 바이트를 사이에 빈칸을 둔 16진수 두 자리로(B2 00 FF), 16진수로 적은 수는 0x 를 붙여 쓴다: 0x1000 은 4096 이다.

Windows 계산기의 프로그래머 모드는 10진수, 16진수, 2진수를 서로 바꿔 준다: 하나에 수를 넣고 나머지를 읽는다.

21.5 16진 덤프 읽기#

16진 덤프는 파일의 바이트를 한 줄에 열여섯 개씩 16진수로 보인다. 튜토리얼의 첫 hello.msi 의 처음 64 바이트다:

offset    bytes                                            as text
00000000  d0 cf 11 e0 a1 b1 1a e1 00 00 00 00 00 00 00 00  ................
00000010  00 00 00 00 00 00 00 00 3e 00 04 00 fe ff 0c 00  ........>.......
00000020  06 00 00 00 00 00 00 00 01 00 00 00 01 00 00 00  ................
00000030  01 00 00 00 00 00 00 00 00 10 00 00 02 00 00 00  ................

처음 여덟 바이트 d0 cf 11 e0 a1 b1 1a e1 은 모든 .msi 가 쓰는 복합 파일 형식의 서명이다 - 상상력을 조금 보태 읽으면 "DOCFILE". 모든 파일 형식은 이런 고정된 무늬, 매직 넘버로 시작해서 프로그램이 파일의 정체를 알아볼 수 있다: 프로그램(.exe)은 4d 5a("MZ"), ZIP 압축 파일과 그래서 MSIX 는 50 4b 03 04("PK"), 캐비닛은 4d 53 43 46("MSCF")로 시작한다. 파일 이름은 거짓말을 할 수 있어도 첫 바이트들은 좀처럼 그러지 않는다.

직접 이렇게 보려면 PowerShell 에서:

PS C:\work\hello> Format-Hex hello.msi | Select-Object -First 4

(배치가 위와 조금 다르지만 바이트는 같다.)

21.6 단위: KB, MB 와 KiB, MiB#

크기는 바이트로 세고, 큰 수에는 접두어를 붙인다. 접두어는 두 벌이 있고, 둘을 헷갈리기 쉽다:

10진(SI)바이트2진(IEC)바이트
1 kB (킬로바이트)1,0001 KiB (키비바이트)1,024 = 2^10
1 MB (메가바이트)1,000,0001 MiB (메비바이트)1,048,576 = 2^20
1 GB (기가바이트)1,000,000,0001 GiB (기비바이트)1,073,741,824 = 2^30
1 TB (테라바이트)10^121 TiB (테비바이트)2^40

2진 크기가 쓰이는 것은 메모리와 파일 형식이 2의 거듭제곱으로 일하기 때문이다. 위의 덤프에도 하나 있다: 오프셋 0x1E 에 복합 파일 머리가 0c 00, 곧 수 12 를 저장하고, 그 섹터의 길이는 2^12 = 4096 바이트(4 KiB, 0x1000) 다 - 형식이 크기가 아니라 지수를 저장한다. MSIX 블록 맵의 한 항목은 64 KiB 를 덮는다. Windows 탐색기는 "KB", "MB" 라고 쓰지만 뜻은 KiB, MiB 다. 디스크 제조사는 10진 단위를 뜻한다. 그래서 "1 TB" 디스크가 931 GB 로 보인다.

rubrapack 은 한도가 2의 거듭제곱일 때 2진 단위를 쓴다: cab-max-size 는 MiB 이고, Windows Installer 의 .msi 한도는 2 GiB(2,147,483,648 바이트)다. 이 매뉴얼이 프로그램 크기를 "MB" 로 쓸 때("200 MB 프로그램")는 대략의 크기이고, 그 차이는 중요하지 않다.

21.7 쓰이는 곳#