Apa perbedaan antara kode asli, kode mesin, dan kode perakitan?

106

Saya bingung tentang kode mesin dan kode asli dalam konteks bahasa .NET.

Apa perbedaan di antara keduanya? Apakah mereka sama?

samaladeepak
sumber
3
Saya punya pertanyaan tentang pertanyaan ini. Apakah pertanyaan ini termasuk dalam persyaratan StackOverflow? afaik tidak, tapi pada saat yang sama pertanyaan semacam ini sangat membantu / informatif. Dengan asumsi pertanyaan jenis ini tidak diperbolehkan, di mana kita harus mengajukan pertanyaan jenis ini jika tidak di sini?
Yousuf Azad

Jawaban:

150

Istilah tersebut memang agak membingungkan, karena terkadang digunakan secara tidak konsisten.

Kode mesin: Ini yang paling jelas. Ini adalah kode yang menggunakan instruksi kode byte yang dipahami dan dijalankan oleh prosesor Anda (bagian fisik logam yang sebenarnya). Semua kode lainnya harus diterjemahkan atau diubah menjadi kode mesin sebelum mesin Anda dapat menjalankannya.

Kode asli: Istilah ini terkadang digunakan di tempat-tempat di mana kode mesin (lihat di atas) dimaksudkan. Namun, terkadang juga digunakan untuk mengartikan kode yang tidak terkelola (lihat di bawah).

Kode unmanaged dan kode dikelola: unmanaged code mengacu pada kode yang ditulis dalam bahasa pemrograman seperti C atau C ++, yang dikompilasi langsung ke kode mesin . Ini kontras dengan kode yang dikelola , yang ditulis dalam C #, VB.NET, Java, atau serupa, dan dieksekusi dalam lingkungan virtual (seperti .NET atau JavaVM) yang "mensimulasikan" prosesor dalam perangkat lunak. Perbedaan utamanya adalah kode terkelola "mengelola" sumber daya (sebagian besar alokasi memori) untuk Anda dengan menggunakan pengumpulan sampah dan dengan menjaga referensi ke objek tetap buram. Kode tidak terkelolaadalah jenis kode yang mengharuskan Anda mengalokasikan dan mengalokasikan memori secara manual, terkadang menyebabkan kebocoran memori (saat Anda lupa mengalokasikannya) dan terkadang kesalahan segmentasi (saat Anda membatalkan alokasi terlalu cepat). Tidak terkelola juga biasanya menyiratkan tidak ada pemeriksaan run-time untuk kesalahan umum seperti dereferensi penunjuk nol atau luapan batas array.

Sebenarnya, sebagian besar bahasa yang diketik secara dinamis - seperti Perl, Python, PHP dan Ruby - juga merupakan kode yang dikelola . Namun, mereka tidak secara umum dijelaskan seperti itu, yang menunjukkan bahwa kode terkelola sebenarnya merupakan istilah pemasaran untuk lingkungan pemrograman komersial yang sangat besar, serius, dan komersial (.NET dan Java).

