Tại sao hệ điều hành phần mềm cụ thể?


77

Tôi đang cố gắng xác định các chi tiết kỹ thuật về lý do tại sao phần mềm được sản xuất bằng ngôn ngữ lập trình cho các hệ điều hành nhất định chỉ hoạt động với chúng.

Theo hiểu biết của tôi, các tệp nhị phân là dành riêng cho một số bộ xử lý do ngôn ngữ máy cụ thể của bộ xử lý mà chúng hiểu và các bộ hướng dẫn khác nhau giữa các bộ xử lý khác nhau. Nhưng tính đặc thù của hệ điều hành đến từ đâu? Tôi đã từng cho rằng đó là API do HĐH cung cấp nhưng sau đó tôi thấy sơ đồ này trong một cuốn sách: Sơ đồ

Hệ điều hành - Nguyên tắc thiết kế và nội bộ lần thứ 7 - W. Stallings (Pearson, 2012)

Như bạn có thể thấy, API không được chỉ định là một phần của hệ điều hành.

Ví dụ: nếu tôi xây dựng một chương trình đơn giản trong C bằng mã sau:

#include<stdio.h>

main()
{
    printf("Hello World");

}

Là trình biên dịch làm bất cứ điều gì hệ điều hành cụ thể khi biên dịch này?


