В чем разница между собственным кодом, машинным кодом и кодом сборки?

Я запутался в машинном коде и нативном коде в контексте языков .NET.

В чем разница между ними? Они одинаковые?

96
задан Peter Cordes 8 August 2018 в 21:58
поделиться

4 ответа

Эти термины действительно немного сбивают с толку, потому что иногда они используются непоследовательно.

Машинный код: Это наиболее четко определенный. Это код, который использует инструкции байт-кода, которые ваш процессор (физический кусок металла, который выполняет фактическую работу) понимает и выполняет напрямую. Весь другой код должен быть переведен или преобразован в машинный код , прежде чем ваша машина сможет его выполнить.

Собственный код: Этот термин иногда используется в тех местах, где имеется в виду машинный код (см. Выше). Однако иногда это слово также используется для обозначения неуправляемого кода (см. Ниже).

Неуправляемый код и управляемый код: Неуправляемый код относится к коду, написанному на таком языке программирования, как C или C ++, который компилируется непосредственно в машинный код .Он контрастирует с управляемым кодом , который написан на C #, VB.NET, Java или аналогичных языках и выполняется в виртуальной среде (например, .NET или JavaVM), которая своего рода «имитирует» процессор. в программном обеспечении. Основное отличие состоит в том, что управляемый код «управляет» ресурсами (в основном распределением памяти) за вас, используя сборку мусора и сохраняя ссылки на объекты непрозрачными. Неуправляемый код - это код, который требует от вас вручную выделять и освобождать память, что иногда вызывает утечки памяти (когда вы забываете освободить память), а иногда и ошибки сегментации (когда вы освобождаете память слишком рано. ). Неуправляемый также обычно подразумевает отсутствие проверок времени выполнения на наличие распространенных ошибок, таких как разыменование нулевого указателя или переполнение границ массива.

Строго говоря, большинство языков с динамической типизацией, таких как Perl, Python, PHP и Ruby, также являются управляемым кодом . Однако они обычно не описываются как таковые, что показывает, что управляемый код на самом деле является своего рода маркетинговым термином для действительно больших, серьезных, коммерческих сред программирования (.NET и Java).

Ассемблерный код: Этот термин обычно относится к типу исходного кода, который люди пишут, когда они действительно хотят писать байтовый код. Ассемблер - это программа, которая превращает этот исходный код в настоящий байт-код. Это не компилятор , потому что преобразование выполняется 1-к-1. Однако термин неоднозначен относительно того, какой тип байт-кода используется: он может быть управляемым или неуправляемым.Если он неуправляемый, результирующий байт-код будет машинным кодом . Если им управлять, это приводит к тому, что байт-код используется негласно виртуальной средой, такой как .NET. Управляемый код (например, C #, Java) компилируется в этот специальный язык байтового кода, который в случае .NET называется Common Intermediate Language (CIL) , а в Java - Java byte- код . Обычному программисту обычно не нужно иметь доступ к этому коду или писать на этом языке напрямую, но когда люди это делают, они часто называют его ассемблерным кодом , потому что они используют ассемблер ], чтобы превратить его в байт-код.

140
ответ дан 24 November 2019 в 05:37
поделиться

Родной код и машинный код - это одно и то же - фактические байты, которые исполняет процессор.

Ассемблерный код имеет два значения: первое - это машинный код, переведенный в более человекочитаемую форму (с байтами для инструкций, переведенными в короткие слова-мнемоники, такие как "JMP" (который "перепрыгивает" на другое место в коде). Другое дело - байткод IL (байты инструкций, которые генерируют компиляторы типа C# или VB и которые в конечном итоге будут переведены в машинный код, но еще не переведены), который живет в DLL или EXE.

6
ответ дан 24 November 2019 в 05:37
поделиться

В .NET сборки содержат код промежуточного языка MS (MSIL, иногда CIL).
Это похоже на машинный код «высокого уровня».

При загрузке MSIL компилируется JIT-компилятором в собственный код (машинный код Intel x86 или x64).

2
ответ дан 24 November 2019 в 05:37
поделиться

То, что вы видите, когда используете Debug + Windows + Disassembly при отладке программы C #, является хорошим руководством для этих терминов. Вот его аннотированная версия, когда я компилирую программу 'hello world', написанную на C #, в конфигурации Release с включенной оптимизацией JIT:

        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

Щелкните правой кнопкой мыши окно и отметьте «Показать байты кода», чтобы получить аналогичное отображение.

В столбце слева указан адрес машинного кода. Его значение подделывается отладчиком, код фактически находится где-то в другом месте. Но это может быть где угодно, в зависимости от местоположения, выбранного JIT-компилятором, поэтому отладчик просто начинает нумерацию адресов с 0 в начале метода.

Второй столбец - это машинный код . Фактические единицы и нули, которые выполняет ЦП. Машинный код, как и здесь, обычно отображается в шестнадцатеричном формате. Показательным, возможно, является то, что 0x8B выбирает инструкцию MOV, дополнительные байты нужны для того, чтобы точно сообщить ЦП, что нужно переместить. Также обратите внимание на две разновидности инструкции CALL: 0xE8 - это прямой вызов, 0xFF - это инструкция косвенного вызова.

Третий столбец - это ассемблерный код . Ассемблер - это простой язык, упрощающий написание машинного кода. Это можно сравнить с компиляцией C # в IL. Компилятор, используемый для перевода ассемблерного кода, называется «ассемблер».У вас, вероятно, есть ассемблер Microsoft на вашем компьютере, его исполняемое имя - ml.exe, ml64.exe для 64-битной версии. Используются две распространенные версии языков ассемблера. Вы видите тот, который используют Intel и AMD. В мире с открытым исходным кодом сборка в нотации AT&T является обычным явлением. Синтаксис языка сильно зависит от типа процессора, для которого он был написан, язык ассемблера для PowerPC сильно отличается.

Хорошо, это касается двух терминов в вашем вопросе. «Собственный код» - это нечеткий термин, он нередко используется для описания кода на неуправляемом языке. Возможно, поучительно посмотреть, какой машинный код генерируется компилятором C. Это версия «hello world» на C:

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. И это в первую очередь вращается вокруг сборщика мусора.

44
ответ дан 24 November 2019 в 05:37
поделиться
Другие вопросы по тегам:

Похожие вопросы: