Tại sao cần có máy ảo?


8

Thay vì biên dịch mã nguồn cho HĐH tương ứng (mà nó được nhắm mục tiêu), bạn biên dịch một lần và chạy ở mọi nơi.

Vì câu hỏi này, tôi sẽ gọi nó là VM (ví dụ, cả Java và .NET). Vì vậy, việc thực hiện các chương trình này trở thành một cái gì đó như

 ------------       ----      ----
| Executable |  -> | VM | -> | OS |
 ------------       ----      ----

Nó hoàn toàn có ý nghĩa, trình biên dịch vẫn chung cho VM tương ứng. Tuy nhiên, việc triển khai VM có thể khác nhau tùy thuộc vào máy sẽ được cài đặt tức là (* nix, windows, mac) x (32 bit, 64 bit).

Câu hỏi của tôi là, thay vì viết VM cho các máy tương ứng, tại sao trình biên dịch không được viết cho máy cụ thể đó? Bằng cách này, thay vì tải xuống VM tương ứng, bạn tải xuống trình biên dịch tương ứng và trình biên dịch đó sẽ chăm sóc mã máy + HĐH cho máy cụ thể đó. Kết quả cuối cùng, việc thực thi mã gốc cho bất kỳ máy nào. Chắc chắn, mỗi mã nguồn sẽ cần biên dịch cho máy cụ thể đó, nhưng bây giờ một ngày, các hệ thống tự động, bản dựng scm có thể giúp chúng ta làm điều này.

Liệu lý do của tôi bị nhầm lẫn là đúng hay tôi đang thiếu một số kỹ thuật ở đây?

Biên tập:

KHẢ NĂNG :

Vâng, đó là một lý do nhưng tính di động là một vấn đề lớn trong các hệ thống tự động ngày nay? Bao lâu thì chúng ta phải lo lắng về việc chúng ta không phải biên dịch nó cho các máy khác? Có một mã được biên dịch cho máy gốc sẽ cho hiệu năng tốt hơn nhiều. Lấy ví dụ Java, bạn không thể lập trình cấp thấp trên Windows và bạn phải chọn JNI.

Lấy các hệ thống tự động như TeamCity / Jenkins hoặc các hệ thống khác. Chúng tôi có thể có một thiết lập hệ thống tự động như vậy trong đó mã được gửi qua kiểm soát phiên bản sẽ dẫn đến việc thực thi.


5
bạn đã trả lời câu hỏi của riêng bạn trong đoạn đầu tiên
ratchet freak

6
'không phải là mới với Java. UCSD Pascal (chỉ cho một ví dụ) đã làm như vậy.
Jerry Coffin

2
@EmAe Và tôi sẽ chỉ ra điều trớ trêu là bạn đã chọn hai hệ thống xây dựng CI chạy trên JVM.
Andrew T Finnell

1
"thay vì tải xuống VM tương ứng, bạn tải xuống trình biên dịch tương ứng và trình biên dịch đó sẽ chăm sóc mã máy + HĐH cho máy cụ thể đó" - chúng tôi có cái gọi là C. Đó là bản chất của cách phần mềm được phân phối trên linux, ví dụ (trước các nhà quản lý gói). Tôi xin lỗi, nhưng tôi phải bỏ phiếu để đóng.
GrandmasterB

Mỗi trình biên dịch chỉ có một chuỗi các máy ảo bên trong. Bạn có thể dừng quá trình biên dịch tại một số điểm, nối tiếp biểu diễn trung gian đó và sau đó gọi phần còn lại của trình biên dịch là "trình thông dịch máy ảo". Nó sẽ không thay đổi bất cứ điều gì.
SK-logic

Câu trả lời:


16

Câu hỏi của tôi là, thay vì viết VM cho các máy tương ứng, tại sao trình biên dịch không được viết cho máy cụ thể đó?

Bởi vì sau đó bạn sẽ không còn có một thực thi di động; bạn sẽ có một tệp thực thi cho mỗi nền tảng. Người dùng sẽ phải tải xuống một tệp thực thi cụ thể cho nền tảng của họ hoặc tự biên dịch mã trên nền tảng cụ thể của họ.

Với VM, mỗi nền tảng chỉ cần VM dành riêng cho nền tảng của nó và sau đó bạn có thể phân phối cùng một tệp thực thi cho mọi nền tảng.