15
Bạn có in ra cửa sổ không? hay một bàn điều khiển? hoặc vào bộ nhớ đồ họa? Làm thế nào để bạn đặt dữ liệu ở đó? Nhìn vào printf cho Apple] [+ sẽ yên tĩnh hơn so với Mac OS 7 và một lần nữa khá khác so với Mac OS X (chỉ gắn bó với một 'dòng' máy tính).

3
Bởi vì nếu bạn đã viết mã đó cho Mac OS 7, nó sẽ hiển thị dưới dạng văn bản trong một cửa sổ mới. Nếu bạn đã làm điều đó trên Apple] [+, nó sẽ được ghi trực tiếp vào một số đoạn bộ nhớ. Trên Mac OS X, nó ghi nó ra bàn điều khiển. Vì vậy, đó là ba cách viết khác nhau để xử lý mã dựa trên phần cứng thực thi được xử lý bởi lớp thư viện.

2
@StevenBurnap yep - en.wikipedia.org/wiki/Aztec_C

10
Chức năng FFT của bạn sẽ vui vẻ chạy trong Windows hoặc Linux (trên cùng CPU) mà không cần biên dịch lại. Nhưng sau đó bạn sẽ hiển thị kết quả như thế nào? Sử dụng API hệ điều hành, tất nhiên. ( printftừ msvcr90.dll không giống như printftừ libc.so.6)
user253751

9
Ngay cả khi các API "không phải là một phần của hệ điều hành", chúng vẫn khác nhau nếu bạn đi từ hệ điều hành này sang hệ điều hành khác. (Tất nhiên, điều này đặt ra câu hỏi cụm từ "không phải là một phần của hệ điều hành" thực sự có nghĩa là gì, theo sơ đồ.)
Theodoros Chatzigiannakis

Câu trả lời:


78

Bạn đề cập đến việc làm thế nào nếu mã dành riêng cho CPU, tại sao nó cũng phải cụ thể đối với HĐH. Đây thực sự là một câu hỏi thú vị mà nhiều câu trả lời ở đây đã đưa ra.

Mô hình bảo mật CPU

Chương trình đầu tiên chạy trên hầu hết các kiến ​​trúc CPU chạy bên trong cái được gọi là vòng trong hoặc vòng 0 . Cách một vòm CPU cụ thể thực hiện các vòng khác nhau, nhưng gần như mọi CPU hiện đại đều có ít nhất 2 chế độ hoạt động, một chế độ được đặc quyền và chạy mã 'kim loại trần' có thể thực hiện bất kỳ hoạt động hợp pháp nào mà CPU có thể thực hiện và cái còn lại là không tin cậy và chạy mã được bảo vệ chỉ có thể thực hiện một bộ khả năng an toàn được xác định. Tuy nhiên, một số CPU có độ chi tiết cao hơn rất nhiều và để sử dụng VM một cách an toàn thì cần ít nhất 1 hoặc 2 vòng bổ sung (thường được gắn nhãn số âm) tuy nhiên điều này nằm ngoài phạm vi của câu trả lời này.

Hệ điều hành đến đâu

Hệ điều hành nhiệm vụ đơn sớm

Trong DOS rất sớm và các hệ thống dựa trên nhiệm vụ đơn đầu tiên khác, tất cả mã đều được chạy trong vòng bên trong, mọi chương trình bạn từng chạy đều có toàn bộ sức mạnh trên toàn bộ máy tính và có thể làm bất cứ điều gì theo nghĩa đen nếu nó bị lỗi bao gồm xóa tất cả dữ liệu của bạn hoặc thậm chí làm hỏng phần cứng trong một vài trường hợp cực đoan như cài đặt chế độ hiển thị không hợp lệ trên màn hình hiển thị rất cũ, tệ hơn, điều này có thể được gây ra bởi mã lỗi đơn giản không có bất kỳ ác ý nào.

Mã này trên thực tế phần lớn là bất khả tri của hệ điều hành, miễn là bạn có trình tải có khả năng tải chương trình vào bộ nhớ (khá đơn giản cho các định dạng nhị phân ban đầu) và mã không dựa vào bất kỳ trình điều khiển nào, thực hiện tất cả các truy cập phần cứng mà nó nên chạy theo bất kỳ HĐH nào miễn là nó chạy trong vòng 0. Lưu ý, một HĐH rất đơn giản như thế này thường được gọi là màn hình nếu nó chỉ được sử dụng để chạy các chương trình khác và không cung cấp thêm chức năng.

Hệ điều hành đa tác vụ hiện đại

Các hệ điều hành hiện đại hơn bao gồm UNIX , các phiên bản Windows bắt đầu bằng NT và nhiều hệ điều hành tối nghĩa khác đã quyết định cải thiện tình trạng này, người dùng muốn các tính năng bổ sung như đa nhiệm để họ có thể chạy nhiều ứng dụng cùng một lúc và bảo vệ, do đó, một lỗi ( hoặc mã độc) trong một ứng dụng không còn có thể gây ra thiệt hại không giới hạn cho máy và dữ liệu.

Điều này đã được thực hiện bằng cách sử dụng các vòng được đề cập ở trên, HĐH sẽ chiếm vị trí duy nhất chạy ở vòng 0 và các ứng dụng sẽ chạy ở các vòng không tin cậy bên ngoài, chỉ có thể thực hiện một bộ hoạt động hạn chế mà HĐH cho phép.

Tuy nhiên, tiện ích và bảo vệ gia tăng này phải trả giá, các chương trình giờ phải làm việc với HĐH để thực hiện các tác vụ mà chúng không được phép tự thực hiện, ví dụ, chúng không còn có thể kiểm soát trực tiếp đĩa cứng bằng cách truy cập bộ nhớ của nó và thay đổi tùy ý thay vào đó, họ phải yêu cầu HĐH thực hiện các tác vụ này cho họ để họ có thể kiểm tra xem họ có được phép thực hiện thao tác không, không thay đổi các tệp không thuộc về họ, nó cũng sẽ kiểm tra xem thao tác đó có thực sự hợp lệ không và sẽ không để phần cứng ở trạng thái không xác định.

Mỗi hệ điều hành quyết định thực hiện các biện pháp bảo vệ khác nhau, một phần dựa trên kiến ​​trúc mà HĐH được thiết kế và một phần dựa trên thiết kế và nguyên tắc của HĐH được đề cập, ví dụ UNIX tập trung vào các máy tốt cho nhiều người dùng và tập trung các tính năng khả dụng cho việc này trong khi các cửa sổ được thiết kế đơn giản hơn, để chạy trên phần cứng chậm hơn với một người dùng. Cách các chương trình không gian người dùng cũng nói chuyện với HĐH hoàn toàn khác trên X86, chẳng hạn như trên ARM hoặc MIPS, buộc một HĐH đa nền tảng phải đưa ra quyết định dựa trên nhu cầu làm việc trên phần cứng mà nó nhắm đến.

Các tương tác cụ thể của HĐH này thường được gọi là "các cuộc gọi hệ thống" và bao gồm cách chương trình không gian người dùng tương tác hoàn toàn với phần cứng thông qua HĐH, về cơ bản chúng khác nhau dựa trên chức năng của HĐH và do đó, một chương trình hoạt động thông qua các cuộc gọi hệ thống cần được hệ điều hành cụ thể.

Trình tải chương trình

Ngoài các cuộc gọi hệ thống, mỗi HĐH cung cấp một phương thức khác nhau để tải một chương trình từ phương tiện lưu trữ thứ cấpvào bộ nhớ , để có thể tải được bởi một HĐH cụ thể, chương trình phải chứa một tiêu đề đặc biệt mô tả cho HĐH như thế nào tải và chạy.

Tiêu đề này đã từng đủ đơn giản để viết một trình tải cho một định dạng khác gần như không đáng kể, tuy nhiên với các định dạng hiện đại như elf hỗ trợ các tính năng nâng cao như liên kết động và khai báo yếu thì giờ đây, HĐH không thể tải nhị phân Điều này không được thiết kế cho nó, điều này có nghĩa là, ngay cả khi không có sự không tương thích của hệ thống, thì rất khó để thậm chí đặt một chương trình trong ram theo cách có thể chạy được.

Thư viện

Các chương trình hiếm khi sử dụng các cuộc gọi hệ thống trực tiếp, tuy nhiên, chúng hầu như chỉ có được chức năng của chúng mặc dù các thư viện bao gồm các cuộc gọi hệ thống ở định dạng hơi thân thiện hơn với ngôn ngữ lập trình, ví dụ, C có Thư viện chuẩn C và glibc trong Linux và tương tự win32 libs Windows NT trở lên, hầu hết các ngôn ngữ lập trình khác cũng có các thư viện tương tự bao bọc chức năng hệ thống theo cách thích hợp.

Các thư viện này ở một mức độ nào đó thậm chí có thể khắc phục các vấn đề đa nền tảng như được mô tả ở trên, có một loạt các thư viện được thiết kế xung quanh để cung cấp một nền tảng thống nhất cho các ứng dụng trong khi quản lý nội bộ các cuộc gọi đến một loạt các HĐH như SDL , điều này có nghĩa là mặc dù các chương trình không thể tương thích nhị phân, các chương trình sử dụng các thư viện này có thể có nguồn chung giữa các nền tảng, làm cho việc chuyển đổi đơn giản như biên dịch lại.

Ngoại lệ ở trên

Bất chấp tất cả những gì tôi đã nói ở đây, đã có những nỗ lực khắc phục những hạn chế của việc không thể chạy các chương trình trên nhiều hệ điều hành. Một số ví dụ điển hình là dự án Wine đã mô phỏng thành công cả trình tải chương trình win32, định dạng nhị phân và thư viện hệ thống cho phép các chương trình Windows chạy trên nhiều UNIX khác nhau. Ngoài ra còn có một lớp tương thích cho phép một số hệ điều hành BSD UNIX chạy phần mềm Linux và tất nhiên shim của chính Apple cho phép một người chạy phần mềm MacOS cũ trong MacOS X.

Tuy nhiên, các dự án này hoạt động thông qua mức độ lớn của nỗ lực phát triển thủ công. Tùy thuộc vào mức độ khác nhau của hai hệ điều hành, độ khó dao động từ mô phỏng khá nhỏ đến mô phỏng gần như hoàn chỉnh của hệ điều hành khác thường phức tạp hơn so với việc tự viết toàn bộ hệ điều hành và do đó đây là ngoại lệ và không phải là quy tắc.


6
+1 "Tại sao hệ điều hành phần mềm cụ thể?" Vì lịch sử.
Paul Draper

2
mô hình bảo mật CPU có nguồn gốc x86? Tại sao và khi nào mô hình được phát minh?
n611x007

8
@naxa Không, nó có từ trước x86, lần đầu tiên nó được triển khai một phần cho Multics vào năm 1969, đây là HĐH đầu tiên có các tính năng chia sẻ thời gian đa người dùng hữu ích cần có mô hình này trong máy tính GE-645 , tuy nhiên việc triển khai này chưa hoàn chỉnh và dựa vào hỗ trợ phần mềm, việc triển khai hoàn toàn và an toàn đầu tiên trong phần cứng là sự kế thừa của nó, Honeywell 6180 . Đây hoàn toàn dựa trên phần cứng và cho phép Multics chạy mã từ nhiều người dùng mà không có cơ hội can thiệp chéo.
Vality

@Vality Ngoài ra, IBM LPAR là ~ 1972.
Elliott Frisch

@ElliottFrisch wow, thật ấn tượng. Tôi đã không nhận ra nó là khá sớm. Cảm ơn thông tin đó.
Vality

48

Như bạn có thể thấy, API không được chỉ định là một phần của hệ điều hành.

Tôi nghĩ rằng bạn đang đọc quá nhiều vào sơ đồ. Có, HĐH sẽ chỉ định giao diện nhị phân cho cách gọi các chức năng của hệ điều hành và nó cũng sẽ xác định định dạng tệp cho các tệp thực thi, nhưng nó cũng sẽ cung cấp API, theo nghĩa là cung cấp một danh mục các hàm có thể được gọi bằng một ứng dụng để gọi các dịch vụ HĐH.

Tôi nghĩ rằng sơ đồ chỉ cố gắng nhấn mạnh rằng các chức năng của hệ điều hành thường được gọi thông qua một cơ chế khác với một cuộc gọi thư viện đơn giản. Hầu hết các hệ điều hành phổ biến sử dụng ngắt bộ xử lý để truy cập các chức năng của hệ điều hành. Các hệ điều hành hiện đại điển hình sẽ không cho phép một chương trình người dùng truy cập trực tiếp vào bất kỳ phần cứng nào . Nếu bạn muốn viết một ký tự lên bàn điều khiển, bạn sẽ phải yêu cầu HĐH làm điều đó cho bạn. Cuộc gọi hệ thống được sử dụng để ghi vào bàn điều khiển sẽ khác nhau tùy theo hệ điều hành, do đó, có một ví dụ về lý do tại sao phần mềm là hệ điều hành cụ thể.

printf là một hàm từ thư viện thời gian chạy C và trong một triển khai điển hình là một hàm khá phức tạp. Nếu bạn google, bạn có thể tìm nguồn cho một số phiên bản trực tuyến. Xem trang này cho một hướng dẫn du lịch của một . Xuống cỏ mặc dù cuối cùng nó thực hiện một hoặc nhiều cuộc gọi hệ thống và mỗi cuộc gọi hệ thống đó là dành riêng cho hệ điều hành máy chủ.


4
Điều gì sẽ xảy ra nếu tất cả các chương trình đã làm là thêm hai số, không có đầu vào hoặc đầu ra. Chương trình đó vẫn là hệ điều hành cụ thể chứ?
Paul

2
Các hệ điều hành có nghĩa là đặt hầu hết các công cụ cụ thể phần cứng phía sau / trong một lớp trừu tượng. Tuy nhiên, bản thân HĐH (sự trừu tượng hóa) có thể khác nhau từ thực thi đến thực hiện. Có POSIX một số hệ điều hành (ít nhiều) tuân thủ và có thể một số hệ điều hành khác nhưng nói chung, các hệ điều hành đơn giản khác nhau quá nhiều trong phần trừu tượng "có thể nhìn thấy" của chúng. Như đã nói trước đây: bạn không thể mở / home / user trên windows và bạn không thể truy cập HKEY_LOCAL_MACHINE \ ... trên hệ thống * N * X. Bạn có thể viết phần mềm ảo ("mô phỏng") cho việc này để giúp mang các hệ thống này lại gần nhau hơn nhưng đó sẽ luôn là "bên thứ 3" (từ OS POV).
RobIII

16
@Paul Có. Cụ thể, cách nó được đóng gói thành một tệp thực thi sẽ dành riêng cho hệ điều hành.
OrangeDog

4
@TimSeguine Tôi không đồng ý với ví dụ của bạn về XP so với 7. Nhiều công việc được Microsoft thực hiện để đảm bảo cùng một API tồn tại trong 7 như trong XP. Rõ ràng những gì đã xảy ra ở đây là chương trình được thiết kế để chạy với một API hoặc hợp đồng nhất định. HĐH mới chỉ tuân thủ cùng API / hợp đồng. Tuy nhiên, trong trường hợp các cửa sổ đó API rất độc quyền, đó là lý do tại sao không có nhà cung cấp hệ điều hành nào khác hỗ trợ nó. Thậm chí sau đó có rất nhiều ví dụ về các chương trình KHÔNG chạy trên 7.
ArTs

3
@Paul: Một chương trình không có Đầu vào / Đầu ra là chương trình trống , chương trình này sẽ biên dịch thành không có op.
Bergi

14

Là trình biên dịch làm bất cứ điều gì hệ điều hành cụ thể khi biên dịch này?

Có lẽ. Tại một số thời điểm trong quá trình biên dịch và liên kết, mã của bạn được chuyển thành nhị phân dành riêng cho hệ điều hành và được liên kết với bất kỳ thư viện cần thiết nào. Chương trình của bạn phải được lưu ở định dạng mà hệ điều hành mong đợi để HĐH có thể tải chương trình và bắt đầu thực thi. Hơn nữa, bạn đang gọi chức năng thư viện tiêu chuẩn printf(), ở một mức độ nào đó được triển khai theo các dịch vụ mà hệ điều hành cung cấp.

Các thư viện cung cấp một giao diện - một lớp trừu tượng từ hệ điều hành và phần cứng - và điều đó cho phép biên dịch lại chương trình của bạn cho một hệ điều hành khác hoặc phần cứng khác. Nhưng sự trừu tượng đó tồn tại ở cấp nguồn - một khi chương trình được biên dịch và liên kết, nó được kết nối với một triển khai cụ thể của giao diện đó dành riêng cho một HĐH cụ thể.


12

Có một số lý do, nhưng một lý do rất quan trọng là Hệ điều hành phải biết cách đọc chuỗi byte tạo chương trình của bạn vào bộ nhớ, tìm các thư viện đi cùng với chương trình đó và tải chúng vào bộ nhớ và sau đó bắt đầu thực thi mã chương trình của bạn. Để thực hiện điều này, những người tạo ra HĐH tạo một định dạng cụ thể cho chuỗi byte đó để mã HĐH biết nơi tìm các phần khác nhau trong cấu trúc chương trình của bạn. Bởi vì các Hệ điều hành chính có các tác giả khác nhau, các định dạng này thường ít liên quan đến nhau. Đặc biệt, định dạng thực thi của Windows có ít điểm chung với định dạng ELF mà hầu hết các biến thể Unix sử dụng. Vì vậy, tất cả các mã tải, liên kết động và mã thực thi này phải là hệ điều hành cụ thể.

Tiếp theo, mỗi HĐH cung cấp một bộ thư viện khác nhau để nói chuyện với lớp phần cứng. Đây là các API mà bạn đề cập và chúng thường là các thư viện trình bày giao diện đơn giản hơn cho nhà phát triển trong khi dịch nó sang các cuộc gọi phức tạp hơn, cụ thể hơn vào độ sâu của chính HĐH, các cuộc gọi này thường không được ghi chép hoặc bảo mật. Lớp này thường khá xám, với các API "HĐH" mới hơn được xây dựng một phần hoặc hoàn toàn trên các API cũ hơn. Ví dụ: trong Windows, nhiều API mới hơn mà Microsoft đã tạo trong nhiều năm qua về cơ bản là các lớp nằm trên các API Win32 ban đầu.

Một vấn đề không phát sinh trong ví dụ của bạn, nhưng đó là một trong những vấn đề lớn hơn mà các nhà phát triển phải đối mặt là giao diện với trình quản lý cửa sổ, để trình bày GUI. Trình quản lý cửa sổ có phải là một phần của "HĐH" hay không đôi khi tùy thuộc vào quan điểm của bạn, cũng như bản thân HĐH, với GUI trong Windows được tích hợp với HĐH ở mức sâu hơn, trong khi GUI trên Linux và OS X tách trực tiếp hơn. Điều này rất quan trọng bởi vì ngày nay, cái mà mọi người thường gọi là "Hệ điều hành" là một con thú lớn hơn nhiều so với những gì sách giáo khoa có xu hướng mô tả, vì nó bao gồm nhiều, nhiều thành phần cấp ứng dụng.

Cuối cùng, không hoàn toàn là một vấn đề hệ điều hành, nhưng một vấn đề quan trọng trong việc tạo tệp thực thi là các máy khác nhau có các mục tiêu ngôn ngữ lắp ráp khác nhau và do đó mã đối tượng được tạo thực tế phải khác nhau. Đây không phải là vấn đề "HĐH" mà là vấn đề phần cứng, nhưng điều đó có nghĩa là bạn sẽ cần các bản dựng khác nhau cho các nền tảng phần cứng khác nhau.


2
Có thể đáng lưu ý rằng các định dạng thực thi đơn giản hơn có thể được tải chỉ bằng một lượng RAM nhỏ (nếu có) vượt quá yêu cầu để giữ mã được tải, trong khi các định dạng phức tạp hơn có thể yêu cầu dung lượng RAM lớn hơn nhiều trong một số trường hợp thậm chí sau khi tải. MS-DOS sẽ tải các tệp COM lên tới 63,75K chỉ bằng cách đọc các byte tuần tự vào RAM bắt đầu từ offset 0x100 của một phân đoạn tùy ý, tải CX với địa chỉ kết thúc và nhảy đến đó. Quá trình biên dịch một lượt có thể được thực hiện mà không cần vá lại (hữu ích với đĩa mềm) bằng ...
supercat

1
... có trình biên dịch bao gồm với mỗi thường trình một danh sách tất cả các điểm vá, mỗi điểm sẽ bao gồm địa chỉ của danh sách trước đó và đặt địa chỉ của danh sách cuối vào cuối mã. HĐH sẽ chỉ tải mã dưới dạng byte thô, nhưng một thói quen nhỏ trong mã có thể áp dụng tất cả các bản vá địa chỉ cần thiết trước khi chạy phần chính của mã.
supercat

9

Từ một câu trả lời khác của tôi:

Hãy xem xét các máy DOS đầu tiên và đóng góp thực sự của Microsoft cho thế giới là:

Autocad phải viết trình điều khiển cho mỗi máy in mà họ có thể in. Hoa sen 1-2-3 cũng vậy. Trong thực tế, nếu bạn muốn phần mềm của bạn in, bạn phải viết trình điều khiển của riêng bạn. Nếu có 10 máy in và 10 chương trình, thì 100 phần khác nhau của cùng một mã phải được viết riêng và độc lập.

Những gì windows 3.1 đã cố gắng thực hiện (cùng với GEM và rất nhiều lớp trừu tượng khác) là để nhà sản xuất máy in viết một trình điều khiển cho máy in của họ và lập trình viên đã viết một trình điều khiển cho lớp máy in windows.

Bây giờ với 10 chương trình và 10 máy in, chỉ có 20 đoạn mã phải được viết và vì phần mã microsoft giống nhau đối với mọi người, nên các ví dụ từ MS có nghĩa là bạn có rất ít việc phải làm.

Giờ đây, một chương trình không chỉ giới hạn ở 10 máy in mà họ chọn hỗ trợ, mà là tất cả các máy in có nhà sản xuất cung cấp trình điều khiển cho trong windows.

Vì vậy, HĐH cung cấp dịch vụ cho các ứng dụng để các ứng dụng không phải thực hiện công việc dư thừa.

Chương trình C ví dụ của bạn sử dụng printf, sẽ gửi các ký tự đến thiết bị xuất chuẩn - tài nguyên dành riêng cho hệ điều hành sẽ hiển thị các ký tự trên giao diện người dùng. Chương trình không cần biết giao diện người dùng ở đâu - có thể là trong DOS, nó có thể nằm trong một cửa sổ đồ họa, nó có thể được dẫn đến một chương trình khác và được sử dụng làm đầu vào cho một quy trình khác.

Bởi vì HĐH cung cấp các tài nguyên này, các lập trình viên có thể hoàn thành nhiều việc hơn với ít công việc.

Tuy nhiên, thậm chí bắt đầu một chương trình là phức tạp. HĐH hy vọng một tệp thực thi sẽ có một số thông tin nhất định ngay từ đầu để cho HĐH biết nó nên được khởi động như thế nào và trong một số trường hợp (môi trường nâng cao hơn như Android hoặc iOS) cần có tài nguyên nào cần được phê duyệt vì chúng chạm vào tài nguyên bên ngoài "Sandbox" - một biện pháp bảo mật để giúp bảo vệ người dùng và các ứng dụng khác khỏi các chương trình hoạt động sai.

Vì vậy, ngay cả khi mã máy thực thi là như nhau và không cần tài nguyên hệ điều hành, một chương trình được biên dịch cho Windows sẽ không chạy trên hệ điều hành OS X mà không có lớp mô phỏng hoặc dịch thuật bổ sung, ngay cả trên cùng một phần cứng chính xác.

Các hệ điều hành kiểu DOS ban đầu thường có thể chia sẻ các chương trình, vì chúng đã triển khai cùng một API trong phần cứng (BIOS) và HĐH được nối vào phần cứng để cung cấp dịch vụ. Vì vậy, nếu bạn đã viết và biên dịch chương trình COM - chỉ là hình ảnh bộ nhớ của một loạt các hướng dẫn bộ xử lý - bạn có thể chạy nó trên CP / M, MS-DOS và một số hệ điều hành khác. Trong thực tế, bạn vẫn có thể chạy các chương trình COM trên các máy Windows hiện đại. Các hệ điều hành khác không sử dụng cùng các móc API BIOS, vì vậy các chương trình COM sẽ không chạy trên chúng mà không có lớp mô phỏng hoặc dịch. Các chương trình EXE tuân theo cấu trúc bao gồm nhiều hơn các hướng dẫn của bộ xử lý, và cùng với các vấn đề API, nó sẽ không chạy trên một máy không hiểu cách tải nó vào bộ nhớ và thực thi nó.


7

Trên thực tế, câu trả lời thực sự là nếu mọi hệ điều hành đều hiểu cách bố trí tệp nhị phân thực thi giống nhau bạn chỉ giới hạn ở các chức năng được tiêu chuẩn hóa (như trong thư viện chuẩn C) mà HĐH cung cấp (mà HĐH cung cấp), thì phần mềm của bạn sẽ , trên thực tế, chạy trên bất kỳ hệ điều hành nào.

Tất nhiên, thực tế không phải vậy. Một EXEtệp không có cùng định dạng với một ELFtệp, mặc dù cả hai đều chứa mã nhị phân cho cùng một CPU. * Vì vậy, mỗi hệ điều hành sẽ cần có thể diễn giải tất cả các định dạng tệp và đơn giản là chúng không làm điều này trong bắt đầu, và không có lý do gì để họ bắt đầu làm như vậy sau này (gần như chắc chắn vì lý do thương mại hơn là kỹ thuật).

Hơn nữa, chương trình của bạn có thể cần thực hiện những việc mà thư viện C không xác định cách thực hiện (ngay cả đối với những việc đơn giản như liệt kê nội dung của thư mục) và trong những trường hợp đó, mọi hệ điều hành đều cung cấp (các) chức năng riêng để đạt được nhiệm vụ, tự nhiên có nghĩa là sẽ không có mẫu số chung thấp nhất để bạn sử dụng (trừ khi bạn tự tạo mẫu số đó).

Vì vậy, trong hiệu trưởng, nó hoàn toàn có thể. Trong thực tế, WINE chạy các tệp thực thi Windows trực tiếp trên Linux.
Nhưng đó là một tấn công việc và (thường) không chính đáng về mặt thương mại.

* Lưu ý: Có rất nhiều tệp thực thi hơn là mã nhị phân. Có một tấn thông tin cho rằng hệ thống điều hành những gì thư viện các tập tin phụ thuộc vào, nó cần bao nhiêu ngăn xếp bộ nhớ, những chức năng nó xuất khẩu sang khác thư viện mà có thể phụ thuộc vào nó, nơi mà các hệ điều hành có thể tìm thấy thông tin gỡ lỗi có liên quan, làm thế nào để " xác định lại vị trí "tệp trong bộ nhớ nếu cần thiết, làm thế nào để xử lý ngoại lệ hoạt động chính xác, v.v ... một lần nữa, có thể có một định dạng duy nhất cho điều này mà mọi người đều đồng ý, nhưng đơn giản là không có.


Thực tế thú vị: có một định dạng nhị phân posiz chuẩn hóa, có thể chạy trên các hệ điều hành. Nó chỉ không được sử dụng phổ biến.
Marcin

@Marcin: Có vẻ như bạn không coi Windows là HĐH. (Hoặc bạn đang nói Windows có thể chạy nhị phân POSIX?!) Vì mục đích trả lời của tôi, POSIX không phải là loại tiêu chuẩn mà tôi đang đề cập. Chữ X trong POSIX là viết tắt của Unix. Nó không bao giờ được sử dụng bởi Windows, mặc dù Windows có hệ thống con POSIX.
Mehrdad

1. Một cái gì đó có thể chạy trên nhiều hệ điều hành mà không cần chạy trên tất cả các hệ điều hành; 2. Windows kể từ NT đã có thể chạy nhị phân posix.
Marcin

1
@Marcin: (1) Như tôi đã nói, X trong POSIX là viết tắt của UNIX . Nó không phải là một tiêu chuẩn được theo sau bởi các hệ điều hành khác, nó chỉ là một nỗ lực để đạt được một mẫu số chung giữa các Unix khác nhau, điều này thật tuyệt nhưng không đáng kinh ngạc. Thực tế là có nhiều hương vị của hệ điều hành Unix ngoài kia hoàn toàn không liên quan đến điểm mà tôi đã cố gắng thực hiện về khả năng tương thích giữa các hệ điều hành khác so với Unix. (2) Bạn có thể cung cấp tài liệu tham khảo cho # 2 không?
Mehrdad

1
@Mehrdad: Marcin nói đúng; Windows SUA (Hệ thống con cho các ứng dụng Unix) tuân thủ POSIX
MSalters

5

Sơ đồ có lớp "ứng dụng" (phần lớn) được phân tách khỏi lớp "hệ điều hành" bởi "thư viện" và ngụ ý rằng "ứng dụng" và "HĐH" không cần biết về nhau. Đó là một sự đơn giản hóa trong sơ đồ, nhưng nó không hoàn toàn đúng.

Vấn đề là "thư viện" thực sự có ba phần: đó là phần triển khai, giao diện cho ứng dụng và giao diện cho HĐH. Về nguyên tắc, hai phần đầu có thể được tạo thành "phổ quát" theo như liên quan đến HĐH (nó phụ thuộc vào nơi bạn cắt nó), nhưng phần thứ ba - giao diện cho HĐH - nói chung là không thể. Giao diện cho HĐH sẽ nhất thiết phải phụ thuộc vào HĐH, API mà nó cung cấp, cơ chế đóng gói (ví dụ: định dạng tệp được sử dụng bởi Windows DLL), v.v.

Bởi vì "thư viện" thường được cung cấp dưới dạng một gói duy nhất, điều đó có nghĩa là một khi chương trình chọn một "thư viện" để sử dụng, nó cam kết với một HĐH cụ thể. Điều này xảy ra một trong hai cách: a) lập trình viên chọn hoàn toàn trước, và sau đó ràng buộc giữa thư viện và ứng dụng có thể là phổ quát, nhưng chính thư viện bị ràng buộc với HĐH; hoặc b) lập trình viên thiết lập mọi thứ để thư viện được chọn khi bạn chạy chương trình, nhưng sau đó chính cơ chế liên kết , giữa chương trình và thư viện, phụ thuộc vào hệ điều hành (ví dụ: Cơ chế DLL trong Windows). Mỗi cái đều có ưu điểm và nhược điểm, nhưng dù bằng cách nào bạn cũng phải đưa ra lựa chọn trước.

