อะไรคือความแตกต่างระหว่าง Native Code, Machine Code และ Assembly Code?


106

ฉันสับสนเกี่ยวกับรหัสเครื่องและรหัสเนทีฟในบริบทของภาษา. NET

อะไรคือความแตกต่างระหว่างพวกเขา? พวกเดียวกันหรือเปล่า?


3
ฉันมีคำถามเกี่ยวกับคำถามนี้ คำถามนี้อยู่ภายใต้ข้อกำหนดของ StackOverflow หรือไม่ afaik ไม่ใช่ แต่ในขณะเดียวกันคำถามประเภทนี้ก็มีประโยชน์ / ให้ข้อมูล สมมติว่าคำถามประเภทนี้ไม่ได้รับอนุญาตเราควรถามคำถามประเภทนี้ที่ไหนถ้าไม่ใช่ที่นี่?
Yousuf Azad

ดูเพิ่มเติม: stackoverflow.com/questions/334326/…
T.Todua

คำตอบ:


150

คำศัพท์นี้ค่อนข้างสับสนเนื่องจากบางครั้งมีการใช้ไม่สอดคล้องกัน

รหัสเครื่อง:เป็นรหัสที่กำหนดไว้อย่างดีที่สุด เป็นรหัสที่ใช้คำสั่งไบต์โค้ดซึ่งโปรเซสเซอร์ของคุณ (ชิ้นส่วนโลหะทางกายภาพที่ใช้งานได้จริง) เข้าใจและดำเนินการโดยตรง รหัสอื่น ๆ ทั้งหมดจะต้องถูกแปลหรือเปลี่ยนเป็นรหัสเครื่องก่อนที่เครื่องของคุณจะสามารถดำเนินการได้

รหัสดั้งเดิม:คำนี้บางครั้งใช้ในสถานที่ที่หมายถึงรหัสเครื่อง (ดูด้านบน) อย่างไรก็ตามบางครั้งก็ใช้เพื่อหมายถึงรหัสที่ไม่มีการจัดการ (ดูด้านล่าง)

รหัสที่ไม่มีการจัดการและการจัดการรหัส: ไม่มีการจัดการรหัสหมายถึงโค้ดที่เขียนในการเขียนโปรแกรมภาษาเช่น C หรือ C ++ ซึ่งจะรวบรวมโดยตรงในรหัสเครื่อง ตรงกันข้ามกับโค้ดที่มีการจัดการซึ่งเขียนด้วย C #, VB.NET, Java หรือที่คล้ายกันและดำเนินการในสภาพแวดล้อมเสมือนจริง (เช่น. NET หรือ JavaVM) ซึ่งประเภทของ "จำลอง" โปรเซสเซอร์ในซอฟต์แวร์ ความแตกต่างที่สำคัญคือรหัสที่มีการจัดการจะ "จัดการ" ทรัพยากร (ส่วนใหญ่เป็นการจัดสรรหน่วยความจำ) ให้คุณโดยใช้การรวบรวมขยะและการอ้างอิงถึงวัตถุที่ทึบแสง รหัสที่ไม่มีการจัดการเป็นรหัสประเภทที่กำหนดให้คุณต้องจัดสรรและยกเลิกการจัดสรรหน่วยความจำด้วยตนเองบางครั้งทำให้หน่วยความจำรั่วไหล (เมื่อคุณลืมยกเลิกการจัดสรร) และบางครั้งความผิดพลาดในการแบ่งส่วน (เมื่อคุณยกเลิกการจัดสรรเร็วเกินไป) โดยปกติแล้วUnmanagedจะหมายความว่าไม่มีการตรวจสอบเวลาทำงานสำหรับข้อผิดพลาดทั่วไปเช่นการอ้างอิงค่า null-pointer หรืออาร์เรย์ล้น

พูดอย่างเคร่งครัดภาษาที่พิมพ์แบบไดนามิกส่วนใหญ่เช่น Perl, Python, PHP และ Ruby เป็นโค้ดที่มีการจัดการเช่นกัน อย่างไรก็ตามพวกเขาไม่ได้ถูกอธิบายโดยทั่วไปเช่นนี้ซึ่งแสดงให้เห็นว่าโค้ดที่มีการจัดการนั้นค่อนข้างเป็นคำทางการตลาดสำหรับสภาพแวดล้อมการเขียนโปรแกรมเชิงพาณิชย์ที่ใหญ่โตจริงจัง (.NET และ Java)

