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

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

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

Ответ 1

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

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

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

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

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

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

Ответ 2

То, что вы видите при использовании Debug + Windows + Disassembly при отладке программы на С#, является хорошим руководством для этих условий. Здесь аннотированная версия, когда я компилирую программу "hello world", написанную на С# в конфигурации 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 в начале метода.

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

Третий столбец - это код сборки. Сборка - это простой язык, предназначенный для упрощения написания машинного кода. Он сравнивается с С#, скомпилированным с 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   

Я не комментировал его, главным образом потому, что он настолько похож на машинный код, сгенерированный программой С#. Вызов функции printf() сильно отличается от вызова Console.WriteLine(), но все остальное примерно одинаково. Также обратите внимание, что отладчик теперь генерирует реальный адрес машинного кода и что он немного умнее о символах. Иногда возникает побочный эффект генерации информации об отладке после генерации машинного кода, например неуправляемых компиляторов. Следует также упомянуть, что я отключил несколько вариантов оптимизации машинного кода, чтобы сделать машинный код похожим. Компиляторы C/С++ имеют гораздо больше времени для оптимизации кода, результат часто трудно интерпретировать. И очень сложно отлаживать.

Ключевые моменты здесь есть очень мало различий между машинным кодом, сгенерированным на управляемом языке компилятором JIT и машинным кодом, сгенерированным компилятором нативного кода. Что является основной причиной того, что язык С# может быть конкурентоспособным с компилятором собственного кода. Единственное реальное различие между ними - это вызовы функций поддержки. Многие из них реализованы в среде CLR. И это вращается вокруг сборщика мусора.

Ответ 3

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

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

Ответ 4

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

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