Bây giờ, điều này không có nghĩa là không thể làm được, nhưng bạn phải rất thông minh. Để khắc phục vấn đề, bạn sẽ phải đi theo con đường chọn thư viện vào thời gian chạy và bạn sẽ phải đưa ra một cơ chế ràng buộc phổ quát không phụ thuộc vào HĐH (vì vậy bạn có thể chịu trách nhiệm duy trì nó, nhiều việc hơn). Đôi khi nó đáng giá.

Bạn không cần phải làm vậy, nhưng nếu bạn sẽ nỗ lực để làm điều đó, rất có thể bạn cũng không muốn bị ràng buộc với một bộ xử lý cụ thể, vì vậy bạn sẽ viết một Máy ảo và bạn sẽ biên dịch chương trình của bạn để một định dạng mã trung tính bộ xử lý.

Bây giờ bạn sẽ nhận thấy tôi đang đi đâu. Các nền tảng ngôn ngữ như Java thực hiện chính xác điều đó. Thời gian chạy Java (thư viện) xác định ràng buộc trung lập HĐH giữa chương trình Java của bạn và thư viện (cách thời gian chạy Java mở và chạy chương trình của bạn) và nó cung cấp một triển khai cụ thể cho HĐH hiện tại. .NET thực hiện điều tương tự ở một mức độ nào đó, ngoại trừ việc Microsoft không cung cấp "thư viện" (thời gian chạy) cho mọi thứ trừ Windows (nhưng những người khác thì làm - xem Mono). Và trên thực tế, Flash cũng làm điều tương tự, mặc dù nó bị giới hạn phạm vi hơn đối với Trình duyệt.