Kode perakitan: Istilah ini umumnya mengacu pada jenis kode sumber yang ditulis orang ketika mereka benar-benar ingin menulis kode byte. Sebuah assembler adalah program yang mengubah kode sumber ini menjadi nyata byte-code. Ini bukan kompilator karena transformasinya 1-ke-1. Namun, istilah ini ambigu mengenai jenis kode byte yang digunakan: dapat dikelola atau tidak. Jika tidak dikelola, kode byte yang dihasilkan adalah kode mesin . Jika dikelola, ini menghasilkan kode byte yang digunakan di belakang layar oleh lingkungan virtual seperti .NET. Kode yang dikelola (misalnya C #, Java) dikompilasi ke dalam bahasa kode byte khusus ini, yang dalam kasus .NET disebut Common Intermediate Language (CIL) dan di Jawa disebut kode byte Java. Biasanya ada sedikit kebutuhan bagi programmer umum untuk mengakses kode ini atau menulis dalam bahasa ini secara langsung, tetapi ketika orang melakukannya, mereka sering menyebutnya sebagai kode assembly karena mereka menggunakan assembler untuk mengubahnya menjadi kode byte.

Timwi
sumber
C ++ dapat dikompilasi ke kode mesin, tetapi sangat sering dikompilasi ke format lain seperti exe yang akan dijalankan dengan sistem operasi.
Gordon Gustafson
Ada bahasa yang mendukung pengumpulan sampah dan referensi buram yang biasanya dikompilasi ke kode mesin. Implementasi yang paling serius dari Common Lisp melakukan itu. Apa yang Anda katakan mungkin benar tentang bahasa yang didukung Microsoft, tetapi ada lebih banyak bahasa yang dikompilasi daripada yang didukung oleh Visual Studio.
David Thornley
3
@CrazyJugglerDrummer: Kode yang terdapat dalam file EXE yang dibuat oleh kompiler C ++ masih merupakan kode mesin. @ David Thornley: Saya menyebutkan secara signifikan lebih banyak bahasa daripada hanya itu, tetapi saya tidak ingin memperumit masalah dengan menyebutkan setiap keanehan yang tidak jelas.
Timwi
Beberapa kompiler, banyak, sebenarnya akan mengkompilasi dari C / C ++ atau bahasa lain ke bahasa assembly kemudian memanggil assembler dan assembler mengubahnya menjadi file objek yang sebagian besar merupakan kode mesin tetapi perlu beberapa sentuhan sebelum mereka dapat masuk ke memori pada prosesor kemudian linker menautkan semuanya ke versi kode mesin program. Intinya adalah C / C ++, dll sering tidak mengkompilasi langsung ke kode mesin itu tidak terlihat oleh pengguna melakukan dua atau tiga langkah di jalan. TCC misalnya adalah pengecualian untuk ini, ia langsung menuju ke kode mesin.
old_timer
Ini terasa seperti nitpicking, tetapi tidak semua assembler menerjemahkan 1-1 ke opcode. Faktanya, banyak assembler modern mendukung konstruksi abstraksi seperti kelas. Contoh: TASM, assembler Borland. en.wikipedia.org/wiki/TASM
Perdana
45

Apa yang Anda lihat saat Anda menggunakan Debug + Windows + Disassembly saat men-debug program C # adalah panduan yang baik untuk istilah-istilah ini. Berikut adalah versi yang dianotasi ketika saya menyusun program 'hello world' yang ditulis dalam C # dalam konfigurasi Rilis dengan pengoptimalan JIT diaktifkan:

        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

Klik kanan jendela dan centang "Show Code Bytes" untuk mendapatkan tampilan serupa.

Kolom di sebelah kiri adalah alamat kode mesin. Nilainya dipalsukan oleh debugger, kodenya sebenarnya terletak di tempat lain. Tapi itu bisa di mana saja, tergantung pada lokasi yang dipilih oleh compiler JIT, jadi debugger mulai menomori alamat dari 0 pada awal metode.

Kolom kedua adalah kode mesin . Angka 1 dan 0 aktual yang dijalankan CPU. Kode mesin, seperti di sini, biasanya ditampilkan dalam hex. Ilustratif mungkin adalah bahwa 0x8B memilih instruksi MOV, byte tambahan yang ada untuk memberi tahu CPU dengan tepat apa yang perlu dipindahkan. Perhatikan juga dua jenis instruksi CALL, 0xE8 adalah panggilan langsung, 0xFF adalah instruksi panggilan tidak langsung.

Kolom ketiga adalah kode assembly . Perakitan adalah bahasa sederhana, dirancang untuk memudahkan penulisan kode mesin. Ini dibandingkan dengan C # yang dikompilasi ke IL. Kompiler yang digunakan untuk menerjemahkan kode assembly disebut "assembler". Anda mungkin memiliki Microsoft assembler di komputer Anda, nama yang dapat dieksekusi adalah ml.exe, ml64.exe untuk versi 64-bit. Ada dua versi umum bahasa assembly yang digunakan. Yang Anda lihat adalah yang digunakan Intel dan AMD. Di dunia open source, perakitan dalam notasi AT&T adalah hal biasa. Sintaks bahasa sangat bergantung pada jenis CPU yang dibuat, bahasa assembly untuk PowerPC sangat berbeda.

Oke, itu menangani dua istilah dalam pertanyaan Anda. "Kode asli" adalah istilah fuzzy, ini tidak jarang digunakan untuk mendeskripsikan kode dalam bahasa yang tidak terkelola. Mungkin instruktif adalah untuk melihat jenis kode mesin apa yang dihasilkan oleh kompilator C. Ini adalah versi 'hello world' di 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   

Saya tidak memberi anotasi, terutama karena sangat mirip dengan kode mesin yang dihasilkan oleh program C #. Pemanggilan fungsi printf () sangat berbeda dari pemanggilan Console.WriteLine () tetapi yang lainnya hampir sama. Juga perhatikan bahwa debugger sekarang membuat alamat kode mesin yang sebenarnya dan itu sedikit lebih pintar tentang simbol. Efek samping dari membuat info debug setelah membuat kode mesin seperti yang sering dilakukan oleh kompiler yang tidak dikelola. Saya juga harus menyebutkan bahwa saya mematikan beberapa opsi pengoptimalan kode mesin untuk membuat kode mesin terlihat serupa. Kompiler C / C ++ memiliki lebih banyak waktu yang tersedia untuk mengoptimalkan kode, seringkali hasilnya sulit untuk ditafsirkan. Dan sangat sulit untuk di-debug.

Poin utama di sini adalah ada sangat sedikit perbedaan antara kode mesin yang dihasilkan dari bahasa yang dikelola oleh kompilator JIT dan kode mesin yang dihasilkan oleh kompilator kode asli. Yang merupakan alasan utama mengapa bahasa C # dapat bersaing dengan kompilator kode asli. Satu-satunya perbedaan nyata di antara mereka adalah panggilan fungsi dukungan. Banyak di antaranya diimplementasikan di CLR. Dan yang utama berputar di sekitar pengumpul sampah.

Hans Passant
sumber
6

Kode asli dan kode mesin adalah hal yang sama - byte sebenarnya yang dieksekusi oleh CPU.

Kode assembly memiliki dua arti: satu adalah kode mesin yang diterjemahkan ke dalam bentuk yang lebih dapat dibaca manusia (dengan byte untuk instruksi yang diterjemahkan menjadi mnemonik pendek mirip kata seperti "JMP" (yang "melompat" ke tempat lain dalam kode). Yang lainnya adalah bytecode IL (byte instruksi yang dihasilkan oleh kompiler seperti C # atau VB, yang pada akhirnya akan diterjemahkan ke dalam kode mesin, tetapi belum) yang hidup dalam DLL atau EXE.

cHao
sumber
2

Dalam .NET, majelis berisi kode MS Intermediate Language (MSIL, terkadang CIL).
Ini seperti kode mesin 'tingkat tinggi'.

Saat dimuat, MSIL dikompilasi oleh kompiler JIT menjadi kode asli (kode mesin Intel x86 atau x64).

Henk Holterman
sumber