ฉันสับสนเกี่ยวกับรหัสเครื่องและรหัสเนทีฟในบริบทของภาษา. NET
อะไรคือความแตกต่างระหว่างพวกเขา? พวกเดียวกันหรือเปล่า?
ฉันสับสนเกี่ยวกับรหัสเครื่องและรหัสเนทีฟในบริบทของภาษา. NET
อะไรคือความแตกต่างระหว่างพวกเขา? พวกเดียวกันหรือเปล่า?
คำตอบ:
คำศัพท์นี้ค่อนข้างสับสนเนื่องจากบางครั้งมีการใช้ไม่สอดคล้องกัน
รหัสเครื่อง:เป็นรหัสที่กำหนดไว้อย่างดีที่สุด เป็นรหัสที่ใช้คำสั่งไบต์โค้ดซึ่งโปรเซสเซอร์ของคุณ (ชิ้นส่วนโลหะทางกายภาพที่ใช้งานได้จริง) เข้าใจและดำเนินการโดยตรง รหัสอื่น ๆ ทั้งหมดจะต้องถูกแปลหรือเปลี่ยนเป็นรหัสเครื่องก่อนที่เครื่องของคุณจะสามารถดำเนินการได้
รหัสดั้งเดิม:คำนี้บางครั้งใช้ในสถานที่ที่หมายถึงรหัสเครื่อง (ดูด้านบน) อย่างไรก็ตามบางครั้งก็ใช้เพื่อหมายถึงรหัสที่ไม่มีการจัดการ (ดูด้านล่าง)
รหัสที่ไม่มีการจัดการและการจัดการรหัส: ไม่มีการจัดการรหัสหมายถึงโค้ดที่เขียนในการเขียนโปรแกรมภาษาเช่น 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. โดยปกติโปรแกรมเมอร์ทั่วไปมีความจำเป็นเพียงเล็กน้อยในการเข้าถึงรหัสนี้หรือเขียนเป็นภาษานี้โดยตรง แต่เมื่อมีคนทำมักเรียกมันว่ารหัสแอสเซมบลีเนื่องจากใช้แอสเซมเบลอร์เพื่อเปลี่ยนเป็นไบต์โค้ด
สิ่งที่คุณเห็นเมื่อคุณใช้ 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 และนั่นหมุนรอบตัวเก็บขยะเป็นหลัก
รหัสดั้งเดิมและรหัสเครื่องเป็นสิ่งเดียวกัน - ไบต์จริงที่ CPU เรียกใช้
รหัสแอสเซมบลีมีสองความหมาย: หนึ่งคือรหัสเครื่องที่แปลเป็นรูปแบบที่มนุษย์อ่านได้มากขึ้น (โดยไบต์สำหรับคำสั่งที่แปลเป็นตัวช่วยจำ wordlike สั้น ๆ เช่น "JMP" (ซึ่ง "กระโดด" ไปยังจุดอื่นในโค้ด) อีกอัน คือรหัสไบต์ IL (ไบต์คำสั่งที่คอมไพเลอร์เช่น C # หรือ VB สร้างขึ้นซึ่งจะถูกแปลเป็นรหัสเครื่องในที่สุด แต่ยังไม่มี) ที่อยู่ใน DLL หรือ EXE
ใน. NET แอสเซมบลีประกอบด้วยรหัสMS Intermediate Language (MSIL, บางครั้ง CIL)
เปรียบเสมือนรหัสเครื่อง "ระดับสูง"
เมื่อโหลดแล้ว MSIL จะถูกคอมไพล์โดยคอมไพเลอร์ JITเป็นโค้ดเนทีฟ (รหัสเครื่อง Intel x86 หรือ x64)