Cuối cùng, có nhiều cách để làm điều tương tự mà không cần cơ chế ràng buộc tùy chỉnh. Bạn có thể sử dụng các công cụ thông thường, nhưng trì hoãn bước ràng buộc vào thư viện cho đến khi người dùng chọn HĐH. Đó chính xác là những gì xảy ra khi bạn phân phối mã nguồn. Người dùng lấy chương trình của bạn và liên kết nó với bộ xử lý (biên dịch nó) và HĐH (liên kết nó) khi người dùng sẵn sàng chạy nó.

Tất cả phụ thuộc vào cách bạn cắt các lớp. Vào cuối ngày, bạn luôn có một thiết bị điện toán được làm bằng phần cứng cụ thể chạy mã máy cụ thể. Các lớp chủ yếu là một khung khái niệm.


3

Phần mềm không phải lúc nào cũng là hệ điều hành cụ thể. Cả Java và hệ thống mã p trước đó (và thậm chí cả ScummVM) đều cho phép phần mềm có thể di động trên các Hệ điều hành. Infocom (nhà sản xuất Zork và máy Z ), cũng có cơ sở dữ liệu quan hệ dựa trên một máy ảo khác. Tuy nhiên, ở một mức độ nào đó, một cái gì đó phải dịch ngay cả những trừu tượng đó thành các hướng dẫn thực tế để được thực thi trên máy tính.


