Меня смущает машинный код и собственный код в контексте языков .NET.
В чем разница между ними? Они одинаковы?
Меня смущает машинный код и собственный код в контексте языков .NET.
В чем разница между ними? Они одинаковы?
Ответы:
Эти термины действительно немного сбивают с толку, потому что иногда они используются непоследовательно.
Машинный код: это наиболее четко определенный. Это код, который использует инструкции байт-кода, которые ваш процессор (физический кусок металла, который выполняет фактическую работу) понимает и выполняет напрямую. Весь другой код должен быть переведен или преобразован в машинный код, прежде чем ваша машина сможет его выполнить.
Собственный код: этот термин иногда используется в тех местах, где имеется в виду машинный код (см. Выше). Однако иногда это слово также используется для обозначения неуправляемого кода (см. Ниже).
Неуправляемый код и управляемый код. Неуправляемый код - это код, написанный на языке программирования, таком как 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.. Обычному программисту обычно не нужно иметь доступ к этому коду или писать на этом языке напрямую, но когда люди это делают, они часто называют его кодом ассемблера, потому что они используют ассемблер для преобразования его в байт-код.
То, что вы видите, когда используете 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. И это в первую очередь вращается вокруг сборщика мусора.
Собственный код и машинный код - это одно и то же - фактические байты, которые выполняет ЦП.
Ассемблерный код имеет два значения: одно - это машинный код, переведенный в более удобочитаемую форму (с байтами для инструкций, переведенными в короткие словесные мнемоники, такие как «JMP» (который «перескакивает» в другое место в коде). - это байт-код IL (байты команд, которые генерируются компиляторами, такими как C # или VB, которые в конечном итоге будут переведены в машинный код, но еще не переведены), который находится в DLL или EXE.
В .NET сборки содержат код промежуточного языка MS (MSIL, иногда CIL).
Это похоже на машинный код «высокого уровня».
При загрузке MSIL компилируется JIT-компилятором в собственный код (машинный код Intel x86 или x64).