NGMsoftware

NGMsoftware
로그인 회원가입
  • 매뉴얼
  • 학습
  • 매뉴얼

    학습


    기타 컴퓨터 기본 상식! 비트와 바이트 그리고 인코딩에 대해 알아보자.

    페이지 정보

    본문

    안녕하세요. 엔지엠소프트웨어입니다. 컴공과를 나오신 분들은 대부분 다 아는 내용일겁니다. 혹시나~ 모르는 분들은 가볍게 읽어보시면 좋을듯 합니다. 그리고, 윈도우와 리눅스에서 동시에 프로젝트를 진행하는 개발자가 봐도 좋을만한 내용이 포함되어 있으니 잠깐 시간을 내서 읽어보세요^^;

    IuRfMdX.png

     

     

    컴퓨터가 정보를 인식할 수 있는 최소 단위는 0과 1입니다. 이 0과 1을 Bit라고 부르죠~ 왜 컴퓨터는 0과 1밖에 인식할 수 없는지를 이해하려면, 반도체(Semiconductor)에 대해 기본적인 내용을 이해하고 있어야 합니다. 반도체는 낮은 온도에서는 전기가 잘 통하지 않고, 높은 온도에서는 전기가 잘 통합니다. 이런 성질을 가지고 있기 때문에 0과 1을 자유롭게 구사할 수 있고, 이 기능을 스위치(트렌지스터)라고 부릅니다. SSD도 반도체지만, 데이타를 저장할 수 있는 특징이 있습니다. 데이타를 유지할 수 있는 이유는 전자를 셀에 가둘 수 있기 때문입니다. 반도체 제조 업체들은 미세 공정을 통해 더 많은 회로를 그려 넣기 위한 경쟁이 뜨겁습니다. 300mm 웨이퍼에 더 많은 칩을 그려 넣을 수 있고, 전자가 다니는 길이 가까우면 속도도 빨라집니다. 그리고 전력 소비량도 낮아지죠. 그렇기에 삼성이나 하이닉스 대만에 TSMC와 같은 업체들이 3나노 공정 경쟁을 하고 있는거죠. 물론, 중국 기업들도 여럿 있지만 뚜렷한 성과를 내지는 못하고 있는 상황입니다. 아무튼, 미세 공정으로 가면 여러가지 장점들도 있지만, 단점도 존재합니다. 전자간의 거리가 가까워서 간섭이 발생하고, 이런 에러들을 바로 잡기 위해 컨트롤러가 필요하고 MLC, TLC에서는 더 자주 에러를 복구해야 합니다. 데이타란 결국 셀에 전자가 있냐 없냐에 따라 0과 1로 구분됩니다. CPU 반도체도 전기가 흐르냐 안흐르냐에 따라 0과 1을 처리하죠. 그래서 SSD나 반도체들의 최소 처리 단위는 0과 1입니다.

    DZxZeo8.jpg

     

     

    1bit는 2가지 경우의 수를 나타낼 수 있습니다. 2bit는 2x2=4(2²) 가지의 경우의 수를 가집니다. 프로그래밍이나 컴퓨터에서 데이타를 처리할 때 가장 기본이 되는 1byte는 8bit입니다. 8비트는 2에 8승이므로 256 가지의 경우의 수를 나타낼 수 있습니다. 잠깐! 근데 여기서 우리가 착각하는게 있습니다. 무의식적으로 0부터 256까지의 숫자를 표현할 수 있을거라고 생각할겁니다. 하지만, 0이 포함되어 있기 때문에 실제로는 0~255까지 표현이 가능합니다. 인덱스도 이와 비슷한데요. 사람은 첫번째 두번째라고 하면 1, 2...와 같이 생각하게 됩니다. 하지만, 컴퓨터는 첫번째가 0이고 두번째가 1이 됩니다. 개발자들은 익숙하기 때문에 큰 문제가 되지 않지만 일반인이 보기에는 다소 이상해 보이는것도 사실입니다. 10진수를 2진수로 변환하는 방법은 의외로 간단합니다. 주어진 수를 2로 계속 나누면서 나머지와 떨어지는 수를 반대로 나열하면 됩니다. 2로 나누어지지 않을때까지요. 아래는 10진수 11을 2진수로 계산하는 과정을 보여주고 있습니다.

    11 / 2 = 몫 5, 나머지 1
    5 / 2 = 몫 2, 나머지 1
    2 / 2 = 몫 1, 나머지 0
    몫 1

     

    마지막 몫 또는 2로 나누어지지 않는 나머지를 가장 왼쪽부터 위로 놓으면 됩니다. 숫자 11의 2진수 값은 1011이 됩니다. 1011을 바이트에 배치하면 [ 0, 0, 0, 0, 1³, 0², 1¹, 1⁰ ]와 같이 표현할 수 있습니다. 2진수를 10진수로 변환하려면 2의 제곱들을 곱해서 전부 더해주면 됩니다. 1 x 2³ + 0 x 2² + 1 x 2¹ + 1 x 2⁰ = 11이 됩니다. 숫자는 이렇게 쉽게 변환이 가능합니다. 그렇다면 문제는 어떻게 컴퓨터가 이해하고 처리할 수 있는걸까요? 아스키코드라는 말이 정확히 뭔지는 몰라도 많이 들어봤을겁니다. 1바이트에 문자를 표현하기 위해서 일종의 약속과 같은 것을 만들었는데요. 예를 들어 대문자 A는 65고 소문자 a는 97입니다. 영어권에서는 ASCII를 이용해서 큰 불편함 없이 모든 문자를 처리할 수 있었습니다. 하지만, 한글이나 한자, 히라가나&카타카나와 같은 문자들은 표현할 수 없었습니다. 그래서, 새롭게 만든게 유니코드(Unicode)입니다. 유니코드는 아스키코드와 다르게 2바이트를 사용합니다. 이렇게 정해진 규칙을 통해서 텍스트를 인코딩하게 됩니다. 구글에서 ASCII code table이라고 검색하면 수많은 사이트와 블로그가 나옵니다. 엔지엠 [ 커스텀 모듈 ]에 문자를 10진수로 변환해주는 액션도 존재 합니다.

    M2fZMxs.png

     

     

    텍스트 인코딩도 수많은 규칙들이 난립하고 개발에 혼란을 초래했었습니다. 지금은, 대부분 UTF-8을 사용하기 때문에 많은 문제가 해소되었죠^^; 여담이지만, 유니코드가 등장하기 이전 아스키는 7비트로 128개의 문자표였습니다. 8비트를 1바이트로 처리하는 컴퓨터가 등장하면서 문제가 커지기 시작했고, 다양한 언어들이 이런 문제를 해결하고자 각자 인코딩을 정의해서 만들었습니다. 물론, 표준이 아니었기 때문에 회사 또는 국가별로 마지막 1비트를 각자 알아서 할당하는 바람에 헬게이트가 열리게 되었던 것입니다. 미국에서 만든 소프트웨어가 한국에서 인코딩이 깨져서 궴궴첪궴궴궴... 와 같이 표시되었던거죠. 지금은 유니코드(고정 2바이트)에서 확장된 UTF-8(가변 길이 4바이트)이 표준처럼 사용되고 있습니다. 불과 10년전만해도 윈도우즈에서 개발한 코드를 리눅스로 복사하면 컴파일이 안되는 문제도 종종 있었습니다. 코드의 문제가 아닌 인코딩 문제인 경우가 대다수였죠^^;

    I4lSnsd.png

     

     

    현재에도 레거시 시스템에서는 EUC-KR이나 GB2312등등... 아직도 많이 사용되고 있습니다. 개발자들의 수고를 덜어준 UTF-8은 가변길이 유니코드 인코딩을 처리할 수 있습니다. 아스키코드는 1바이트로 처리하고, 유니코드는 2바이트로 처리합니다. 위에서 언급했듯이 UTF-8은 최대 4바이트까지 처리할 수 있습니다. 개발자들은 개발도구 또는 편집기에서 자주 접하는 UTF-16도 알고 있을겁니다. 하지만 사용하는 곳을 본적은 없을거예요. UTF-8과 동일하지만, 기본적으로 2바이트를 사용하고 있기 때문에 아스키코드와 같이 1바이트만 필요한 문자인 경우 불필요하게 1바이트를 소모하게 되어 사용되지 않고 있습니다. UTF-8은 윈도우즈나 리눅스에서 사실상 표준과도 같이 사용하고 있습니다. 메모장이나 기타 다른 문서를 만들고 저장하면 인코딩이 UTF-8인것을 볼 수 있죠? 그리고 BOM이라는 정체 불명의 어떤놈도 같이 붙어 있는걸 볼 수 있습니다. BOM(Byte Order Mark)은 문서가 어떤 인코딩으로 되어 있는지를 알 수 있게 해주는 마크입니다.

    #pragma once
    CHAR HDDSPOOF_BUFFER[MAX_HDDS][32] = { 0x20 };
    CHAR HDDORG_BUFFER[MAX_HDDS][32] = { 0 };
    
    typedef struct _VendorInfo
    {
    	char pad_0x0000[0x8];
    	char Info[64];
    } VendorInfo;
    
    typedef struct _HDD_EXTENSION
    {
    	char pad_0x0000[0x60];
    	VendorInfo* pVendorInfo;
    	char pad_0x0068[0x8];
    	char* pHDDSerial;
    	char pad_0x0078[0x30];
    } HDD_EXTENSION, *PHDD_EXTENSION;
    
    typedef __int64(__fastcall *RaidUnitRegisterInterfaces)(PHDD_EXTENSION a1);
    RaidUnitRegisterInterfaces pRegDevInt = NULL;
    
    INT HDD_count = 0;
    
    void SpoofHDD()
    {
    	UINT64 address = GetKernelAddress("storport.sys");
    
    	pRegDevInt = address + RegDevIntOFF;
    
    	PDEVICE_OBJECT pObject = NULL;
    	PFILE_OBJECT pFileObj = NULL;
    
    	UNICODE_STRING DestinationString;
    	RtlInitUnicodeString(&DestinationString, L"\\Device\\RaidPort0");
    
    	NTSTATUS status = IoGetDeviceObjectPointer(&DestinationString, FILE_READ_DATA, &pFileObj, &pObject);
    
    	PDRIVER_OBJECT pDriver = pObject->DriverObject;
    
    	PDEVICE_OBJECT pDevice = pDriver->DeviceObject;
    
    	while (pDevice->NextDevice != NULL)
    	{
    		if (pDevice->DeviceType == FILE_DEVICE_DISK)
    		{
    			PHDD_EXTENSION pDeviceHDD = pDevice->DeviceExtension;
    
    			CHAR HDDSPOOFED_TMP[32] = { 0x0 };
    			randstring(&HDDSPOOFED_TMP, SERIAL_MAX_LENGTH - 1);
    
    			//Can be optimised...
    			for (int i = 1; i <= SERIAL_MAX_LENGTH + 1; i = i + 2)
    			{
    				memcpy(&HDDORG_BUFFER[HDD_count][i - 1], &pDeviceHDD->pHDDSerial[i], sizeof(CHAR));
    				memcpy(&HDDORG_BUFFER[HDD_count][i], &pDeviceHDD->pHDDSerial[i - 1], sizeof(CHAR));
    
    				memcpy(&HDDSPOOF_BUFFER[HDD_count][i - 1], &HDDSPOOFED_TMP[i], sizeof(CHAR));
    				memcpy(&HDDSPOOF_BUFFER[HDD_count][i], &HDDSPOOFED_TMP[i - 1], sizeof(CHAR));
    			}
    
    			RtlStringCchPrintfA(pDeviceHDD->pHDDSerial, SERIAL_MAX_LENGTH + 1, "%s", &HDDSPOOFED_TMP);
    
    			//reset the registry entries to the faked serials
    			pRegDevInt(pDeviceHDD);
    
    			HDD_count++;
    		}
    
    		pDevice = pDevice->NextDevice;
    	}
    }
    

     

    가장 먼저 만들어진 UTF-8이 기본이다보니 BOM이 불필요하지만 윈도우즈는 어떤 이유인지 BOM을 붙여서 인코딩합니다. 참고로, 개발 도구들은 BOM이 없이 UTF-8로 인코딩해줍니다. 그렇다보니 리눅스에서 만든 코드를 FTP로 윈도우즈로 받아온 후 메모장에서 열어서 수정하고 다시 리눅스로 업로드하면 문제가 됩니다. 리눅스는 UTF-8로 인코딩된 문서는 BOM을 처리하지 않기 때문입니다. 아무튼~ 지금은 윈도우즈도 BOM을 사용하지 않으니 크게 문제되지는 않겠지만, 윈도우즈 7버전에서 개발하신다면 유의해야 합니다. 설마 지금도 윈도우즈7을 사용하시는 분은 없겠죠^^;

    NusBkXo.jpg

     

     

    여기까지 대략 비트와 바이트 그리고, 인코딩에 대해 아주 기초적인 내용을 알아봤는데요. 그렇다면 개발자에게 이게 왜 중요할까요? 요즘은 램 용량이 커서 프로그램을 만드는데 크게 영향은 없긴 합니다. 위에서도 언급했듯이 UTF-16이 외면 받는 이유는 불필요한 데이타를 낭비하기 때문이라고 말했습니다. 비트와 바이트, 킬로바이트, 메가바이트등등... 프로그램이 얼마나 메모리를 효율적으로 잘 사용하는지가 여기에 달려 있기 때문입니다. 우리가 무심코 사용하는 List는 동적 가변 길이 컬렉션입니다. 이미 24바이트가 할당되어 있으며, 컬렉션의 요소가 24바이트를 넘으면 자동으로 24바이트를 늘려줍니다. 그래서, 길이가 정해져 있는 배열의 경우에는 어레이를 사용하는게 메모리 관리에 더 효율적입니다. 그런데 대부분은 이렇게 메모리를 타이트하게 관리해야 할만한 프로젝트 경험이 없을겁니다. 있다고 하더라도 일정 압박에 큰 고민없이 자료형을 사용하고 있기도 합니다. 자주 사용하는 char의 경우 1byte이며 0~255까지 문자를 표현할 수 있습니다. short은 2byte니깐 256*2-1 하면 0~65,535까지 표현할 수 있네요. 참고로, int는 4byte이고 long은 8byte입니다. 엄청나게 큰 수죠-_-; 그리고, 잘 사용하지 않는 언사인드(Unsigned) 자료형도 존재합니다. 여기서 사인이란 부호를 말합니다. 양수, 음수 부호죠~ 언사인드는 마이너스 부호를 사용하지 않는다는 의미로 양수만 넣을 수 있습니다.

     

    제 프로그램의 경우에는 음수값을 입력받고 싶지 않거나 양수로 큰 값을 설정해야 하는 경우 언사인드 자료형을 사용합니다. 언사인드는 ulong, uint, ushort과 같이 자료형 앞에 u(Unsigned)가 붙습니다. 이런 경우 이 변수 또는 맴버는 음수를 저장할 수 없게 됩니다. 이렇게 양으로 큰 값을 받도록 했더라도 예측하지 못한 더 큰 값이 들어오면 Overflow(오버플로우: 과하다, 넘치다)가 발생합니다. 값을 담을수가 없어서 넘쳤다는 의미입니다. 대부분의 개발 언어가 비슷한 자료형을 제공합니다. 다만, 자바스크립트는 number 하나만 존재합니다. 요즘은 웹개발에서 타입스크립트를 사용하기 때문에 비슷하다고 하면 또 그렇다고 할 수 있겠네요. 이렇게 다양한 데이타를 담을 수 있도록 자료형이 준비되어 있는데요. 간단한 프로그램이라도 데이타의 유형에 따라 잠깐 고민해보는 습관을 들이는게 나중을 위해서 좋습니다. 이로인해 더 큰 문제가 발생하기도 하지만요^^; 예측하지 못한 데이타가 들어오는걸 방지하기 위한 조치도 미리 해두어야 하구요. Overflow로 인해 프로그램이 다운되는건 막아야 하니까요!

    e96AHgN.jpg

     

     

    대충 머리속에 있는 내용들을 풀어서 쓰다보니 자료도 없고, 앞뒤 두서없이 작성한 글이 되어 버렸습니다. 현재 반도체 업계에 종사하고 있지만, 아직도 배워야 할게 산더미처럼 쌓여있네요. 이제는 귀농? 정확히 말하면 귀촌이겠네요. 집에서 슬슬 개발이나 하면서 노년을 보냈으면 좋겠네요. 회사에서 나가라고 하기전에 먼저 나가야 하는데... 그날이 오긴 할지 모르겠습니다. 안정적인 수익 모델이 만들어지면 좋겠지만~ 쉽지 않군요.

     

    개발자에게 후원하기

    MGtdv7r.png

     

    추천, 구독, 홍보 꼭~ 부탁드립니다.

    여러분의 후원이 빠른 귀농을 가능하게 해줍니다~ 답답한 도시를 벗어나 귀농하고 싶은 개발자~

    감사합니다~

    • 네이버 공유하기
    • 페이스북 공유하기
    • 트위터 공유하기
    • 카카오스토리 공유하기
    추천0 비추천0

    댓글목록

    등록된 댓글이 없습니다.