3
Tuy nhiên, Java chạy trên một máy ảo không phải là hệ điều hành chéo. Bạn phải sử dụng một nhị phân JVM khác nhau cho mỗi HĐH
Izkata

3
@Izkata Đúng, nhưng bạn không biên dịch lại phần mềm (chỉ là JVM). Ngoài ra, xem câu cuối cùng của tôi. Nhưng tôi sẽ chỉ ra rằng Sun đã có một bộ vi xử lý có thể thực thi trực tiếp mã byte.
Elliott Frisch

3
Java là một hệ điều hành, mặc dù nó thường không được coi là một. Phần mềm Java dành riêng cho HĐH Java và có các trình giả lập HĐH Java cho hầu hết các HĐH "thực". Nhưng bạn có thể làm điều tương tự với bất kỳ máy chủ và hệ điều hành đích nào - như chạy phần mềm Windows trên Linux bằng WINE.
dùng253751

@immibis Tôi sẽ nói cụ thể hơn. Các lớp nền tảng Java (JFC, thư viện chuẩn của Java) là một khung. Bản thân Java là một ngôn ngữ. JVM tương tự như một HĐH: nó có "Máy ảo" trong tên của nó và thực hiện các chức năng tương tự như một HĐH từ góc độ của mã đang chạy trong nó.

