엔디언(endian)과 바이트 오더(byte order)
2023년 04월 30일
Wiki
📕 목차
엔디언이란?
엔디언(Endianness)은 컴퓨터의 메모리와 같은 1차원의 공간에 여러 개의 연속된 대상을 배열하는 방법을 뜻합니다. 엔디언의 종류는 다음과 같습니다.
- 빅 엔디언(Big-endian): 큰 단위가 앞에 나오는 방식
- 리틀 엔디언(Little-endian): 작은 단위가 앞에 나오는 방식
- 미들 엔디언 (Middle-endian): 두 경우에 속하지 않거나 둘을 모두 지원하는 것
유래
엔디언이라는 단어는 조너선 스위프트의 《걸리버 여행기》에 나오는 소인국 릴리퍼트 이야기에서 달걀을 깰 때 뭉툭한 끝(big-end)을 먼저 깨는 사람들(빅 엔디언)과 뾰족한 끝(little-end)을 먼저 깨는 사람들(리틀 엔디언) 사이에 격론이 벌어진 데서 따왔다고 합니다.
빅 엔디언과 리틀 엔디언 중 어느 것을 쓰느냐 하는 문제는 상황에 따라서 임의적이어서, 따라서 종종 논란의 대상이 되었다고 합니다. 코딩에서 중괄호를 올릴 것이냐, 내릴 것인가와 같은 논쟁과 비슷한 느낌인 것 같습니다.
컴퓨터와 관련되어 엔디언이라는 말은 대니 코언(Danny Cohen)이 이런 플레임을 잠재우기 위해 1980년에 쓴 On Holy Wars and a Plea for Peace라는 글에서 유래했습니다.
바이트 오더
바이트 오더는 여러 개의 바이트를 어떤 순서로 배열할지를 규정하는 규칙입니다.
이에 따라 바이트 오더도 엔디언으로 표현할 수 있습니다. 컴퓨터에서는 시스템 혹은 네트워크 간의 데이터 저장 및 전송은 주로 바이트 단위로 처리합니다. 이 규칙이 없으면 데이터를 저장하거나 전송할 때 서로 다른 시스템 사이에서 데이터 표현이 일치하지 않아서 문제가 발생할 수 있습니다.
예를 들어, 다른 CPU 아키텍처를 사용하는 두 시스템 간에 데이터를 전송하는 경우, 각 시스템에서 데이터를 표현하는 방식이 다르기 때문에 전송된 데이터가 해석될 때 오류가 발생할 수 있습니다. 이를 방지하기 위해 바이트 오더를 규정하여 데이터 표현 방식을 통일시킵니다.
또한, 바이트 오더는 네트워크 프로토콜에서도 중요한 역할을 합니다. 인터넷에서는 다양한 시스템에서 데이터를 전송하고 받습니다. 따라서 네트워크 프로토콜에서는 바이트 오더를 규정하여 데이터를 전송하는 시스템 간에 데이터 표현 방식이 일치하도록 합니다.
따라서 바이트 오더는 서로 다른 시스템 사이에서 데이터를 일관성 있게 저장하고 전송하는 데 필요한 중요한 규칙입니다.
호스트 바이트 오더 (Host Byte Order) 와 네트워크 바이트 오더 (Network Byte Order)
바이트 오더 개념을 공부하면서 중요하게 생각되는 것은 바로 호스트 바이트 오더와 네트워크 바이트 오더입니다. 이 둘은 서로 다른 개념이므로 구분하여 이해해야 합니다.
호스트 오더 바이트는 현재 사용하는 컴퓨터의 바이트 오더 방식을 의미합니다.
예를 들어, x86 아키텍처에서는 리틀 엔디언 방식을 사용하고, SPARC 아키텍처에서는 빅 엔디언 방식을 사용합니다. 따라서 호스트 오더 바이트는 사용 중인 컴퓨터의 아키텍처에 따라 달라집니다.
반면에 네트워크 오더 바이트는 데이터를 전송하기 위한 네트워크에서 사용되는 바이트 오더 방식입니다.
네트워크 오더 바이트는 언제나 빅 엔디언 방식을 사용합니다.
따라서, 호스트 오더 바이트와 네트워크 오더 바이트 간의 변환 작업이 필요할 수 있습니다. 예를 들어, 데이터를 송신하기 전에 호스트 오더 바이트를 네트워크 오더 바이트로 변환하고, 데이터를 수신한 후에는 네트워크 오더 바이트를 호스트 오더 바이트로 변환해야 합니다. 이러한 변환 작업은 일반적으로 프로그래밍 언어나 라이브러리에서 제공하는 함수를 이용하여 수행됩니다.
바이트 오더를 사용하는 곳은?
바이트 오더는 컴퓨터 시스템에서 데이터를 저장하고 전송하는 방식을 의미하므로, 다양한 분야에서 사용됩니다. 몇 가지 예를 들어보면 다음과 같습니다.
-
⭐️ 네트워크 프로토콜 ⭐️: 네트워크에서 데이터 전송을 위해 사용되는 프로토콜인 TCP/IP, UDP 등에서는 데이터 전송을 위해
빅엔디언 방식
을 주로 사용합니다. 위에서 언급한 네트워크 바이트 오더와 연관이 있습니다. -
파일 형식: 다양한 파일 형식에서도 바이트 오더를 사용합니다. 예를 들어, BMP 이미지 파일의 헤더 정보는 빅엔디언 방식으로 저장되며, WAV 오디오 파일에서는 리틀엔디언 방식을 사용합니다.
-
데이터베이스: 데이터베이스에서도 바이트 오더를 사용합니다. 예를 들어, Oracle 데이터베이스는 네이티브 바이트 오더를 사용하며, PostgreSQL에서는 리틀엔디언 방식을 지원합니다. MySQL도 리틀 엔디언 방식을 사용합니다.
-
⭐️ 마이크로프로세서 ⭐️: 마이크로프로세서에서도 바이트 오더가 중요한 역할을 합니다. CPU에서 사용하는 레지스터의 바이트 오더는 CPU 아키텍처에 따라 다르며, 이에 따라 메모리와의 데이터 전송 방식도 달라집니다. 위에서 언급한 호스트 바이트 오더와 주로 연관이 있습니다.
사례
네트워크 프로토콜의 바이트 오더
TCP/IP: 빅엔디언 방식
TCP/IP 프로토콜에서는 빅엔디언 방식을 사용합니다. 예를 들어, IP 주소와 포트 번호는 4바이트의 빅엔디언 형식으로 표현됩니다. 이외에도 TCP/IP 헤더의 여러 필드에서 빅엔디언 방식이 사용됩니다.
예시) IP 주소가 192.168.0.1이고, 포트 번호가 80일 떄의 TCP/IP 헤더
45 00 00 3c 12 34 00 00 80 06 00 00 (c0 a8 00 01)
00 00 00 00 (00 00 00 00) 00 00 00 00 00 00 00 00
50 02 20 00 (23 45) 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
위 예시에서 IP 주소와 포트 번호는 다음과 같이 표현됩니다.
- 출발지 IP 주소: c0 a8 00 01 (192.168.0.1) | 목적지 IP 주소: 00 00 00 00
- 출발지 포트 번호: 23 45 | 목적지 포트 번호: 00 50 (80)
TCP/IP 프로토콜은 데이터 전송 시 사용되는 패킷의 크기를 일반적으로 1바이트 단위로 전송합니다. 따라서 TCP/IP 프로토콜에서는 워드(2바이트) 또는 롱 워드(4바이트)를 사용하는 것이 아니라, 각각의 바이트(byte)를 순서대로 전송합니다. 이는 TCP/IP 프로토콜이 바이트 오더(Byte Order)에 대한 문제를 고려하지 않아도 되는 이점을 가지고 있습니다.
HTTP/1.1 프로토콜의 바이트 오더 - 평문 기반
HTTP는 주로 평문 문자열을 인코딩해서 전송하기 때문에 바이트 오더의 개념이 적용되지 않습니다.
HTTP/2 프로토콜의 바이트 오더 - 빅 엔디언 (big endian)
HTTP/2 프로토콜은 HTTP/1.1과 다르게 이진 프레이밍 계층
(Binary framing layer)를 사용하여 데이터를 전송합니다. 이진 프레임은 여러 개의 프레임으로 나뉘며 각각은 프레임 헤더와 프레임 데이터로 구성됩니다.
여기서는 데이터가 전송을 위한 바이트 오더 방식을 살펴보기 위해, 프레임 헤더의 바이트 오더 방식을 살펴보겠습니다. 프레임 헤더는 프레임 타입, 프레임 길이 등으로 구성됩니다.
+-----------------------------------------------+
| Length (24) |
+---------------+---------------+---------------+
| Type (8) | Flags (8) |
+-+-------------+---------------+-------------------------------+
|R| Stream Identifier (31) |
+=+=============================================================+
| Frame Payload (0...) ...
+---------------------------------------------------------------+
프레임 헤더의 첫 3바이트(Length)는 프레임 데이터의 길이를 나타냅니다. 이 값은 빅엔디언
으로 인코딩되어 있습니다. 예를 들어, 길이가 16384(0x4000)바이트인 경우, Length 필드의 값은 다음과 같이 인코딩됩니다.
00 00 40 00
16384 바이트는 이진수 0100000000000000로 나타내고 이를 16진수로 표현하면 4000입니다.
HTTP/2 프로토콜에서 Length 필드의 크기는 1 word(16 bit)로 표현되므로 2 바이트를 사용합니다. 따라서 Length 필드의 값은 0x4000이 됩니다.
HTTPS의 경우 HTTP 버전에 따른 바이트 오더 방식을 따릅니다.
파일 형식과 바이트 오더
JPEG 이미지: 대부분 빅엔디언 방식을 사용합니다.
JPEG 파일은 압축된 이미지 파일이기 때문에 바이트 오더의 개념이 적용되는 구조는 크게 두 가지로 나뉩니다.
-
JFIF 파일 포맷
JFIF(JPEG File Interchange Format)는 JPEG 이미지를 위한 파일 포맷 중 하나로, 이미지 파일에 대한 정보와 썸네일, 색공간, 화소 데이터 등을 포함하고 있습니다.
JFIF 파일 포맷은 이미지 데이터를 포함하는 애플리케이션 데이터 부분에서는 사용하는 바이트 오더가 정의되어 있지 않습니다.
-
JPEG 데이터 스트림
JPEG 이미지 파일은 보통 JPEG 데이터 스트림으로 구성되어 있습니다. JPEG 데이터 스트림은 각각의 세그먼트(segment)로 구성되어 있고, 세그먼트의 타입에 따라 구조가 다릅니다.
각 세그먼트에서는 일반적으로 빅엔디언 방식이 사용됩니다.
예를 들어, SOI 세그먼트(Start of Image)는 FF D8로 시작하며, SOS 세그먼트(Start of Scan)는 FF DA로 시작합니다. 이러한 세그먼트에서 데이터의 길이 정보를 나타내는 필드는 빅엔디언으로 저장됩니다.
아래는 JPEG 파일의 일부를 16진수로 표현한 예시입니다.
FF D8 FF E0 00 10 4A 46 49 46 00 01 01 01 00 60
00 60 00 00 FF DB 00 43 00 08 06 06 07 06 05 08
위 예시에서 FF D8은 SOI 세그먼트를 나타내며, FF E0은 APP0 세그먼트를 나타냅니다. 이 중 APP0 세그먼트에서는 세그먼트 길이와 함께 JFIF 파일 포맷에 대한 정보를 포함합니다. 이 부분에서 사용되는 바이트 오더는 정의되어 있지 않습니다. 그 이후의 데이터에서는 일반적으로 빅엔디언 방식이 사용됩니다.
- GIF 이미지: 대부분 리틀엔디언 방식을 사용합니다.
- PNG 이미지: 네트워크 바이트 오더를 따르며, 즉 빅엔디언 방식을 사용합니다.
- PDF 문서: 리틀엔디언 방식을 사용합니다.
데이터베이스의 바이트 오더
- Oracle: 대부분 빅엔디언 방식을 사용합니다.
- MySQL: 리틀엔디언 방식을 사용합니다.
- PostgreSQL: 네트워크 바이트 오더를 따르며, 즉 빅엔디언 방식을 사용합니다.
- SQL Server: 리틀엔디언 방식을 사용합니다.
- MongoDB: 네트워크 바이트 오더를 따르며, 즉 빅엔디언 방식을 사용합니다.
주의: 데이터베이스가 저장하는 데이터의 형태와 사용하는 바이트 오더는 데이터베이스 버전, 데이터 타입, 설정 등에 따라 다를 수 있습니다.
CPU 아키텍처의 바이트 오더
- x86 아키텍처: 리틀 엔디언(Little endian)
- ARM 아키텍처: 리틀 엔디언(Little endian)
- ARMv6 부터는 빅엔디언 방식도 지원
- M1 아키텍처: 리틀 엔디언(Little endian)
- SPARC 아키텍처: 빅 엔디언(Big endian)
- PowerPC 아키텍처: 빅 엔디언(Big endian)
- PowerPC 970 이후부터는 리틀 엔디언 방식도 지원
- MIPS 아키텍처: 빅 엔디언(Big endian)
- MIPS32r6/MIPS64r6 이후부터는 리틀 엔디언 방식도 지원
주의: 마이크로프로세서의 아키텍처는 여러 가지가 있으며, 특정 아키텍처에서도 다른 바이트 오더를 사용할 수 있습니다. 또한, 마이크로프로세서의 종류와 모델에 따라 바이트 오더가 다를 수 있습니다.