為什麼不同系統不能兼容同一個已編譯的可執行二進位文件?

高級語言都是編譯成可執行二進位文件再運行的,我所理解的可執行二進位文件就是編譯成機器語言(就是機器能夠直接解釋的語言) 為什麼不同系統(windows linux )編譯出來的文件不能在互相平台執行?


主要的原因是格式不同API不同,前者更重要一些。

一個可執行的二進位文件包含的不僅僅是機器指令,還包括各種數據、程序運行資源,機器指令只是其中的一部分。

一個可執行文件要被執行的時候,操作系統需要為其分配資源,這些資源包括:內存空間(物理的和虛擬的),進程、線程資源等等,其中可執行文件的機器指令一般都放在代碼段(彙編語言里稱之為text段),其它資源可能放到數據段以及其它段里,這裡「段」(segment)可以大致的理解為一段內存範圍。操作系統(Windows/Linux)需要知道這個可執行文件需要多大的內存,有多少個段,分別載入到哪些內存地址上。可執行文件需要告訴操作系統,要為可執行文件準備哪些東西它才能運行。

可執行文件在執行之前,操作系統要有一些準備工作,因為不同的操作系統,準備工作是不同的,所以可執行文件的格式不完全相同。Windows上大部分可執行文件為PE格式,Linux里大部分可執行文件為ELF格式。格式不同導致了不同的可執行文件無法跨平台直接使用。這是原因之一。

當然了,我見過網上有大神解決了一些格式不同的問題,但跨平台運行還需要解決另一個障礙,就是操作系統API不同。一個可執行文件所執行的絕大多數操作(比如:文件操作、輸入輸出、內存申請釋放、任務調度等等)都需要與操作系統交互才能完成,而不同的操作系統使用這些操作的方法完全不同,所以這個障礙更難跨越。這是原因之二。

如果能解決以上兩個原因,那麼有些可執行文件理論上是可以跨Windows和Linux在x86平台上運行的,因為Intel和AMD的CPU里,主要的硬體指令(機器指令)是相同的,也就是說0101這種二進位數,是一樣的。但是如果切換到ARM平台,會有更大的麻煩就是硬體指令也不同,那麼就完全沒辦法了。

有沒有可能有跨平台運行的可執行文件呢,理論上是存在的,過去的時候也有一些辦法,但限制極多,比如Windows過去是支持COM格式的文件的,這個文件就沒有文件頭,大小不能超過64K,只能在一個16位環境里(真實的或者虛擬的)運行,是真正的裸二進位文件。Linux里某些BIN文件恰好也是裸二進位文件(有些BIN文件沒有ELF頭,但不是所有的BIN都是這樣的)。經過一些配置以後BIN文件也是可以在Linux上運行的。於是某些精巧設計的COM/BIN文件可以在限制極多的情況下跨平台運行,但也許只能做計算,無法做輸出,大小也只有64K大,並且如果要做稍微複雜點的操作,就需要兩套機器代碼實現。另外,很不幸的是64位環境里COM文件已經不再支持了。


樓主,我真的覺得你是個人才,很有想法

樓主問題多,我們一個一個來

先說為什麼不同系統編譯的文件不能跨平台


是因為編出來的文件格式不一樣,不同OS對可執行文件有自己的規定


除此之外,程序不可避免會用到系統相關的代碼。有人提到的wine正是從這裡入手的,實現了win32的api。

那為什麼不同系統不能兼容同一個已編譯的可執行二進位文件呢?

當然是可以的啦。。。。


除了qq這種對操作系統強依賴的軟體外,世界上最廣泛使用的個人軟體(之一)pc遊戲是完全可以做到二進位復用的


要二進位跨平台很簡單(備註防噴:僅討論x86),遊戲里的平台相關代碼其實很少,剝離掉。其餘代碼轉換成自定義的二進位代碼格式,然後實現一個自己的loader就可以在任意系統上load你的程序了

如果不會做這個,真應該反思一下你的本科學習啦。。。


現實本應該是這樣的,但為什麼現實不是這樣的呢?。。。

簡版答案:

大多數程序員都不合格,太不合格了,當然,我一般也不認為那些人是程序員就是了。。。

當然的當然,有人可以說非主流平台不需要照顧等等等等原因,但其實只是借口。。。程序員隨手實現一個這樣的東西沒有那麼誇張的金錢開銷的


好像沒有人提到,除了執行文件格式、系統提供的API外,還有一個東西叫做應用二進位介面(Application binary interface, ABI)。


很多系統明明就可以的啊?Windows上能跑DOS程序,DOS上能跑CP/M程序,Linux上能跑一些Windows程序,FreeBSD上能跑大多數Linux程序……
同樣的體系架構下,不同的OS無非就是可執行程序的文件格式不一樣或者OS API不一樣,前者搞個binary loader就行(比如Windows下的PE/LE/MZ loader或者Wine),後者做個OS API轉接層就行(比如Wine或者FreeBSD上的Linux兼容層)
你遇到的那些不能用的情況,無非就是binary loader和OS API轉接層沒人做就是了。


我看了你在上一個答案的回復,我大致明白你為什麼疑惑了。在操作系統課本中幾乎都提到過操作系統的位置。
---------------------Applicaion---------------------
---------------------OS----------------------------
----------------------Hardware-----------------
也就是說,你的Application層是在OS之上的,那麼你的應用程序是無法繞過OS而直接接觸到硬體的,而你需要讀寫這樣的操作,那麼你終歸要和OS打交道,而每個OS提供的API也是不同的,其最後產生的結果自然也是不同的,這裡面的中斷機制、文件系統各個方面都不一樣。