1

Bạn nói

phần mềm được sản xuất bằng ngôn ngữ lập trình cho các hệ điều hành nhất định chỉ hoạt động với chúng

Nhưng chương trình bạn đưa ra làm ví dụ sẽ hoạt động trên nhiều hệ điều hành và thậm chí một số môi trường kim loại trần.

Điều quan trọng ở đây là sự phân biệt giữa mã nguồn và nhị phân được biên dịch. Ngôn ngữ lập trình C được thiết kế đặc biệt để độc lập với hệ điều hành ở dạng nguồn. Nó thực hiện điều này bằng cách để lại việc giải thích những thứ như "in ra bàn điều khiển" cho người thực hiện. Nhưng C có thể được tuân thủ một cái gì đó cụ thể về hệ điều hành (xem các câu trả lời khác để biết lý do). Ví dụ, các định dạng thực thi PE hoặc ELF.


6
Có vẻ như khá rõ ràng rằng OP đang hỏi về nhị phân, không phải mã nguồn.
Caleb

0

Những người khác đã trình bày chi tiết kỹ thuật tốt, tôi muốn đề cập đến một lý do kỹ thuật ít hơn, khía cạnh UX / UI của mọi thứ:

Viết một lần, cảm thấy lúng túng ở mọi nơi

Mọi hệ điều hành đều có API giao diện người dùng và tiêu chuẩn thiết kế riêng. Có thể viết một giao diện người dùng cho một chương trình và để nó chạy trên nhiều hệ điều hành, tuy nhiên làm như vậy tất cả nhưng đảm bảo rằng chương trình sẽ cảm thấy không phù hợp ở mọi nơi. Tạo một giao diện người dùng tốt đòi hỏi phải điều chỉnh các chi tiết cho từng nền tảng được hỗ trợ.

