Tôi nhầm lẫn về mã máy và mã gốc trong ngữ cảnh của các ngôn ngữ .NET.
sự khác biệt giữa chúng là gì? Chúng có giống nhau không?
Tôi nhầm lẫn về mã máy và mã gốc trong ngữ cảnh của các ngôn ngữ .NET.
sự khác biệt giữa chúng là gì? Chúng có giống nhau không?
Câu trả lời:
Các thuật ngữ thực sự là một chút khó hiểu, bởi vì chúng đôi khi được sử dụng không nhất quán.
Mã máy: Đây là mã được xác định rõ ràng nhất. Nó là mã sử dụng các lệnh byte-mã mà bộ xử lý của bạn (mảnh kim loại vật lý hoạt động thực tế) hiểu và thực thi trực tiếp. Tất cả các mã khác phải được dịch hoặc chuyển đổi thành mã máy trước khi máy của bạn có thể thực thi.
Mã gốc: Thuật ngữ này đôi khi được sử dụng ở những nơi có nghĩa là mã máy (xem ở trên). Tuy nhiên, đôi khi nó cũng được dùng để chỉ mã không được quản lý (xem bên dưới).
Unmanaged code và mã số quản lý: không được quản lý đang đề cập đến mã được viết bằng một ngôn ngữ lập trình như C hoặc C ++, được biên dịch trực tiếp vào mã máy . Nó trái ngược với mã được quản lý , được viết bằng C #, VB.NET, Java hoặc tương tự và được thực thi trong môi trường ảo (chẳng hạn như .NET hoặc JavaVM), loại “mô phỏng” một bộ xử lý trong phần mềm. Sự khác biệt chính là mã được quản lý “quản lý” các tài nguyên (chủ yếu là cấp phát bộ nhớ) cho bạn bằng cách sử dụng bộ sưu tập rác và bằng cách giữ cho các tham chiếu đến các đối tượng không rõ ràng. Mã không được quản lýlà loại mã yêu cầu bạn cấp phát và khử cấp phát bộ nhớ theo cách thủ công, đôi khi gây ra rò rỉ bộ nhớ (khi bạn quên cấp phát) và đôi khi là lỗi phân đoạn (khi bạn cấp phát quá sớm). Không được quản lý cũng thường ngụ ý rằng không có kiểm tra thời gian chạy cho các lỗi phổ biến như tham chiếu null-pointer hoặc tràn giới hạn mảng.
Nói một cách chính xác, hầu hết các ngôn ngữ được gõ động - chẳng hạn như Perl, Python, PHP và Ruby - cũng là mã được quản lý . Tuy nhiên, chúng không thường được mô tả như vậy, điều này cho thấy rằng mã được quản lý thực sự là một thuật ngữ tiếp thị cho các môi trường lập trình thương mại thực sự lớn, nghiêm túc (.NET và Java).
Mã hội: Thuật ngữ này thường dùng để chỉ loại mã nguồn mà mọi người viết khi họ thực sự muốn viết mã byte. Trình hợp dịch là một chương trình biến mã nguồn này thành mã byte thực. Nó không phải là một trình biên dịch vì quá trình chuyển đổi là 1-to-1. Tuy nhiên, thuật ngữ này không rõ ràng về loại mã byte được sử dụng: nó có thể được quản lý hoặc không được quản lý. Nếu nó không được quản lý, mã byte kết quả là mã máy . Nếu nó được quản lý, nó sẽ dẫn đến mã byte được sử dụng ở hậu trường bởi một môi trường ảo như .NET. Mã được quản lý (ví dụ: C #, Java) được biên dịch thành ngôn ngữ mã byte đặc biệt này, trong trường hợp .NET được gọi là Ngôn ngữ trung gian chung (CIL) và trong Java được gọi là mã byte-mã Java. Thông thường lập trình viên thông thường ít cần phải truy cập mã này hoặc viết trực tiếp bằng ngôn ngữ này, nhưng khi mọi người làm vậy, họ thường gọi nó là mã hợp ngữ vì họ sử dụng trình hợp dịch để biến nó thành mã byte.
Những gì bạn thấy khi sử dụng Gỡ lỗi + Windows + Disassembly khi gỡ lỗi chương trình C # là hướng dẫn tốt cho các thuật ngữ này. Đây là phiên bản có chú thích của nó khi tôi biên dịch chương trình 'hello world' được viết bằng C # trong cấu hình Phát hành có bật tính năng tối ưu hóa 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
Nhấp chuột phải vào cửa sổ và đánh dấu vào "Show Code Bytes" để hiển thị tương tự.
Cột bên trái là địa chỉ mã máy. Giá trị của nó được giả mạo bởi trình gỡ lỗi, mã thực sự nằm ở một nơi khác. Nhưng đó có thể là bất cứ đâu, tùy thuộc vào vị trí được chọn bởi trình biên dịch JIT, vì vậy trình gỡ lỗi chỉ bắt đầu đánh số địa chỉ từ 0 khi bắt đầu phương thức.
Cột thứ hai là mã máy . 1 và 0 thực tế mà CPU thực thi. Mã máy, như ở đây, thường được hiển thị dưới dạng hex. Minh họa có lẽ là 0x8B chọn lệnh MOV, các byte bổ sung ở đó để cho CPU biết chính xác những gì cần được di chuyển. Cũng lưu ý hai hương vị của lệnh CALL, 0xE8 là lệnh gọi trực tiếp, 0xFF là lệnh gọi gián tiếp.
Cột thứ ba là mã lắp ráp . Assembly là một ngôn ngữ đơn giản, được thiết kế để giúp viết mã máy dễ dàng hơn. Nó so với C # đang được biên dịch sang IL. Trình biên dịch được sử dụng để dịch mã hợp ngữ được gọi là "trình hợp dịch". Bạn có thể có trình hợp dịch Microsoft trên máy của mình, tên thực thi của nó là ml.exe, ml64.exe cho phiên bản 64-bit. Có hai phiên bản phổ biến của hợp ngữ được sử dụng. Cái bạn thấy là cái mà Intel và AMD sử dụng. Trong thế giới mã nguồn mở, việc lắp ráp theo ký hiệu AT&T là phổ biến. Cú pháp ngôn ngữ phụ thuộc nhiều vào loại CPU được viết, ngôn ngữ hợp ngữ cho PowerPC rất khác nhau.
Được rồi, nó giải quyết hai thuật ngữ trong câu hỏi của bạn. "Mã gốc" là một thuật ngữ mờ, nó không được sử dụng phổ biến để mô tả mã bằng ngôn ngữ không được quản lý. Hướng dẫn có lẽ là xem loại mã máy nào được tạo bởi trình biên dịch C. Đây là phiên bản 'hello world' trong 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
Tôi đã không chú thích nó, chủ yếu là vì nó rất giống với mã máy được tạo bởi chương trình C #. Lời gọi hàm printf () khá khác với lệnh gọi Console.WriteLine () nhưng mọi thứ khác đều giống nhau. Cũng lưu ý rằng trình gỡ lỗi hiện đang tạo địa chỉ mã máy thực và nó thông minh hơn một chút về các ký hiệu. Một tác dụng phụ của việc tạo thông tin gỡ lỗi sau khi tạo mã máy giống như các trình biên dịch không được quản lý thường làm. Tôi cũng nên đề cập rằng tôi đã tắt một vài tùy chọn tối ưu hóa mã máy để làm cho mã máy trông giống nhau. Các trình biên dịch C / C ++ có nhiều thời gian hơn để tối ưu hóa mã, kết quả thường khó diễn giải. Và rất khó để gỡ lỗi.
Điểm chính ở đây là có rất ít sự khác biệt giữa mã máy được tạo từ ngôn ngữ được quản lý bởi trình biên dịch JIT và mã máy được tạo bởi trình biên dịch mã gốc. Đó là lý do chính tại sao ngôn ngữ C # có thể cạnh tranh với trình biên dịch mã gốc. Sự khác biệt thực sự duy nhất giữa chúng là các lệnh gọi hàm hỗ trợ. Nhiều trong số đó được thực hiện trong CLR. Và điều đó chủ yếu xoay quanh người thu gom rác.
Mã gốc và mã máy giống nhau - các byte thực tế mà CPU thực thi.
Mã hội có hai nghĩa: một là mã máy được dịch sang dạng dễ đọc hơn của con người (với các byte cho các lệnh được dịch thành các thuật nhớ ngắn gọn giống như từ như "JMP" ("nhảy" sang một vị trí khác trong mã). là mã byte IL (byte lệnh mà các trình biên dịch như C # hoặc VB tạo ra, cuối cùng sẽ được dịch thành mã máy, nhưng vẫn chưa) sống trong DLL hoặc EXE.
Trong .NET, hợp ngữ chứa mã Ngôn ngữ trung gian MS (MSIL, đôi khi là CIL).
Nó giống như một mã máy 'cấp cao'.
Khi được tải, MSIL được trình biên dịch JIT biên dịch thành mã gốc (mã máy Intel x86 hoặc x64).