也許你還會如你上一條回復一樣再次強調是編譯為010101,這只是一個程序的部分反映而已。對於一個程序來說,它還需要鏈接其他所需要的庫等信息記錄在Tables裡面,這些Tables也包括了你的函數地址,執行入口等信息。然而這些Tables裡面的排列信息,在每個系統中的安排是不同的,如果你放置一個exe文件給Linux,Linux完全不知道從哪裡開始執行這個程序,所以只有01010101是沒用的,你需要告訴我從哪裡開始。

我們再來說程序與OS的交互方式的不同。就本質而言,OS提供了訪問硬體的能力,而應用程序其實也是想訪問這些硬體,只是沒有辦法的必須要經過OS(再次看上面的那三條線),而要做到這點,通常的措施是把一些信息放在CPU寄存器,然後產生一個軟中斷。很遺憾,Windows與Linux放置的寄存器與中斷機制是不同的。所以,當你把一個exe程序載入到內存,那也是沒用的,因為這個exe完全沒有辦法與OS Kernel交互。於是,Wine就出現了,來通過一些措施來辦到這點兒。

繼續說下去好像就是CPU了,但是我覺得和你這個問題關係已經不大了,你的疑惑主要是前面說的這幾條。

————————Update————————
關於CPU可以參考下面1樓的評論


文件格式不一樣啊,windows只認pe和 linux上面是elf吧。

另外系統相關的api也不一樣。

為什麼非要不一樣。這裡就涉及微軟對商業模式的看法了,有人願意開源,有人通過專利限制別人做同樣的東西跟自己競爭。其中還涉及差異化的問題,正如html5標準,每個瀏覽器的具體實現不一樣,web前端碼農由於這種實現得不一樣,在兼容性問題上疲於奔命


這要分兩種情況,
1.你沒有調用Linux特殊的函數庫,而使用的是C/C++的標準庫,這樣在同樣平台下能完成的工作是相同的,但是因為Linux下gcc或g++編譯出的可執行文件文件頭是ELF,windows下可執行文件頭是PE,所以只要把Linux下編譯出的可執行文件頭去掉,然後加上一個PE文件頭就可以在windows下跑了。
2.你調用了Linux特殊的函數(動態)庫,比如包含了unistd.h等Linux系統特有的API函數庫(或indows下的windows.h等等),這樣你就沒法直接移植了,因為程序執行會去調用動態庫的函數,Platform變了找不到就要報錯。除非你能把Linux下的特有庫移植到windows上去,但是這工作量。
另外,補充一下一樓的答案,CPU依然是很大原因,特別想Linux用在很多嵌入式環境下。如果構架、指令集不同,需要的二進位代碼也不同,這就為什麼我們在vs上編譯程序的時候可以看到編譯目標是x86、x64、ARM等。其實Wine的實質是在Linux上提供一些Windows特有的介面,很多為Windows開發的程序使用大量Windows特有的介面,因此不能Linux上直接運行,反之亦然。Java、C#的多平台,就是先將代碼編譯成中間語言,然後又虛擬機和運行時環境執行,但是其實要實現多平台,必須有對應平台的虛擬機(運行時環境)才行。
引用:同一段C++在Linux和windows下編譯後的可執行文件有什麼不同?-CSDN論壇-CSDN.NET-中國最大的IT技術社區


為了說明這個問題,我先講一下什麼是庫,以及程序編譯的方式。
所謂庫,就是指過程的集合,那麼什麼是過程?舉一個簡單的例子:
int fact(int n)

if(n&<1)return(1);
else return (n*fact(n-1));

這個就是過程,你也可以理解為一個函數。
而庫的本質,就是把很多過程編譯成二進位文件的集合。
而程序是如何編譯成一個可執行文件的呢,過程是這樣的,C程序——彙編語言程序——機器語言目標文件,然後鏈接器把機器語言目標文件對庫進行鏈接,並在程序頭部加上對程序的必要描述如程序大小,數據部分大小,生成可執行文件。
而庫有兩種,一種是靜態鏈接庫,在編譯的過程中就會把庫鏈接,庫程序會成為可執行代碼的一部分。
另一種就是動態鏈接庫(daynamically linked libraries,DLL),這種庫只有在程序的執行過程中才會被鏈接,這就是為什麼有時候運行程序會提示缺少xxx.dll的原因。而其他答案提到的API,我的理解就是這種動態鏈接庫。
為什麼其他系統會無法運行呢,有兩方面的原因:
其一,各個系統的可執行文件文件頭格式不一樣,導致其他系統無法正確得到程序的大小、第一條指令的偏移地址以及數據的位置。
其二,各個系統的動態鏈接庫不一樣,導致無法正確調用過程。
本人初學CS,可能理解不太對,歡迎各位補充指正。


首先,你要明白操作系統是什麼,幹什麼的,簡單說操作系統就是管理機器的資源(資源就是內存,硬碟,網卡等等)。然後應用程序是運行在操作系統上的。但你要知道不同操作系統對應用開放的介面是不同的,明白這一點後,你想要的答案也就有了。


只要操作系統支持就好辦,微軟最近在Windows裡面加了一個Windows Subsystem for Linux,就支持Linux程序直接在Windows下面運行了。

其實Windows Subsystem for Linux本質上就是提供了樓上各位答主所說的可執行文件格式支持和操作系統API支持。


可以這麼說:不同的OS對同樣的010101的理解不一樣啊。


推薦閱讀:

openSUSE 的人氣為何遠不如 Ubuntu 和 Fedora ?
買 MacBook 裝 Windows 系統的人是什麼心理?
為什麼主流手機系統拋棄了「回收站」這個功能?
為什麼很多人喜歡在桌面反覆刷新?
iOS 9 正式版的使用體驗如何?

TAG:操作系統 | 編程 | 程序 | 計算機體系架構 |