바이트 순서, 리틀 엔디언, 빅 엔디언

이 글은 2009년에 제가 idluser.org 게시판에 IDL Tips 게시판에 올렸던 내용입니다. BYTEORDER 명령에 대한 질문에 대해 좀 길게 답변을 올렸던 것이고, 몇 년 지났다고 바뀌는 내용은 아니어서 그대로 이곳에 옮겨 봅니다. “몇 년 전만 해도 모토롤라 계열의 CPU가 꽤 많이 씌였습니다”라는 문장이 있는데, 이 부분이 이제 “10년 전”으로 바뀌면 더 적당할 것 같긴 합니다만, 어차피 이나 저나 옛날 얘깁니다.

바이트 순서(byte order). 리틀 엔디언(little endian), 빅 엔디언(big endian) (이종혁. 2009. 8. 12.)

복잡한 내용일 수도 있고, 실제로는 간단히 해결할 문제일 수도 있다고 봅니다. 

지금은 거의 인텔 CPU와 그 호환 CPU가 대부분의 시장을 장악하고 있지만, 몇 년 전만 해도 모토롤라 계열의 CPU가 꽤 많이 쓰였습니다. 이 두가지 CPU(인텔계/모토롤라계)는 데이터 처리 방식이 서로 반대였습니다. 저도 내막을 잘은 모르지만, CPU 설계가 애초에 그랬을 수도 있고 둘이 서로 어쨌든 다르게 만들어 보려고 그런 것일 수도 있습니다. 

일반 사용자 입장에서 이용하는 데에는 거의 이런 차이가 티가 안납니다. 단, 데이터를 저장하고, 읽어오는 중에는 일반 사용자들도, “어라 이거봐라”하는 문제가 보일 수 있는 것이고, 이것이 아마도 제가 생각하기에 현재 남아있는 byte order 문제가 아닐까 생각합니다. 

말 그대로, “바이트의 순서”입니다. 영 다른 얘기일 수 있지만, 10진수의 예를 들어보면, A라는 CPU는 157이라고 기록한 것을 B라는 CPU는 751이라고 기록합니다. 둘은 같은 숫자를 의미하는 것이고 최종 사용자는 같은 값을 보아야 하는데, 데이터 저장은 하여간 거꾸로 됩니다. 이 경우 1자리 수는 전혀 문제가 안되겠지요? 5를 뒤집어 써봐야 5니까요.

이제 다시 컴퓨터 얘기로 돌아오면, 바이트는 8개의 비트로 이루어진 최소의 자료 전송/저장 단위입니다. 컴퓨터에서 1바이트면 0~255 사이의 정수를 하나 기록할 수 있습니다. 그 이상의 숫자를 생각해 보면요, 두 바이트가 주어지면 -32768~32767 사이의 정수를 기록할 수가 있게 됩니다. 4바이트가 주어지만 -20억~+20억 정도의 정수를 기록할 수 있습니다. (실수는 얘기가 좀 다릅니다. 예로 들기도 좀 복잡하구요. 나중에 처리는 모두 byteorder 등의 IDL 문장으로 간단히 처리되므로 예는 쉬운 것만 들도록 하지요).

1바이트짜리 데이터는 문제가 안됩니다. 뒤집어도 그놈이 그놈이니까요. 2바이트짜리 데이터는 뭔가 좀 문제가 됩니다. 515라는 숫자를 예를 들어 보면요, 이는 바이트 진법에서 2*256+3 으로 분해할 수 있습니다. 10진법에서 456을 4*10^2+5+10+6으로 분해하는 것과 같은 방법이라고 생각하면 되겠습니다. 그러면 컴퓨터는 515=2*256+3 이므로 2와 3을 기록해 두어야 515라는 데이터를 복원해 낼 수 있습니다. 2와 3입니다. 이 두 숫자를 어떤 순서로 기록하느냐에 따라 영 다른 값이 나옵니다. 이를 잘못 읽어서 (2)*256+(3)이 아닌 (3)*256+(2)로 생각을 한다면 그 값은 770입니다. 영 다른 수입니다. 그런데 두 가지 CPU 에서 이를 서로 다른 방법으로 저장하기 때문에 혼란이 발생하는 것입니다. 에휴.

BYTEORDER 라는 프로시저를 질문하셨는데요, 더 간단해 보이는 함수로 SWAP_endian() 이 있습니다. 이를 이용해 테스트를 해 보세요.

IDL> print, SWAP_endian(770)
     515
IDL> print, swap_endian(515)
     770

엔디언이라는 용어가 또 등장하는데요, 걸리버 여행기에 나오는 소인국/대인국이 서로 거꾸로였던 내용을 따와서, 한쪽 CPU는 little endian, 다른 한쪽 CPU는 big endian이라고 부릅니다. 그냥 그렇답니다. 저도 그렇게 깊은 근원까지는 파고 들어가는 스타일 아닙니다. 

