2011-04-06 56 views
10

比方說,有一個簡單的程序,如:系統調用如何轉換爲CPU指令?

#include<stdio.h> 

void main() 
{ 
    int x; 
    printf("Cool"); 
    fd = open("/tmp/cool.txt", O_READONLY) 
} 

open是一個系統調用在這裏。我猜想當shell運行它時,它會讓其他幾百個系統調用來實現它?對於像int x這樣的聲明怎麼樣 - 在某些時候它應該在背景中有一些額外的系統調用來從計算機獲取內存?

我不確定系統調用和普通的東西之間的界限是什麼......最後,所有的東西都需要操作系統的幫助?

或者它像是C生成一個可執行的代碼(代碼),它可以在處理器上運行,並且不需要操作系統幫助,直到系統調用到達時 - 在此時它必須執行某些操作來加載操作系統指令etc ...

有點含糊不清:)請澄清。

+0

Idk如果這將有所幫助,但是:「軟件<->(例如系統調用)OS /內核<- Drivers ->硬件」。某些時候,操作系統(例如它是否包括UI,標準支持工具?)和內核(操作系統的「心臟」)和驅動程序(例如內核驅動程序的一部分?)之間的界限是模糊的。零件向左需要零件向右。例如。使用C API - >在Filesystem上執行操作 - >與IDE設備交談,... – 2011-04-06 18:06:30

+0

謝謝。我的問題是儘可能減少內核等的抽象。因此,讓我們說有一個可執行文件 - 稱爲new(使用gcc new.c new創建)。我使用像hexdump新的工具,如0000 01E5 A000等,這是INTEL的CPU代碼? – Nishant 2011-04-06 18:19:12

+0

