編譯程序是否有操作系統的參與?
我說的編譯程序指的是將高級語言編譯成彙編語言的程序。在這個過程中是否有操作系統的參與呢?因為一直以來我都認為操作系統是硬體和軟體的橋樑,所有要涉及到硬體的,都通過操作系統實現。
比如編譯程序是如何知道 本機的硬體信息 而生成其對應的彙編代碼的(彙編代碼機器相關),是操作系統的提供了查詢的介面? 還有比如程序中靜態分配的變數(並非指運行時動態分配的)的存儲位置也是由操作系統分配的嗎?然後我們看到的編譯好的彙編代碼里的那些地址只是類似於高級語言中的變數的標識符而已?(邏輯地址)實在苦惱於此,求大神指點一二…
比如編譯程序是如何知道 本機的硬體信息 而生成其對應的彙編代碼的(彙編代碼機器相關),是操作系統的提供了查詢的介面?
沒有。你當然要告訴編譯器去使用哪種機器碼。至於默認的那個,本來不同的系統上不同的CPU架構的編譯器就是分開編譯,寫死在裡面了的。
還有比如程序中靜態分配的變數(並非指運行時動態分配的)的存儲位置也是由操作系統分配的嗎?然後我們看到的編譯好的彙編代碼里的那些地址只是類似於高級語言中的變數的標識符而已?(邏輯地址)
算是吧,編譯器會統計所有全局變數的大小的總和,寫在exe裡面,exe被執行的時候會直接分配好,然後再在上面跑構造函數。
你編譯出來的程序又不是一定要在編譯這個程序的機器上跑,所以當然要你提供目標平台的架構指令集信息至於變數怎麼表示,當然要編譯器根據目標平台的約定來生成對應的東西,寄存器多的變數當然都存進寄存器,寄存器少的扔進棧操作系統有沒有參與,肯定參與了啊,關機了你還怎麼編譯
編譯器讀寫文件之類的功能都需要系統調用,所以肯定是有——但目標代碼信息不是由操作系統提供的。
編譯器肯定是要知道生成的代碼是用於什麼操作系統,什麼CPU的。
但是題主你有一個誤區,覺得編譯器編譯出來的東西,一定是當前平台上能運行的。並不是。我們完全可以做一個編譯器,運行在x86_64的windows 10 64bit上,編譯出來的程序是運行在ARM CPU的安卓4.4上——很多安卓程序就是這麼開發的。這種技術叫做交叉編譯。
題主你大概是在學習C/C++,他們的編譯器內部一般會有個默認的配置,寫好了目標平台的參數,你不額外指定平台參數,那就會使用默認值,默認值通常是和當前運行的平台兼容的。日常用的PC/Windows平台上,默認的目標CPU是iX86,X從3~6都有可能。可以看出來,默認的目標CPU巨老,這主要是為了兼容性考慮。如果你不想照顧老機器,可以往後調。
很顯然,無論你使用默認值,還是手工指定配置,都不需要讓操作系統幫忙查詢。有個特例下需要。gcc里的-mtune和-march參數值可以設成native,意思就是目標CPU以當前電腦為準,這時候就是操作系統去查詢當前電腦是什麼CPU了。
全局變數相關的信息都在exe里寫好了,每個變數(如果有名字)叫什麼,有多大,放在全局數據區的哪個位置等等。等程序載入的時候,操作系統會根據這些信息分配好相應的內存,然後計算出來每個變數的內存地址。
嚴格意義上說是沒有的。你可以了解一下「交叉編譯」(Cross Compilation),在獲知另一操作系統平台的硬體信息與指令集格式之後(拿到交叉編譯 toolchain),就可以做交叉編譯。
至於編譯器怎樣獲取硬體信息,簡單來講,是從你自己那裡得到的。想一下你安裝 Visual Studio / MinGW / clang 等等這些工具的過程;你會挑選適配了你的平台(x86 / x86_64 / ARM)的對應工具,這就是它從你那裡得知硬體信息的過程。如果沒有其他硬體的聯動,基本就是與 CPU 指令集種類直接相關了。
Data 的存儲區是鏈接時最終決定的。編譯時將 Data 放置在目標文件的特定位置,鏈接時會有合併,也不是操作系統相關。彙編代碼里的地址的表達範圍要比變數廣很多,比如,還有指針步長。
你寫的大部分程序都要系統參與,因為一部分庫 API,比如文件讀寫,實際上背後都調用系統 API。
但是有些東西雖然和系統相關,比如目標代碼生成,但它不用系統的參與。你可以想想一下,你可以查表把一段高級語言給手動弄成彙編,這個過程中毫無系統的參與,機器也是這麼乾的。
比如編譯程序是如何知道 本機的硬體信息 而生成其對應的彙編代碼的(彙編代碼機器相關),是操作系統的提供了查詢的介面?
有種東西叫做「交叉編譯」,就是說假如目標主機運算能力太差,不足以在上面跑一個編譯器,那麼就在其它的機子上編譯,再把編譯好的二進位拿過來用。所以可見(1)目標語言和本機沒多大關係,(2)對於一個編譯器來說,能接受什麼語言,能輸出什麼語言,都是寫死了的。
還有比如程序中靜態分配的變數(並非指運行時動態分配的)的存儲位置也是由操作系統分配的嗎?然後我們看到的編譯好的彙編代碼里的那些地址只是類似於高級語言中的變數的標識符而已?
棧、堆、和全局變數區,是系統給你划出來那麼大一塊地方,理論上是你自己管理,但是編譯器把這事給你做了。你如果研究反彙編的話,能了解它是怎麼做的。堆有些複雜,可能還要引入一個庫。
編譯器和操作系統基本上是複雜度最高的兩種軟體工程了。我盡量把我知道的解釋通俗點:
首先是主問題,
編譯程序是否有操作系統的參與?
當然是有。大部分在你機器裡面的操作,列印到屏幕,寫文件,甚至取得當前時間,都是在內核態運行的,在內核態運行,操作系統就必須要參與了。回到問題,編譯器要列印輸出,要寫目標文件(.o),要獲取當前時間,都在內核態運行,所以都要操作系統參與。
第一個子問題
比如編譯程序是如何知道 本機的硬體信息 而生成其對應的彙編代碼的(彙編代碼機器相關),是操作系統的提供了查詢的介面?
給你個實際操作吧,你找台Linux運行:
uname -a
或者
cat /proc/cpuinfo
你就可以拿到CPU信息。稍微深入一點,uname的信息其實也是從cpuinfo來的,在Linux裡面,一切皆文件。那讀文件,根據我上面說的,要不要操作系統參與呢?要的對伐。再啰嗦一句機器碼,機器碼就是這種CPU認識的一套指令,所以才要跑去拿CPU信息。
第二個子問題
還有比如程序中靜態分配的變數(並非指運行時動態分配的)的存儲位置也是由操作系統分配的嗎?然後我們看到的編譯好的彙編代碼里的那些地址只是類似於高級語言中的變數的標識符而已?
這個存貯位置是由可執行文件格式決定的。比如Linux上面的elf,和Windows的PE(?),他們定義了啥放在哪。但是在編譯過程中,決定這個位置的是編譯器的實現邏輯,而純邏輯在用戶態跑就可以,不需要系統調用,所以並沒有操作系統的參與。
對了,我把關鍵字斜體了,方便你進一步搜索。希望能有幫助。
補充一個,忘了還有一個子問題
然後我們看到的編譯好的彙編代碼里的那些地址只是類似於高級語言中的變數的標識符而已?
可以這麼說。但要徹底理解這些地址是咋來的需要了解內存管理和虛內存是啥,這個夠寫一本書,就不展開了。簡單的說,虛地址可以在直接編譯期決定,只有物理地址需要在運行期決定。回到問題,決定這些地址也是編譯器邏輯所以不需要操作系統參與。
這個問題肯定是否定的。一個例子:linus開發linux的時候,肯定是先有的gcc這個編譯器,後有的linux這個操作系統。所以gnu要求linux正式的名字加上 gnu linux。gnu這個項目也是為了要實現一個操作系統而誕生的,所以先實現編譯器。只不過操作系統難產了。
理論上編譯器也不應該依賴操作系統的參與,除非是商業公司實現的有意綁定在自己公司的操作系統上面,好的編譯器也應該是跨硬體的,比如支持各種CPU指令集。支持交叉編譯等等。
那我們這種在x86 Linux下編譯sparc目標碼到裸機prom的找誰去?
我的想法是不管是什麼編譯器,生成本機的,還是交叉編譯的,它總是個程序吧,要運行肯定在操作系統看來是一個進程吧,操作系統肯定參與了
當然要了。你怎麼都得用操作系統提供的io來讀字元串吧。。
有種編譯叫做交叉編譯。。。。
即使不交叉也和當前os沒有半毛錢關係
同意@陸非明的答案
沒有。編譯器是一個翻譯程序,input是高級語言(一個文件),output是一個二進位文件,翻譯的規則是語言標準(針對input)以及各種規範(保證二進位兼容的ABI,文件格式,如ELF等),和操作系統沒有半毛錢關係,強行有關係也就是編譯器本身運行時候和操作系統發生的關係。 而編譯出來的文件如果運行起來,就和操作系統有千絲萬縷的關係了。
編譯器運行需要用到操作系統的系統調用。
早期的編譯器 應該可以在沒有操作系統的 設備上運行吧編譯程序也是程序
strace一下你就知道了……
彙編到機器碼你用16進位編譯器也能做到
高級程序語言首先被編譯成中間的語法樹結構,例如gcc的gimple,再由不同平台的彙編定義,生成彙編代碼,再生成機器碼,交叉編譯在此處實現。鏈接器算出各個變數的邏輯地址,運行時轉換為相應的實際地址,大體過程是這樣。但你要說整個編譯過程沒操作系統參與那是不可能,編譯器作為一個用戶程序,一定要運行在操作系統之上啊……逃……
學完操作系統,編譯原理,連接器和載入器原理就明白了。最後一步生成可執行文件,這個是連接器的工作。這個過程跟操作系統相關,因為操作系統定義了自己的可執行文件格式
推薦閱讀:
※編譯器、解釋器和虛擬機有什麼區別和聯繫?大體原理是什麼?
※編譯器處理轉義符?
※Android上ARM本地庫是如何運行在其他CPU架構上的?
※Linux/Unix中查看一個C/C++大工程中所有函數的調用順序,有哪些方法?
※編譯器的自舉原理是什麼?