2
VM ngày nay có hiệu suất tốt hơn so với tín dụng của bạn. Việc sử dụng VM cung cấp cho bạn tất cả các tính năng được quản lý như hệ thống loại, khung thư viện và bộ sưu tập rác miễn phí.
Robert Harvey

3
@Robert: Hãy nhớ rằng, không phải tất cả các mã gốc là C hoặc C ++. Họ không chậm chạp trong việc viết chương trình vì họ là người bản địa và do đó "đối phó với nhiều rắc rối cấp thấp;" họ chậm viết các chương trình vì chúng là những ngôn ngữ được thiết kế kém, không làm tốt công việc xử lý các công cụ cấp thấp. Nhưng bạn có thể có được hiệu suất cấp C từ Delphi với cùng một cách dễ dàng phát triển mà bạn tìm thấy trong các ngôn ngữ được quản lý.
Mason Wheeler

7
@Andrew: Mọi người có thể nói Java nhanh theo một số điểm chuẩn kiểm tra một số thuật toán trong một số trường hợp bị cô lập. Nhưng sau đó, bạn đi và chạy một chương trình Java thực tế, như OpenOffice, và phải mất 30 giây để mở hộp thoại Save kỳ dị, so với khoảng 1-2 giây trên mã gốc và họ đi đến kết luận rằng tất cả tốc độ lý thuyết đó trong điểm chuẩn có nghĩa là không có gì; Java vẫn chậm.
Mason Wheeler

2
@MasonWheeler Khi bạn xóa bình luận của mình và mở rộng trên một bình luận mới. Sự phong phú của những người có thể thoát khỏi "lập trình" trong ngôn ngữ Java cao hơn đáng kể vì rào cản gia nhập C và C ++ là nghiêm cấm, do đó các chương trình thực sự tồi tệ. Java không chậm và bằng chứng giai thoại của một bộ ứng dụng khủng khiếp không chứng minh điều gì khác.
Andrew T Finnell

10
@MasonWheeler: Chỉ để xóa một quan niệm sai lầm phổ biến: OpenOffice không được viết bằng Java - chủ yếu là bằng C ++. Nó chỉ cần Java cho một số chức năng đặc biệt. Xem wiki.service.openoffice.org/wiki/Java_and_OpenOffice.org .
sleske

12

Bạn đang thiếu một cái gì đó rất lớn với những VM này. Họ làm chính xác những gì bạn nói, nhưng tự động. Nó được gọi là Trình biên dịch đúng lúc và đó là lý do tại sao .NET (Trên Windows) và Java cực kỳ gần với tốc độ của mã C ++ được biên dịch tự nhiên.

Mã nguồn Java / C # được chuyển đổi thành mã byte. Mã byte này sau đó được biên dịch thành mã máy trên máy hiện đang chạy. Đối với hầu hết các phần, VM sẽ chạy mã gốc thay vì xử lý lại mã byte.

Tôi đã bỏ qua khá nhiều và quá đơn giản hóa quy trình, nhưng VM làm rất nhiều việc.


Bạn không nhận được câu hỏi của tôi. Tôi không nghi ngờ về hiệu suất của mã khi nó đang được chạy trên VM cụ thể. Câu hỏi là tại sao sử dụng VM khi chúng ta có thể có trình biên dịch để biên dịch nó cho máy cụ thể.
Em Ae

2
@EmAe VM DOES biên dịch nó cho máy cụ thể. Nó được gọi là JIT. Tôi rất đề nghị bạn đọc lên trên nó.
Andrew T Finnell

1
Được rồi, tôi nhầm lẫn ở đây. Sửa lỗi và giúp tôi. Vì vậy, trình biên dịch biên dịch cho máy cụ thể ngay cả sau đó nó được chọn để được thực thi trên VM? Nếu mã được biên dịch đó (giả sử JAVA chẳng hạn) được chuyển sang VM trên UNIX / Mac / Ubuntu thì điều này có cần phải biên dịch lại không? KHÔNG. Bởi vì đối với VM, nguồn gốc là BYTE-CODE và BYTE-CODE sau đó được chuyển đổi bằng JIT. Program -> Compiled to byte code -> JIT for machine -> Execution. 4 bước có liên quan. Tôi hỏi rằng tại sao chúng ta sử dụng 4 bước này? sau đó chúng ta có thểProgram -> Machine specific compiler -> Execute
Em Ae