「也許」。 C和C++(通常)被編譯爲[機器代碼](http://en.wikipedia.org/wiki/Machine_code)。但是,在hexdump中查看的確切數據可能不會與CPU指令相關 - 例如,它可能是可執行文件中的其他未執行數據,例如[COFF頭](http:///en.wikipedia.org/wiki/COFF)或字符串數​​據。然而,這段代碼將使用C運行時並且執行大量系統調用「只是爲了運行」。也就是說,CPU可以在可執行文件中執行機器代碼 - 但只能在其編譯的上下文中執行。 – 2011-04-06 23:38:15

回答

25

我沒有按順序回答問題,所以我在回答問題前加上前綴。我冒昧地編輯了一下它們。您沒有指定處理器架構,但我假設您想知道x86,所以處理器級別的細節將與x86相關。其他體系結構可以有不同的表現(內存管理,系統調用如何進行等)。我也使用Linux作爲例子。

c編譯器是否生成可執行代碼,可直接在處理器上運行而無需OS協助,直至系統調用到達爲止,此時必須執行某些操作才能加載操作系統指令?

是的,這是正確的。編譯器生成可以直接在處理器上運行的本機機器碼。但是,從編譯器獲得的可執行文件包含代碼和其他所需數據,例如,有關將內存中的代碼加載到何處的說明。在Linux上,ELF格式通常用於可執行文件。

如果進程完全加載到內存中並具有足夠的堆棧空間,則在進行系統調用之前不需要進一步的操作系統幫助。進行系統調用時,它只是調用OS的機器代碼中的一條指令。程序本身不需要以任何方式「加載操作系統指令」。處理器處理將執行轉移到OS代碼。

在x86架構上使用Linux,機器代碼進行系統調用的一種方式是使用軟件中斷向量128將執行傳輸到操作系統。在x86彙編(Intel語法)中,表示爲int 0x80。然後,Linux將在執行系統調用之前根據調用程序放入處理器寄存器的值執行任務:系統調用編號位於eax處理器寄存器中,系統調用參數位於其他處理器寄存器中。操作系統完成後,它將在eax寄存器中返回一個結果,並且可能修改了系統調用參數指向的緩衝區。但請注意,這不是進行系統調用的唯一方法。

但是,如果進程不完全在內存中,並且執行轉移到此時不在內存中的代碼的一部分,則處理器會導致頁面錯誤,從而將執行移至操作系統,然後將進程的所需部分加載到內存中,並將執行轉移回進程,然後該進程可以正常繼續執行,甚至不會發現任何事情發生。

我不完全確定下一點,所以拿一粒鹽。維基百科關於stack overflow的文章(計算機錯誤,不是這個網站:)似乎表明堆棧通常是固定大小的,所以int x;不應該導致操作系統運行,除非那部分堆棧不在內存中(見前面段)。如果你有一個具有動態堆棧大小的系統(如果它甚至是可能的,但據我所知,它是),當堆棧空間用完時,int x;也可能導致頁面錯誤,提示操作系統分配更多堆棧空間的過程。

頁面錯誤導致執行轉移到操作系統,但不是通常意義上的系統調用。系統調用是當您希望爲您執行一些工作時對操作系統的明確調用。頁面錯誤和其他此類事件是隱含的。硬件中斷不斷將執行從您的進程轉移到OS,以便它可以對它們做出反應。之後,它將執行轉移回您的進程或其他進程。

在多任務操作系統上,即使只有一個處理器/內核,也可以一次運行多個程序。這是通過一次只運行一個程序來完成的,但是可以快速切換程序。硬件定時器中斷確保控制及時傳回操作系統,以便一個進程不會將CPU全部佔用。當控制傳遞給操作系統並且已經完成了它所需要的操作時,它可能始終啓動與被中斷的過程不同的過程。操作系統完全透明地處理所有這些,所以你不必考慮它,你的過程也不會注意到它。從你的過程看,它正在不斷地執行。

簡而言之:只有當您明確提出要求時,您的程序纔會執行系統調用。操作系統也可以根據需要將進程中的部分進程和內存交換出去,並且通常在後臺執行與進程相關且無關的事情,但通常不需要考慮這些。 (您可以減少頁面錯誤的量,不過,通過保持你的程序儘可能的小,而這樣的事情)

在這種情況下open()是一個明確的系統調用,但我想,當外殼運行它,它使得其他幾百個系統調用來實現它。

不,殼與您的c程序中的open()調用無關。你的程序會進行一次系統調用,而shell根本不會出現。

shell只會影響程序啓動時的程序。當您使用shell啓動程序時,shell會執行系統調用來分離第二個進程,然後執行execve系統調用以將其本身替換爲您的程序。之後,你的程序就可以控制。在控制權到達你的main()函數之前,它執行一些初始化代碼,這是由編譯器放在那裏的。如果您想查看某個進程所進行的系統調用,則可以在Linux上使用strace來查看它們。例如,只需說strace ls即可查看ls在執行過程中所做的系統調用。如果你編譯一個只有main()函數的c程序立即返回,你可以用strace看到初始化代碼所做的系統調用。

該過程如何從計算機等獲取其內存?它必須再次涉及一些系統調用嗎?我不確定系統調用和普通的東西之間的界限是什麼。最終的一切都需要操作系統的幫助,對吧?

是的,系統調用。當您的程序通過execve系統調用加載到內存中時,它會爲您的進程獲取足夠的內存。當您需要更多內存並致電malloc()時,如果系統調用內存緩衝區內存不足以提供您的進程,系統將調用它來增大進程的數據段。

並非所有事情都需要操作系統的明確幫助。如果您有足夠的內存,將所有輸入內存中,並將輸出數據寫入內存,則根本不需要操作系統。也就是說,只要你只對內存中已有的數據進行計算,不需要更多的內存,並且不需要與外部世界進行通信,你就不需要操作系統。另一方面,一個不與外部世界進行通信的程序是一個非常無用的程序,因爲它不能得到任何輸入,並且不能給出任何輸出。即使你計算pi的百萬分之一,如果你不輸出它給用戶也沒有關係。

此回答得到了相當於大,所以萬一我錯過了某些東西或者沒有足夠清楚地解釋某些東西,請給我留言,我會盡量詳細說明。如果有人發現任何錯誤,一定要將它們指出來。

+0

感謝您的好解釋。如果我需要更清晰的話,我會閱讀它併發布commnents。對我來說,描繪整個事物總是很困難,但是會試圖得出一些想法,並詢問是否仍有不清楚的地方。 – Nishant 2011-04-10 08:42:53

+0

不錯的閱讀你的答案..但是「當你需要更多的內存和調用malloc()時,它會做一個brk系統調用來增長數據段」..不應該是堆段嗎? – Laz 2013-03-23 14:40:32

+0

@RamBhat:嗯,不,至少在x86上,真的沒有「堆段」這樣的東西。雖然堆存在於數據段中,而malloc()通常從堆中分配內存,必要時增加數據段(和堆),所以從這個意義上說,你是正確的。 (另外,對不起,我花了很長時間才找到你) – 2014-02-12 21:40:05