Яка різниця між нативним кодом, машинним кодом та кодом складання?


106

Мене плутає машинний код та нативний код у контексті мов .NET.

У чому різниця між ними? Вони однакові?


3
У мене є питання щодо цього питання. Чи підпадає це питання під вимогу StackOverflow? afaik це не так, але в той же час подібне питання дуже корисне / інформативне. Якщо припустити, що цей тип питань заборонений, то де нам слід задавати такий тип питань, якщо не тут?
Юсуф Азад

Дивіться також: stackoverflow.com/questions/334326 / ...
T.Todua

Відповіді:


150

Терміни дійсно трохи заплутані, оскільки їх іноді використовують непослідовно.

Код машини: це найбільш чітко визначений. Це код, який використовує інструкції байт-коду, які ваш процесор (фізичний шматок металу, який виконує фактичну роботу) розуміє та виконує безпосередньо. Всі інші коди повинні бути переведені або перетворені в машинний код, перш ніж ваша машина може виконати його.

Рідний код: Цей термін іноді використовується в місцях, де мається на увазі машинний код (див. Вище). Однак іноді він також використовується для позначення некерованого коду (див. Нижче).

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

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

Код складання: Цей термін, як правило, відноситься до виду вихідного коду, який люди пишуть, коли вони дійсно хочуть писати байт-код. Асемблер це програма , яка перетворює цей вихідний код в реальному байт-коду. Він не є компілятором, оскільки перетворення 1-в-1. Однак термін неоднозначний щодо того, який тип байтового коду використовується: ним можна керувати чи керувати ним. Якщо він не управляється, отриманий байт-код є машинним кодом . Якщо ним керувати, це призводить до отримання байт-коду, який використовується поза кадром у віртуальному середовищі, такому як .NET. Керований код (наприклад, C #, Java) компілюється в цю спеціальну мову байт-коду, яка у випадку .NET називається загальною проміжною мовою (CIL) а в Java називається байт-кодом Java. Зазвичай звичайний програміст не має доступу до цього коду або писати цією мовою безпосередньо, але коли люди це роблять, вони часто називають його збірним кодом, оскільки вони використовують асемблер, щоб перетворити його в байт-код.


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

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

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

Деякі компілятори, фактично, компілюються з C / C ++ або інших мов на мову складання, потім викликають асемблер, і асемблер перетворює його в об'єктні файли, які є переважно машинним кодом, але потребують декількох дотиків, перш ніж вони зможуть увійти в пам'ять на процесорі. лінкер пов'язує все це у версії програми машинного коду. Сенс C / C ++ і т.д. часто не збирається прямо в машинний код, він невидимий для користувача робить два-три кроки на шляху. Наприклад, TCC є винятком із цього, він дійсно переходить до машинного коду.
old_timer

Це здається, що випиваються, але не всі асемблери перекладають 1-1 на опкоди. Насправді багато сучасних асемблерів підтримують такі абстракційні конструкції, як класи. Приклад: TASM, асемблер Borland. en.wikipedia.org/wiki/TASM
Прем'єр

45

Те, що ви бачите при використанні налагодження + Windows + демонтаж при налагодженні програми C #, є хорошим посібником щодо цих умов. Ось примітка до нього, коли я складаю програму "привіт світ", написану на C # у конфігурації випуску з включеною оптимізацією 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 на початку методу.

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

Третій стовпчик - це код складання . Збірка - це проста мова, покликана полегшити запис машинного коду. Він порівнюється з компілюванням C # в IL. Компілятор, який використовується для перекладу коду збірки, називається "асемблером". Напевно, у вас на комп'ютері є асемблер Microsoft, його виконавче ім'я ml.exe, ml64.exe для 64-розрядної версії. Є дві поширені версії мов складання, які використовуються. Ви бачите той, який використовують Intel та AMD. У світі з відкритим кодом складання в нотаціях AT&T є загальним явищем. Синтаксис мови сильно залежить від типу процесора, для якого написано, мова складання для PowerPC сильно відрізняється.

Гаразд, це стосується двох термінів у вашому питанні. "Рідний код" - нечіткий термін, його нечасто використовують для опису коду некерованою мовою. Навчальним, можливо, є побачити, який машинний код генерується компілятором C. Це "привіт світ" версія на 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.