Rất nhiều trong số đó là những chi tiết nhỏ, nhưng hiểu sai và bạn sẽ làm người dùng thất vọng:

  • Các hộp thoại xác nhận có các nút theo thứ tự khác nhau trong Windows và OSX; mắc lỗi này và người dùng sẽ bấm vào nút sai bằng bộ nhớ cơ. Windows có "Ok", "Hủy" theo thứ tự đó. OSX có thứ tự hoán đổi và văn bản của nút do-it là một mô tả ngắn về hành động sẽ được thực hiện: "Hủy", "Di chuyển đến Thùng rác".
  • Hành vi "Quay lại" là khác nhau đối với iOS và Android. Các ứng dụng iOS vẽ nút quay lại của riêng họ khi cần, thường là ở phía trên bên trái. Android có một nút chuyên dụng ở phía dưới bên trái hoặc bên phải thấp hơn tùy thuộc vào xoay màn hình. Cổng nhanh vào Android sẽ hoạt động không chính xác nếu nút quay lại hệ điều hành bị bỏ qua.
  • Cuộn đà là khác nhau giữa iOS, OSX và Android. Đáng buồn thay, nếu bạn không viết mã UI gốc, bạn có thể phải viết hành vi cuộn của riêng bạn.

Ngay cả khi về mặt kỹ thuật có thể viết một cơ sở mã UI chạy ở mọi nơi, tốt nhất bạn nên điều chỉnh cho từng hệ điều hành được hỗ trợ.