รหัสแอสเซมบลี:โดยทั่วไปคำนี้หมายถึงประเภทของซอร์สโค้ดที่ผู้คนเขียนเมื่อต้องการเขียนไบต์โค้ดจริงๆ แอสเซมเบลอร์คือโปรแกรมที่เปลี่ยนซอร์สโค้ดนี้ให้เป็นไบต์โค้ดจริง ไม่ใช่คอมไพเลอร์เนื่องจากการแปลงเป็น 1 ต่อ 1 อย่างไรก็ตามคำดังกล่าวไม่ชัดเจนว่าจะใช้ไบต์โค้ดประเภทใด: สามารถจัดการหรือไม่จัดการได้ ถ้ามันเป็นที่ไม่มีการจัดการที่เกิดไบต์รหัสคือรหัสเครื่อง หากมีการจัดการจะส่งผลให้ไบต์โค้ดที่ใช้อยู่เบื้องหลังโดยสภาพแวดล้อมเสมือนเช่น. NET โค้ดที่มีการจัดการ (เช่น C #, Java) ถูกคอมไพล์เป็นภาษาไบต์โค้ดพิเศษนี้ซึ่งในกรณีของ. NET เรียกว่าCommon Intermediate Language (CIL)และใน Java เรียกว่าJava byte-code. โดยปกติโปรแกรมเมอร์ทั่วไปมีความจำเป็นเพียงเล็กน้อยในการเข้าถึงรหัสนี้หรือเขียนเป็นภาษานี้โดยตรง แต่เมื่อมีคนทำมักเรียกมันว่ารหัสแอสเซมบลีเนื่องจากใช้แอสเซมเบลอร์เพื่อเปลี่ยนเป็นไบต์โค้ด


C ++ สามารถคอมไพล์เป็นรหัสเครื่องได้ แต่มักจะคอมไพล์เป็นรูปแบบอื่น ๆ เช่น exe ที่จะทำงานด้วยระบบปฏิบัติการ
Gordon Gustafson

มีภาษาที่รองรับการรวบรวมขยะและการอ้างอิงแบบทึบซึ่งโดยทั่วไปจะคอมไพล์เป็นรหัสเครื่อง การใช้ Common Lisp ที่ร้ายแรงที่สุดทำเช่นนั้น สิ่งที่คุณพูดอาจเป็นจริงจากภาษาที่ Microsoft รองรับ แต่มีภาษาที่คอมไพล์มากกว่าที่ Visual Studio รองรับ
David Thornley

3
@CrazyJugglerDrummer: รหัสที่อยู่ในไฟล์ EXE ที่สร้างโดยคอมไพเลอร์ C ++ ยังคงเป็นรหัสเครื่อง @David Thornley: ฉันพูดถึงภาษามากกว่าภาษาเหล่านั้นอย่างมีนัยสำคัญ แต่ฉันไม่ต้องการทำให้เรื่องซับซ้อนโดยการพูดถึงความแปลกประหลาดที่คลุมเครือ
Timwi

คอมไพเลอร์บางตัวโดยมากจะคอมไพล์จากภาษา C / C ++ หรือภาษาอื่น ๆ เป็นภาษาแอสเซมบลีจากนั้นเรียกแอสเซมเบลอร์และแอสเซมเบลอร์จะเปลี่ยนเป็นอ็อบเจ็กต์ไฟล์ซึ่งส่วนใหญ่เป็นรหัสเครื่อง แต่ต้องสัมผัสเพียงเล็กน้อยก่อนจึงจะสามารถเข้าสู่หน่วยความจำบนโปรเซสเซอร์ได้ ตัวเชื่อมโยงจะเชื่อมโยงทั้งหมดเข้ากับเวอร์ชันรหัสเครื่องของโปรแกรม จุดที่เป็น C / C ++ และอื่น ๆ มักจะไม่รวบรวมโดยตรงกับรหัสเครื่องซึ่งผู้ใช้มองไม่เห็นทำขั้นตอนสองหรือสามขั้นตอน ตัวอย่างเช่น TCC เป็นข้อยกเว้นสำหรับรหัสเครื่องโดยตรง
old_timer

สิ่งนี้ให้ความรู้สึกเหมือน nitpicking แต่แอสเซมเบลอร์ทั้งหมดไม่ได้แปล 1-1 เป็น opcodes ในความเป็นจริงแอสเซมเบลอร์สมัยใหม่จำนวนมากสนับสนุนโครงสร้างที่เป็นนามธรรมเช่นคลาส ตัวอย่าง: TASM ผู้ประกอบของ Borland en.wikipedia.org/wiki/TASM
นายกรัฐมนตรี

45

สิ่งที่คุณเห็นเมื่อคุณใช้ Debug + Windows + Disassembly เมื่อดีบักโปรแกรม C # คือคำแนะนำที่ดีสำหรับข้อกำหนดเหล่านี้ นี่เป็นเวอร์ชันที่มีคำอธิบายประกอบเมื่อฉันรวบรวมโปรแกรม 'hello world' ที่เขียนด้วย 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

คลิกขวาที่หน้าต่างและเลือก "Show Code Bytes" เพื่อให้ได้การแสดงผลที่คล้ายกัน

คอลัมน์ทางด้านซ้ายคือที่อยู่รหัสเครื่อง ค่าของมันถูกปลอมโดยดีบักเกอร์รหัสนั้นอยู่ที่อื่นจริงๆ แต่อาจเป็นที่ใดก็ได้ขึ้นอยู่กับตำแหน่งที่เลือกโดยคอมไพเลอร์ JIT ดังนั้นดีบักเกอร์จะเริ่มการกำหนดหมายเลขแอดเดรสจาก 0 เมื่อเริ่มต้นเมธอด

คอลัมน์ที่สองเป็นรหัสเครื่อง 1s และ 0s จริงที่ CPU รัน รหัสเครื่องเช่นที่นี่มักแสดงเป็นเลขฐานสิบหก ตัวอย่างอาจเป็นไปได้ว่า 0x8B เลือกคำสั่ง MOV ไบต์เพิ่มเติมจะอยู่ที่นั่นเพื่อบอก CPU ว่าต้องย้ายอะไร โปรดสังเกตสองรสชาติของคำสั่ง CALL, 0xE8 คือการโทรโดยตรง, 0xFF คือคำสั่งการโทรทางอ้อม

คอลัมน์ที่สามเป็นรหัสการชุมนุม แอสเซมบลีเป็นภาษาง่ายๆออกแบบมาเพื่อให้เขียนโค้ดเครื่องได้ง่ายขึ้น เปรียบเทียบกับ C # ที่คอมไพล์เป็น IL คอมไพเลอร์ที่ใช้ในการแปลรหัสแอสเซมบลีเรียกว่า "แอสเซมเบลอร์" คุณอาจมีแอสเซมเบลอร์ของ Microsoft ในเครื่องของคุณชื่อปฏิบัติการคือ ml.exe, ml64.exe สำหรับเวอร์ชัน 64 บิต มีภาษาแอสเซมบลีทั่วไปสองเวอร์ชันที่ใช้งานอยู่ สิ่งที่คุณเห็นคือ Intel และ AMD ใช้ ในโลกโอเพนซอร์สการประกอบในสัญกรณ์ AT&T เป็นเรื่องปกติ ไวยากรณ์ของภาษาขึ้นอยู่กับชนิดของ CPU ที่เขียนขึ้นเป็นอย่างมากภาษาแอสเซมบลีสำหรับ PowerPC แตกต่างกันมาก

โอเคนั่นจัดการกับสองคำในคำถามของคุณ "Native code" เป็นคำที่คลุมเครือซึ่งไม่ใช่เรื่องผิดปกติที่ใช้เพื่ออธิบายโค้ดในภาษาที่ไม่มีการจัดการ คำแนะนำอาจเป็นการดูว่าคอมไพเลอร์ 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

รหัสดั้งเดิมและรหัสเครื่องเป็นสิ่งเดียวกัน - ไบต์จริงที่ CPU เรียกใช้

รหัสแอสเซมบลีมีสองความหมาย: หนึ่งคือรหัสเครื่องที่แปลเป็นรูปแบบที่มนุษย์อ่านได้มากขึ้น (โดยไบต์สำหรับคำสั่งที่แปลเป็นตัวช่วยจำ wordlike สั้น ๆ เช่น "JMP" (ซึ่ง "กระโดด" ไปยังจุดอื่นในโค้ด) อีกอัน คือรหัสไบต์ IL (ไบต์คำสั่งที่คอมไพเลอร์เช่น C # หรือ VB สร้างขึ้นซึ่งจะถูกแปลเป็นรหัสเครื่องในที่สุด แต่ยังไม่มี) ที่อยู่ใน DLL หรือ EXE


2

ใน. NET แอสเซมบลีประกอบด้วยรหัสMS Intermediate Language (MSIL, บางครั้ง CIL)
เปรียบเสมือนรหัสเครื่อง "ระดับสูง"

เมื่อโหลดแล้ว MSIL จะถูกคอมไพล์โดยคอมไพเลอร์ JITเป็นโค้ดเนทีฟ (รหัสเครื่อง Intel x86 หรือ x64)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.