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


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.