Lý do tương tự bạn không viết lại tất cả mã của bạn nhiều lần trong hội đồng. Tái sử dụng.
Andrew T Finnell

2
Thêm vào đó, không ai nói bạn phải thực hiện bốn bước đó, bạn luôn có thể sử dụng trình biên dịch AOT. gcc.gnu.org/java
TomJ

7

Giống như nhiều khái niệm trong khoa học máy tính, VM cung cấp một lớp trừu tượng. Bạn viết mã theo 'giao diện' (mã byte / ngôn ngữ trung gian) và lớp trừu tượng (VM) xử lý các chi tiết triển khai (biên dịch mã cho máy đích và các tác vụ khác.) Lớp trừu tượng cung cấp cho bạn một bộ về các dịch vụ có chi tiết triển khai mà bạn không còn cần quan tâm. Trong trường hợp này, bạn không còn phải lo lắng về các chi tiết cụ thể của phần cứng cơ bản, với điều kiện là các dịch vụ lớp trừu tượng (VM) có mặt. Các lợi ích của khách hàng theo cách tương tự - họ không cần biết chi tiết về nền tảng của họ, chỉ là họ có thể sử dụng lớp trừu tượng.

Tất nhiên, có sự đánh đổi với bất kỳ sự trừu tượng. Bạn mất quyền kiểm soát chi tiết đối với các chi tiết ở phía bên kia của sự trừu tượng hóa và phải dựa vào sự trừu tượng đó để sử dụng một triển khai hợp lý. Bạn cần xem xét lợi ích tiềm năng thông qua việc sử dụng một sự trừu tượng chống lại sự đánh đổi. Trong một số ứng dụng nhất định, nhược điểm có thể cân nhắc lợi ích - bạn có thể cần mức độ kiểm soát cao đối với từng nền tảng.

Đề xuất của bạn về việc sử dụng một hệ thống tự động để biên dịch cho các nền tảng khác nhau chính xác là những gì mà các lớp trừu tượng .NET và Java đã làm. Một trong những lợi ích của việc biên dịch JIT là trình biên dịch có các chi tiết cụ thể về máy mà nó đang chạy. Điều này có thể giới thiệu tiềm năng tối ưu hóa mà nếu không thực hiện được khi xây dựng bản phát hành trên máy của nhà phát triển.

Nếu bạn lo lắng về việc chậm thời gian chạy do tạo mã gốc và thực thi chương trình xảy ra cùng nhau, bạn có tùy chọn tạo mã gốc trong khi cài đặt thay vì khi chương trình được thực thi lần đầu tiên. .NET cung cấp nGen cho mục đích này, có thể thực hiện tạo mã gốc khi cài đặt thay vì thời gian chạy. CLR sau đó sẽ sử dụng mã gốc được lưu trong bộ nhớ cache thay vì thực hiện biên dịch JIT.


3

Bạn đang quá đơn giản hóa điều này rất nhiều, đa nền tảng không chỉ đơn thuần là biên dịch cho một tập lệnh gốc cụ thể. Phổ biến hơn không, mã C / C ++ giống nhau không thể được biên dịch lại trên các nền tảng khác nhau vì API: s và thư viện khác nhau. Ví dụ, phát triển GUI rất khác nhau trên Windows và Ubuntu Linux, giao tiếp ổ cắm có thể không giống nhau trên Unix và IBM z / OS, v.v.

Vì vậy, để đạt được "viết một lần, biên dịch (và liên kết) ở bất cứ đâu", bạn sẽ phải tạo một lớp trừu tượng của một số loại, trong đó bạn tạo một API chung cho tất cả các dịch vụ cấp hệ điều hành. Sau đó, bạn cần triển khai và phân phối điều này cho tất cả các nền tảng bạn muốn hỗ trợ.

Ngoài ra, trong khi các mô hình bộ nhớ ngày càng giống nhau ngày nay, vẫn còn một số khác biệt giữa các nền tảng khác nhau, do đó bạn cũng cần phân bổ bộ nhớ trừu tượng. Dễ nhất ở đây có lẽ là để tạo trình quản lý bộ nhớ "ảo" của riêng bạn trên đầu trang gốc.