-2

Một điểm khác biệt quan trọng ở điểm này là tách trình biên dịch khỏi trình liên kết. Trình biên dịch rất có thể tạo ra ít nhiều cùng một đầu ra (sự khác biệt chủ yếu là do các #if WINDOWSs khác nhau ). Mặt khác, trình liên kết phải xử lý tất cả các công cụ cụ thể của nền tảng - liên kết các thư viện, xây dựng tệp thực thi, v.v.

Nói cách khác, trình biên dịch quan tâm chủ yếu đến kiến ​​trúc CPU, bởi vì nó tạo ra mã có thể chạy thực tế và phải sử dụng các hướng dẫn và tài nguyên của CPU (lưu ý rằng mã byte của IL hoặc JVM của .NET sẽ được coi là tập lệnh của CPU ảo theo quan điểm này). Đây là lý do tại sao bạn phải biên dịch mã riêng cho x86ARM, ví dụ.

Mặt khác, trình liên kết phải lấy tất cả dữ liệu và hướng dẫn thô này và đặt nó ở định dạng mà trình tải (ngày nay, hầu như luôn luôn là HĐH) có thể hiểu, cũng như liên kết bất kỳ thư viện được liên kết tĩnh nào (cũng bao gồm mã cần thiết cho liên kết động, cấp phát bộ nhớ, v.v.).

Nói cách khác, bạn chỉ có thể biên dịch mã một lần và chạy mã trên cả Linux và Windows - nhưng bạn phải liên kết nó hai lần, tạo ra hai tệp thực thi khác nhau. Bây giờ, trong thực tế, bạn thường phải tạo ra các khoản phụ cấp trong mã (đó là nơi các chỉ thị của trình biên dịch (trước) xuất hiện), do đó, ngay cả việc biên dịch một lần liên kết hai lần cũng không được sử dụng nhiều. Chưa kể rằng mọi người đang coi việc biên dịch và liên kết là một bước duy nhất trong quá trình xây dựng (giống như bạn không còn quan tâm đến các phần của chính trình biên dịch).

Phần mềm thời đại DOS thường có tính di động nhị phân nhiều hơn, nhưng bạn phải hiểu rằng nó cũng được biên dịch không phải chống lại DOS hay Unix, mà là chống lại một hợp đồng nhất định phổ biến đối với hầu hết các PC kiểu IBM - giảm tải những gì mà ngày nay API gọi là ngắt phần mềm. Điều này không cần liên kết tĩnh, vì bạn chỉ phải đặt các thanh ghi cần thiết, gọi ví dụ int 13hcho các chức năng đồ họa và CPU chỉ nhảy đến một con trỏ bộ nhớ được khai báo trong bảng ngắt. Tất nhiên, một lần nữa, thực tế là rất nhiều khó khăn hơn, bởi vì để có được hiệu suất bàn đạp, bạn phải tự viết tất cả các phương pháp đó, nhưng về cơ bản, nó hoàn toàn có thể đi xung quanh HĐH. Và tất nhiên, có một thứ luôn luôn cần sự tương tác với API hệ điều hành - chấm dứt chương trình. Tuy nhiên, nếu bạn sử dụng các định dạng đơn giản nhất có sẵn (ví dụ:COMtrên DOS, không có tiêu đề, chỉ là hướng dẫn) và không muốn thoát, chúc bạn may mắn! Và tất nhiên, bạn cũng có thể xử lý việc chấm dứt hợp lý trong thời gian chạy, do đó bạn có thể có mã cho cả chấm dứt Unix và chấm dứt DOS trong cùng một tệp thực thi và phát hiện trong thời gian chạy nào nên sử dụng :)


điều này dường như chỉ đơn thuần lặp lại các điểm giải thích trong nàynày câu trả lời trước đó được đăng ngày hôm qua
một loại muôi
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.