Sự khác biệt giữa mã gốc, mã máy và mã lắp ráp là gì?


106

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?


3
Tôi có một câu hỏi liên quan đến câu hỏi này. Câu hỏi này có thuộc yêu cầu của StackOverflow không? afaik thì không, nhưng đồng thời loại câu hỏi này rất hữu ích / cung cấp thông tin. Giả sử loại câu hỏi này không được phép, nếu không phải ở đây thì chúng ta nên đặt những câu hỏi kiểu này ở đâu?
Yousuf Azad

Câu trả lời:


150

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à đượ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 codemã 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.


C ++ có thể biên dịch sang mã máy, nhưng nó rất thường được biên dịch sang các định dạng khác như exe sẽ chạy với hệ điều hành.
Gordon Gustafson

Có những ngôn ngữ hỗ trợ thu thập rác và các tham chiếu không rõ ràng thường được biên dịch thành mã máy. Hầu hết các triển khai nghiêm túc của Common Lisp đều làm được điều đó. Những gì bạn nói có thể đúng với các ngôn ngữ được Microsoft hỗ trợ, nhưng có nhiều ngôn ngữ biên dịch hơn được Visual Studio hỗ trợ.
David Thornley

3
@CrazyJugglerDrummer: Mã chứa trong tệp EXE được tạo bởi trình biên dịch C ++ vẫn là mã máy. @David Thornley: Tôi đã đề cập đến nhiều ngôn ngữ hơn đáng kể so với chỉ những ngôn ngữ đó, nhưng tôi không muốn làm phức tạp vấn đề bằng cách đề cập đến mọi điều kỳ lạ khó hiểu.
Timwi

Một số trình biên dịch sẽ thực sự biên dịch từ C / C ++ hoặc các ngôn ngữ khác sang hợp ngữ sau đó gọi trình hợp dịch và trình hợp dịch biến nó thành các tệp đối tượng chủ yếu là mã máy nhưng cần một vài lần chạm trước khi chúng có thể đi vào bộ nhớ trên bộ xử lý. trình liên kết liên kết tất cả chúng vào phiên bản mã máy của chương trình. Vấn đề là C / C ++, v.v. thường không biên dịch thẳng sang mã máy, nó vô hình đối với người dùng thực hiện một hai hoặc ba bước trên đường. Ví dụ, TCC là một ngoại lệ, nó đi trực tiếp vào mã máy.
old_timer

Điều này giống như nitpicking, nhưng không phải tất cả các nhà lắp ráp đều dịch 1-1 thành opcodes. Trên thực tế, nhiều trình lắp ráp hiện đại hỗ trợ các cấu trúc trừu tượng như các lớp. Ví dụ: TASM, trình lắp ráp của Borland. vi.wikipedia.org/wiki/TASM
Thủ tướng Chính phủ

45

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.


6

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.


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.