Trong khi bạn đang ở đó, các hệ thống tệp và kiểm soát truy cập (ACL) được xử lý khá khác nhau giữa Unix và Windows, do đó bạn sẽ cần phải trừu tượng hóa những điều đó. Thậm chí có thể tạo trình bao bọc "Tệp" của riêng bạn để triển khai cơ bản.

Sau đó, chúng tôi có luồng. Vì Linux chỉ có các tiến trình và Windows có các luồng, nên chúng ta cũng cần phải trừu tượng hóa điều này.

Chà, tại thời điểm này bạn đã xây dựng khá nhiều máy ảo. Vấn đề ở đây là trong khi biên dịch một số mã tùy ý cho các nền tảng khác nhau khá dễ dàng, việc xây dựng phần mềm làm việc với bất kỳ sự phức tạp nào có thể chạy trên bất kỳ nền tảng nào đều không quan trọng.


2

Một ưu điểm chính khác của phương pháp VM / JIT là khả năng tương thích nhị phân. Ví dụ: giả sử bạn có một hội đồng / JAR / DLL chứa lớp A và một hội đồng khác có chứa lớp B, xuất phát từ lớp A. Điều gì xảy ra nếu bạn thay đổi lớp A, ví dụ: bạn thêm một thành viên riêng? Thêm một thành viên riêng không nên có bất kỳ ảnh hưởng nào đến lớp B, nhưng nếu tập hợp chứa B đã được biên dịch thành mã gốc, bạn có thể sẽ gặp phải các lỗi lạ và các sự cố khó tái tạo, bởi vì mã gốc được biên dịch với cách bố trí bộ nhớ cũ của A, do đó, nó sẽ dành quá ít bộ nhớ cho các biến của A, vì vậy các thành viên của A và các thành viên B được thêm vào sẽ được ánh xạ tới cùng một vị trí bộ nhớ.

Mặt khác, nếu bạn đang sử dụng VM, tất cả các vấn đề này sẽ biến mất, bởi vì khi mã được biên dịch thành mã gốc, bố cục của tất cả các lớp được biết đến.


2

Câu hỏi của tôi là, thay vì viết VM cho các máy tương ứng, tại sao trình biên dịch không được viết cho máy cụ thể đó?

Hãy tưởng tượng trong giây lát rằng đây là cách nó hoạt động. Tôi viết một ứng dụng Java, sau đó tôi biên dịch - trình biên dịch sau đó tạo ra một tệp thực thi cho mọi nền tảng được hỗ trợ. Bây giờ tôi có các tệp thực thi sau:

  • Solaris x64
  • Solaris x86
  • Solaris SPARC
  • Windows x86
  • Windows x64
  • Linux x86
  • Linux x64
  • Hệ điều hành Mac
  • BSD

Vân vân.

Sau đó tôi tải tất cả những thứ này lên trang web của mình và cung cấp một liên kết tải xuống cho mỗi cái. Sau đó tôi gửi tất cả những thứ này đến download.com, tucows.com, sofpedia.com, gửi từng cái một.

Đây dường như là một nỗi đau lớn với tôi! Khi tôi cập nhật phần mềm lên phiên bản 1.1 thì tôi cần lặp lại quy trình này.

Và điều gì xảy ra khi một nền tảng mới xuất hiện? Bây giờ phần mềm của tôi không có sẵn cho nền tảng này, trừ khi tôi cập nhật phần mềm dành cho nhà phát triển của mình để hỗ trợ, biên dịch lại, sau đó phát hành lại phần mềm của tôi cho nền tảng mới này.

Còn người dùng thì sao? Họ truy cập trang web của tôi và sau đó phải chọn từ danh sách chính xác phiên bản phần mềm phù hợp của tôi để sử dụng cho nền tảng của họ. Hầu hết người dùng không biết liệu họ chạy x86 hay x64, vì vậy họ có thể sẽ tải xuống phiên bản sai và nhận được một số lỗi. Điều gì sẽ xảy ra nếu sau đó họ chuyển từ Windows sang Mac, bây giờ họ cần quay lại và tải xuống lại phần mềm của tôi.

Tất cả điều này là một nỗi đau lớn, đối với cả nhà phát triển và người dùng và tất cả những điều này có thể tránh được bằng cách chỉ biên dịch sang mã byte sau đó chạy qua VM, chính xác như Java hiện đang làm.

