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


106

Меня смущает машинный код и собственный код в контексте языков .NET.

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


3
У меня вопрос по этому вопросу. Подпадает ли этот вопрос под требования StackOverflow? ага, это не так, но в то же время этот вопрос очень полезен / информативен. Предполагая, что этот тип вопросов не разрешен, где мы должны задавать такие вопросы, если не здесь?
Юсуф Азад

Ответы:


150

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

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

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

Неуправляемый код и управляемый код. Неуправляемый код - это код, написанный на языке программирования, таком как 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.. Обычному программисту обычно не нужно иметь доступ к этому коду или писать на этом языке напрямую, но когда люди это делают, они часто называют его кодом ассемблера, потому что они используют ассемблер для преобразования его в байт-код.


C ++ может компилироваться в машинный код, но очень часто он компилируется в другие форматы, такие как exe, которые будут работать с операционной системой.
Гордон Густафсон,

Есть языки, которые поддерживают сборку мусора и непрозрачные ссылки, которые обычно компилируются в машинный код. Это делают самые серьезные реализации Common Lisp. То, что вы говорите, может быть правдой в отношении языков, поддерживаемых Microsoft, но существует больше скомпилированных языков, чем поддерживается Visual Studio.
Дэвид Торнли

3
@CrazyJugglerDrummer: Код, содержащийся в файлах EXE, созданных компиляторами C ++, по-прежнему является машинным кодом. @ Дэвид Торнли: Я упомянул значительно больше языков, чем только они, но я не хотел усложнять ситуацию, упоминая все неясные странности.
Timwi

Некоторые компиляторы, многие из которых, фактически компилируются с C / C ++ или других языков на язык ассемблера, затем вызывают ассемблер, и ассемблер превращает его в объектные файлы, которые в основном представляют собой машинный код, но нуждаются в нескольких штрихах, прежде чем они смогут перейти в память на процессоре, а затем компоновщик связывает все это с версией программы с машинным кодом. Дело в том, что C / C ++ и т. Д. Часто не компилируются прямо в машинный код, он невидимо для пользователя делает два или три шага по пути. TCC, например, является исключением из этого правила, он переходит непосредственно в машинный код.
old_timer

Это похоже на придирки, но не все ассемблеры переводят 1-1 в коды операций. Фактически, многие современные ассемблеры поддерживают такие конструкции абстракции, как классы. Пример: TASM, ассемблер Borland. en.wikipedia.org/wiki/TASM
Prime

45

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


6

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

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


Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.