네이티브 코드, 기계어 코드 및 어셈블리 코드의 차이점은 무엇입니까?


106

.NET 언어의 맥락에서 기계 코드와 네이티브 코드에 대해 혼란스러워합니다.

그들 사이의 차이점은 무엇입니까? 동일합니까?


3
이 질문에 대한 질문이 있습니다. 이 질문이 StackOverflow의 요구 사항에 해당합니까? 그렇지는 않지만 동시에 이런 종류의 질문은 매우 유용하고 유익합니다. 이런 유형의 질문이 허용되지 않는다고 가정 할 때 여기가 아니라면 이러한 유형의 질문을 어디에서해야합니까?
— Yousuf Azad

답변:


150

이 용어는 때때로 일관되지 않게 사용되기 때문에 실제로 약간 혼란 스럽습니다.

기계 코드 : 가장 잘 정의 된 코드 입니다. 프로세서 (실제 작업을 수행하는 물리적 금속 조각)가 직접 이해하고 실행하는 바이트 코드 명령을 사용하는 코드입니다. 다른 모든 코드는 기계가 실행하기 전에 기계어 코드 로 변환되거나 변환되어야 합니다.

네이티브 코드 : 이 용어는 때때로 기계 코드 (위 참조)가 의미하는 곳에서 사용됩니다 . 그러나 때로는 비 관리 코드 를 의미하는데도 사용됩니다 (아래 참조).

비 관리 코드 및 관리 코드 : 비 관리 코드는 기계어 코드 로 직접 컴파일되는 C 또는 C ++와 같은 프로그래밍 언어로 작성된 코드를 나타냅니다 . C #, VB.NET, Java 등으로 작성되고 소프트웨어에서 프로세서를 "시뮬레이션"하는 가상 환경 (예 : .NET 또는 JavaVM)에서 실행 되는 관리 코드 와 대조됩니다 . 주된 차이점은 관리 코드가 가비지 수집을 사용하고 개체에 대한 참조를 불투명하게 유지하여 리소스 (대부분 메모리 할당)를 "관리"한다는 것입니다. 비 관리 코드메모리를 수동으로 할당하고 할당 해제해야하는 코드의 종류로, 때로는 메모리 누수 (할당 해제를 잊었을 때)와 세그먼트 오류 (너무 빨리 할당 해제 할 때)를 유발합니다. 관리되지 않음은 또한 일반적으로 null 포인터 역 참조 또는 배열 경계 오버플로와 같은 일반적인 오류에 대한 런타임 검사가 없음을 의미합니다.

엄밀히 말하면 Perl, Python, PHP 및 Ruby와 같은 대부분의 동적 형식 언어도 관리 코드 입니다. 그러나 일반적으로 그렇게 설명되지는 않습니다. 이는 관리 코드 가 실제로 크고 심각한 상용 프로그래밍 환경 (.NET 및 Java)에 대한 마케팅 용어라는 것을 보여줍니다 .