Nếu hiệu suất gần giống với chạy mã gốc, thì câu hỏi không phải là quá nhiều tại sao không biên dịch thành mã gốc, thay vì tại sao phải biên dịch sang mã gốc?


1

Trong phần chỉnh sửa bài đăng của bạn, bạn đặt câu hỏi về tính hữu ích của tính di động, wrt VM.

Trong cuộc sống chuyên nghiệp của tôi, tính di động đã được các yếu tố quan trọng nhất - nền tảng triển khai của tôi là rất hiếm khi cùng một nền tảng mà tôi triển khai trên, và vì vậy tôi có thể phát triển và thử nghiệm trên Windows, vượt qua nợ QA của tôi, những người sẽ kiểm tra trên Linux và triển khai vào môi trường sản xuất của chúng tôi trên Solaris.

Đây không chỉ là một ví dụ đơn độc và thiếu thốn - điều này gần như là bánh mì của sự tồn tại nghề nghiệp của tôi trong suốt 12 năm qua trong sự nghiệp kéo dài nhiều thập kỷ của tôi. Tôi chắc chắn có nhiều người khác có cùng trải nghiệm hoặc tương tự.

Nếu chúng tôi sử dụng các trình biên dịch (chéo) khác nhau (trên cùng một mã nguồn) để tạo ra các nhị phân khác nhau, thì bạn không thể cảm thấy tự tin về chất lượng của từng nhị phân, nếu chúng không được kiểm tra trên HĐH / Kiến trúc riêng.

Dự án Trình biên dịch FreePascal mã nguồn mở tuyệt vời có dòng thẻ "ghi một lần, biên dịch ở bất cứ đâu" - trong khi điều này là đúng, tôi không mong đợi thấy bất kỳ nhà phần mềm có uy tín nào làm như vậy và phát hành ứng dụng mà không cần kiểm tra kỹ lưỡng trên tất cả các nền tảng.


Tôi vẫn sẽ đặt câu hỏi về tính hợp lệ của thử nghiệm trên một nền tảng và triển khai trên nền tảng khác, ngay cả khi sử dụng VM. Bạn đang triển khai trên hai triển khai VM khác nhau, một lỗi có thể chứa một lỗi cụ thể của hệ thống không có trong lỗi khác. Cơ hội rất mong manh, nhưng vẫn nên thử nghiệm và triển khai trên các hệ thống giống hệt nhau.
Gavin Coates

TBH, tôi thực sự đồng ý, nhưng việc kiểm tra VM có dễ hơn trình biên dịch không? (vd số ít người khác) đã biên soạn ứng dụng của bạn.
Crollster

Crollster: Tôi đồng ý. trong thế giới thực, bạn sẽ sử dụng phần cứng và phần mềm giống hệt nhau, nhưng điều này đôi khi không thể. Sử dụng một nền tảng khác nhau có thể được chấp nhận trong hầu hết các trường hợp, nhưng điều đáng lưu ý là có nguy cơ xảy ra lỗi, mặc dù tương đối nhỏ.
Gavin Coates

1

Dễ dàng: kiểm tra.

Nếu mã hoạt động tốt trên Máy ảo, bạn có thể tin tưởng rằng nó sẽ chạy trên phần cứng khác đã được chứng minh là có VM hoạt động.

Nếu bạn biên dịch cho các nền tảng khác nhau, bạn có rất nhiều thử nghiệm phải làm, bởi vì mọi nền tảng đều có những đặc điểm riêng.


0

Có nhiều tính di động hơn là chỉ có thể biên dịch mã!

Hành vi của một chương trình C có thể khác nhau trên các nền tảng nhiệt tình:

int  i;
char c[6];
int * p;

p = &c[2];
p = p + 1;

Hoạt động mà không có khiếu nại trên chip IBM Power, nhưng, thông qua một ngoại lệ trên chip SPARC.

Hệ điều hành, phiên bản hệ điều hành, cài đặt đăng ký biến môi trường hệ thống tệp đều có thể ảnh hưởng đến cách chương trình được biên dịch sẽ chạy.

Một JVM đảm bảo khá nhiều hành vi tương tự cho dù môi trường phần cứng và phần mềm.

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.