리틀 엔디언이 지금 우리가 보통 사용하는 인텔 CPU 계열입니다. 빅 엔디언이 예전 유닉스 머신들(Sun Sparc, HP-UX, IBM-AIX) 그리고 예전의 Mac 등이 있습니다. 주의하실 점은 이는 운영체제를 타는 특성이 아니라 CPU를 타는 특성입니다. 그러므로 Linux는 누가 봐도 Unix에 유사해 보이지만, Intel CPU를 쓰는 리눅스 시스템은 Little endian입니다. 요즘의 Mac은 인텔 CPU를 쓰므로 리틀 엔디언입니다. x86계열에서 돌아가는 선 솔라리스도 리틀 엔디언 계열입니다. 

아마도 이제 리틀 엔디언이 90%이상 세상을 장악하지 않았나 생각합니다. 어쨌든 과거에 또는 호환성을 이유로 빅 엔디언으로 데이터를 저장하였거나, 하고 있는 경우가 꽤 많이 존재합니다. 빅 엔디언 데이터 포맷은 아직도 꽤 많이 쓰입니다. 과거에 중요한 일, 큰 일은 모두 유닉스 시스템이 담당했고 그 세계의 표준은 빅 엔디언이었습니다.

질문 : 그러면, 예전 데이터를 읽으려면 예전 유닉스 시스템을 장만해야 합니까?

답변 : 그러지 않으려고 byteorder라든지 swap_endian()이라는 함수 또는 파일 입출력에 바이트순서 자동 처리 기능이 들어간 것입니다. 이들의 목적은 순전히 위에 나온 역사적/현실적 문제를 처리하기 위한 것입니다. 

예1. 빅 엔디언으로 읽어들인 변수 A를 리틀 엔디언으로 바꾸어 보아야 합니다. 
   print, swap_endian(A)
   또는, byteorder, A    ; 이를 실행하면 A 변수의 엔디언이 바뀝니다. 

예2. 예전에 Sun Sparc 에서 저장한 파일인데, 예전에 읽던 프로그램으로 읽어도 이상한 값들이 보입니다. 
   바이트 순서가 달라진 시스템에서 사용한 것입니다. 파일을 읽을 때,  /SWAP_endian 키워드를 이용해 보세요.
   openr, 1, filename, /SWAP_endian
   이렇게 말입니다. 

예3. Windows 를 쓰고 있습니다. 그런데, 파일을 작성할 때 HP-UX 를 쓰는 연구실 표준으로 저장할 수 있을까요?
   openw, 1 filename, /SWAP_endian

예4. 우리 연구소에서는 예로부터 파일을 모두 BIG_endian으로 저장하고 있습니다. 이 파일들을 읽는 프로그램을 만들려고 하는데, 리틀 엔디언 머신,빅 엔디언 머신에서 모두 알아서 작동할 수 있도록 만들 수 있습니까?
  ::: 이런 문제가 닥치면 SWAP_IF 계열의 키워드를 써야 합니다. 
  SWAP_IF_LITTLE_endian 은 “만일 실행중인 컴퓨터가 리틀 엔디언 머신이라면 바이트 순서를 바꾼다”라는 의미가 있습니다. 즉, 파일이 빅 엔디언으로 저장되었다는 의미입니다. 빅 엔디언 컴퓨터에서 실행될 때는 바이트 순서를 바꾸지 않습니다. 
  반대의 키워드로 SWAP_IF_BIG_endian 이 있습니다. 이는 역시 “파일은 리틀 엔디언으로 저장이 되어 있다”는 의미로 해석될 수 있습니다. “빅 엔디언 머신이라면 바이트 순서를 바꿔야 한다”라는 뜻이니까요.
  SWAP_endian 함수에도 이러한 키워드가 있어 머신 특성에 따라 알아서 처리되는 프로그램을 만들 수 있습니다. 

이는 모두 ASCII(TXT) 파일이 아닌 바이너리 파일에서 오는 문제입니다. 그러므로 바이너리 파일을 시키는 대로 읽었는데, 숫자가 영 딴 숫자가 나온다고 생각되신다면 바이트 순서를 한번 바꾸어 보십시오. 어려운 일은 아니니까요. 만일 이렇게 해서 감이 잡히는 숫자가 보인다면 바이트 순서가 문제가 되었던
상황일 것입니다. 

이런 골치 아픈 문제를 해결하기 위해 나온 표준 바이너리가 XDR 포맷이라는 것입니다. 그리고 이러한 XDR 방식을 이용해서 만든 고급 바이너리 형식 중에 netCDF, HDF, HDF5 등이 있습니다. 이들은 바이너리 형식이지만 byteorder 문제를 따지지 않습니다. 

위 예는 2바이트 정수를 가지고 간단히 보여드린 예이지만, 4바이트 정수, 4바이트 실수, 8바이트 정수 8바이트 실수 등 모든 수 체계를 저장한 바이너리 파일이라면 같은 방식으로 처리할 수 있습니다. 

왜 이렇게까지 귀찮게 되었는지 역사적으로는 복잡하지만 실제 프로그래머는 /SWAP_endian 키워드 하나만 쓰면 다 해결 되는 문제라고 생각합니다. 이런 긴 스토리는 재미삼아 읽는 것 뿐이지요.