어셈블리 코드 : 이 용어는 일반적으로 사람들이 실제로 바이트 코드를 작성하고 싶을 때 작성하는 소스 코드의 종류를 나타냅니다. 어셈블러는 실시간 바이트 코드로이 소스 코드를 켤 수있는 프로그램입니다. 변환이 일대일이기 때문에 컴파일러 가 아닙니다 . 그러나 어떤 종류의 바이트 코드가 사용되는지에 대한 용어는 모호합니다. 관리되거나 관리되지 않을 수 있습니다. 관리되지 않는 경우 결과 바이트 코드는 기계 코드 입니다. 관리되는 경우 .NET과 같은 가상 환경에서 백그라운드에서 사용되는 바이트 코드가 생성됩니다. 관리 코드 (예 : C #, Java)는이 특수 바이트 코드 언어로 컴파일됩니다. .NET의 경우 CIL (Common Intermediate Language) 이라고하고 Java 에서는 Java 바이트 코드 라고 합니다.. 이 바로이 언어로이 코드 또는 쓰기에 액세스 할 수있는 일반적인 프로그래머에 대한 필요가 거의 보통이지만, 사람이 할 때, 그들은 종종로 참조 어셈블리 코드 그들이 사용하기 때문에 어셈블러 바이트 코드로 전원을 켭니다.


C ++는 기계 코드로 컴파일 할 수 있지만 운영 체제에서 실행되는 exe와 같은 다른 형식으로 컴파일되는 경우가 많습니다.
— Gordon Gustafson

일반적으로 기계어 코드로 컴파일되는 가비지 수집 및 불투명 참조를 지원하는 언어가 있습니다. Common Lisp의 가장 심각한 구현은 그렇게합니다. Microsoft에서 지원하는 언어는 사실 일 수 있지만 Visual Studio에서 지원하는 것보다 컴파일 된 언어가 더 많습니다.
— David Thornley 2010-08-10

3
@CrazyJugglerDrummer : C ++ 컴파일러에 의해 생성 된 EXE 파일에 포함 된 코드는 여전히 기계 코드입니다. @David Thornley : 저는 그 언어보다 훨씬 더 많은 언어를 언급했지만 모호한 모든 이상한 점을 언급함으로써 문제를 복잡하게 만들고 싶지 않았습니다.
— Timwi

일부 컴파일러는 실제로 C / C ++ 또는 다른 언어에서 어셈블리 언어로 컴파일 한 다음 어셈블러를 호출하고 어셈블러는이를 대부분 기계 코드이지만 프로세서의 메모리로 이동하기 전에 몇 번의 터치가 필요한 객체 파일로 변환합니다. 링커는이 모든 것을 프로그램의 기계어 코드 버전에 연결합니다. C / C ++ 등의 요점은 종종 사용자에게 보이지 않는 기계 코드로 직접 컴파일되지 않고 2 ~ 3 단계를 수행합니다. 예를 들어 TCC는 예외적으로 기계 코드로 직접 이동합니다.
— old_timer 2010-08-12

이것은 nitpicking처럼 느껴지지만 모든 어셈블러가 1-1을 opcode로 변환하는 것은 아닙니다. 사실, 많은 현대 어셈블러는 클래스와 같은 추상화 구조를 지원합니다. 예 : 볼랜드의 어셈블러 인 TASM. en.wikipedia.org/wiki/TASM
— 프라임 '

45

C # 프로그램을 디버깅 할 때 Debug + Windows + Disassembly를 사용할 때 표시되는 내용은 이러한 용어에 대한 좋은 가이드입니다. 다음은 JIT 최적화가 활성화 된 릴리스 구성에서 C #으로 작성된 'hello world'프로그램을 컴파일 할 때 주석이 달린 버전입니다.

        static void Main(string[] args) {
            Console.WriteLine("Hello world");
00000000 55                push        ebp                           ; save stack frame pointer
00000001 8B EC             mov         ebp,esp                       ; setup current frame
00000003 E8 30 BE 03 6F    call        6F03BE38                      ; Console.Out property getter
00000008 8B C8             mov         ecx,eax                       ; setup "this"
0000000a 8B 15 88 20 BD 02 mov         edx,dword ptr ds:[02BD2088h]  ; arg = "Hello world"
00000010 8B 01             mov         eax,dword ptr [ecx]           ; TextWriter reference
00000012 FF 90 D8 00 00 00 call        dword ptr [eax+000000D8h]     ; TextWriter.WriteLine()
00000018 5D                pop         ebp                           ; restore stack frame pointer
        }
00000019 C3                ret                                       ; done, return

창을 마우스 오른쪽 버튼으로 클릭하고 "Show Code Bytes"를 선택하면 유사한 디스플레이가 표시됩니다.

왼쪽 열은 기계 코드 주소입니다. 그 값은 디버거에 의해 가짜이며 코드는 실제로 다른 곳에 있습니다. 그러나 JIT 컴파일러가 선택한 위치에 따라 어디든 될 수 있으므로 디버거는 메서드 시작시 0부터 주소 번호 지정을 시작합니다.

두 번째 열은 기계 코드 입니다. CPU가 실행하는 실제 1과 0. 여기와 같이 기계어 코드는 일반적으로 16 진수로 표시됩니다. 예를 들어 0x8B가 MOV 명령을 선택하면 추가 바이트가 CPU에 정확히 무엇을 이동해야하는지 알려주는 것입니다. 또한 CALL 명령어의 두 가지 특징 인 0xE8은 직접 호출이고 0xFF는 간접 호출 명령어입니다.

세 번째 열은 어셈블리 코드 입니다. 어셈블리는 기계어 코드를 더 쉽게 작성할 수 있도록 설계된 간단한 언어입니다. IL로 컴파일되는 C #과 비교됩니다. 어셈블리 코드를 번역하는 데 사용되는 컴파일러를 "어셈블러"라고합니다. 컴퓨터에 Microsoft 어셈블러가있을 수 있습니다. 실행 파일 이름은 ml.exe, 64 비트 버전의 경우 ml64.exe입니다. 사용중인 어셈블리 언어에는 두 가지 공통 버전이 있습니다. 당신이 보는 것은 Intel과 AMD가 사용하는 것입니다. 오픈 소스 세계에서는 AT & T 표기법의 어셈블리가 일반적입니다. 언어 구문은 작성된 CPU의 종류에 따라 크게 달라지며 PowerPC의 어셈블리 언어는 매우 다릅니다.

좋아요, 귀하의 질문에서 두 가지 용어를 다룹니다. "네이티브 코드"는 모호한 용어로, 관리되지 않는 언어로 코드를 설명하는 데 드물게 사용되지 않습니다. C 컴파일러가 생성하는 기계 코드의 종류를 확인하는 것이 좋습니다. 이것은 C의 'hello world'버전입니다.

int _tmain(int argc, _TCHAR* argv[])
{
00401010 55               push        ebp  
00401011 8B EC            mov         ebp,esp 
    printf("Hello world");
00401013 68 6C 6C 45 00   push        offset ___xt_z+128h (456C6Ch) 
00401018 E8 13 00 00 00   call        printf (401030h) 
0040101D 83 C4 04         add         esp,4 
    return 0;
00401020 33 C0            xor         eax,eax 
}
00401022 5D               pop         ebp  
00401023 C3               ret   

저는 주석을 달지 않았습니다. 대부분 C # 프로그램에서 생성 된 기계어 코드 와 매우 유사 하기 때문입니다. printf () 함수 호출은 Console.WriteLine () 호출과는 상당히 다르지만 다른 모든 것은 거의 동일합니다. 또한 디버거가 이제 실제 기계 코드 주소를 생성하고 있으며 기호에 대해 조금 더 똑똑하다는 점에 유의하십시오. 관리되지 않는 컴파일러처럼 기계어 코드를 생성 한 후 디버그 정보를 생성하는 부작용이 자주 발생합니다. 또한 기계어 코드를 유사하게 보이게하기 위해 몇 가지 기계어 코드 최적화 옵션을 해제했음을 언급해야합니다. C / C ++ 컴파일러는 코드를 최적화하는 데 더 많은 시간을 사용할 수 있으며 결과를 해석하기 어려운 경우가 많습니다. 그리고 디버깅하기가 매우 어렵습니다.

여기에 요점이있다이다 매우 네이티브 코드 컴파일러에 의해 생성 된 JIT 컴파일러와 기계 코드에 의해 관리되는 언어에서 생성 된 기계어 코드 사이에 약간의 차이가. 이것이 C # 언어가 네이티브 코드 컴파일러와 경쟁 할 수있는 주된 이유입니다. 그들 사이의 유일한 차이점은 지원 함수 호출입니다. 대부분은 CLR에서 구현됩니다. 그리고 그것은 가비지 컬렉터를 중심으로 일차적으로 회전합니다.


6

네이티브 코드와 기계어 코드는 CPU가 실행하는 실제 바이트입니다.

어셈블리 코드에는 두 가지 의미가 있습니다. 하나는보다 사람이 읽을 수있는 형식으로 번역 된 기계 코드입니다 (명령어 바이트는 "JMP"(코드의 다른 지점으로 "점프")와 같은 짧은 단어 같은 니모닉으로 번역됨). DLL 또는 EXE에있는 IL 바이트 코드 (C # 또는 VB와 같은 컴파일러가 생성하는 명령어 바이트로, 결국 기계 코드로 변환되지만 아직은 아닙니다)입니다.


당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.