Linux下的C程式設計實戰之三程序控制

2021-05-26 23:44:01 字數 4155 閱讀 2678

1.linux程序

linux程序在記憶體中包含三部分資料:**段、堆疊段和資料段。**段存放了程式的**。**段可以為機器中執行同一程式的數個程序共享。堆疊段存放的是子程式(函式)的返回位址、子程式的引數及程式的區域性變數。而資料段則存放程式的全域性變數、常數以及動態資料分配的資料空間(比如用malloc函式申請的記憶體)。與**段不同,如果系統中同時執行多個相同的程式,它們不能使用同一堆疊段和資料段。

linux程序主要有如下幾種狀態:使用者狀態(程序在使用者狀態下執行的狀態)、核心狀態(程序在核心狀態下執行的狀態)、記憶體中就緒(程序沒有執行,但處於就緒狀態,只要核心排程它,就可以執行)、記憶體中睡眠(程序正在睡眠並且處於記憶體中,沒有被交換到swap裝置)、就緒且換出(程序處於就緒狀態,但是必須把它換入記憶體,核心才能再次排程它進行執行)、睡眠且換出(程序正在睡眠,且被換出記憶體)、被搶先(程序從核心狀態返回使用者狀態時,核心搶先於它,做了上下文切換,排程了另乙個程序,原先這個程序就處於被搶先狀態)、建立狀態(程序剛被建立,該程序存在,但既不是就緒狀態,也不是睡眠狀態,這個狀態是除了程序0以外的所有程序的最初狀態)、僵死狀態(程序呼叫exit結束,程序不再存在,但在程序表項中仍有記錄,該記錄可由父程序收集)。

下面我們來以乙個程序從建立到消亡的過程講解linux程序狀態轉換的「生死因果」。

(1)程序被父程序通過系統呼叫fork建立而處於建立態;

(2)fork呼叫為子程序配置好核心資料結構和子程序私有資料結構後,子程序進入就緒態(或者在記憶體中就緒,或者因為記憶體不夠而在swap裝置中就緒);

(3)若程序在記憶體中就緒,程序可以被核心排程程式排程到cpu執行;

(4)核心排程該程序進入核心狀態,再由核心狀態返回使用者狀態執行。該程序在使用者狀態執行一定時間後,又會被排程程式所排程而進入核心狀態,由此轉入就緒態。有時程序在使用者狀態執行時,也會因為需要核心服務,使用系統呼叫而進入核心狀態,服務完畢,會由核心狀態轉回使用者狀態。要注意的是,程序在從核心狀態向使用者狀態返回時可能被搶占,這是由於有優先順序更高的程序急需使用cpu,不能等到下一次排程時機,從而造成搶占;

(5)程序執行exit呼叫,進入僵死狀態,最終結束。

2.程序控制

程序控制中主要涉及到程序的建立、睡眠和退出等,在linux中主要提供了fork、exec、clone的程序建立方法,sleep的程序睡眠和exit的程序退出呼叫,另外linux還提供了父程序等待子程序結束的系統呼叫wait。

fork

對於沒有接觸過unix/linux作業系統的人來說,fork是最難理解的概念之一,它執行一次卻返回兩個值,完全「不可思議」。先看下面的程式:

int main()

else}

執行結果為:

this

is child process

this is child process

this is parent process

this is parent process

fork在英文中是「分叉」的意思,這個名字取得很形象。乙個程序在執行中,如果使用了fork,就產生了另乙個程序,於是程序就「分叉」了。當前程序為父程序,通過fork()會產生乙個子程序。對於父程序,fork函式返回子程式的程序號而對於子程式,fork函式則返回零,這就是乙個函式返回兩次的本質。可以說,fork函式是unix系統最傑出的成就之一,它是七十年代unix早期的開發者經過理論和實踐上的長期艱苦探索後取得的成果。

如果我們把上述程式中的迴圈放的大一點:

int main()

else}

則可以明顯地看到父程序和子程序的併發執行,交替地輸出「this is child process」和「this is parent process」。

此時此刻,我們還沒有完全理解fork()函式,再來看下面的一段程式,看看究竟會產生多少個程序,程式的輸出是什麼?

int main()

else}}

exec

在linux中可使用exec函式族,包含多個函式(execl、execlp、execle、execv、execve和execvp),被用於啟動乙個指定路徑和檔名的程序。

exec函式族的特點體現在:某程序一旦呼叫了exec類函式,正在執行的程式就被乾掉了,系統把**段替換成新的程式(由exec類函式執行)的**,並且原有的資料段和堆疊段也被廢棄,新的資料段與堆疊段被分配,但是程序號卻被保留。也就是說,exec執行的結果為:系統認為正在執行的還是原先的程序,但是程序對應的程式被替換了。

fork函式可以建立乙個子程序而當前程序不死,如果我們在fork的子程序中呼叫exec函式族就可以實現既讓父程序的**執行又啟動乙個新的指定程序,這實在是很妙的。fork和exec的搭配巧妙地解決了程式啟動另一程式的執行但自己仍繼續執行的問題,請看下面的例子:

char command[max_cmd_len];

void main()

else}}

這個函式基本上實現了乙個shell的功能,它讀取使用者輸入的程序名和引數,並啟動對應的程序。

clone

clone是linux2.0以後才具備的新功能,它較fork更強(可認為fork是clone要實現的一部分),可以使得建立的子程序共享父程序的資源,並且要使用此函式必須在編譯核心時設定clone_actually_works_ok選項。

clone函式的原型為:

int clone(int (*fn)(void *), void *child_stack, int flags, void *arg);

標誌含義clone_parent

建立的子程序的父程序是呼叫者的父程序,新程序與建立它的程序成了「兄弟」而不是「父子」

clone_fs

子程序與父程序共享相同的檔案系統,包括root、當前目錄、umask

clone_files

子程序與父程序共享相同的檔案描述符(file descriptor)表

clone_newns

在新的namespace啟動子程序,namespace描述了程序的檔案hierarchy

clone_sighand

子程序與父程序共享相同的訊號處理(signal handler)表

clone_ptrace

若父程序被trace,子程序也被trace

clone_vfork

父程序被掛起,直至子程序釋放虛擬記憶體資源

clone_vm

子程序與父程序執行於相同的記憶體空間

clone_pid

子程序在建立時pid與父程序一致

clone_thread

linux 2.4中增加以支援posix執行緒標準,子程序與父程序共享相同的執行緒群

來看下面的例子:

int variable, fd;

int do_something()

int main(int argc, char *argv)

printf("we could read from the file\n");

return 0;}

執行輸出:

the variable is now 42

file read error

程式的輸出結果告訴我們,子程序將檔案關閉並將變數修改(呼叫clone時用到的clone_vm、clone_files標誌將使得變數和檔案描述符表被共享),父程序隨即就感覺到了,這就是clone的特點。

sleep

函式呼叫sleep可以用來使程序掛起指定的秒數,該函式的原型為:  

unsigned int sleep(unsigned int seconds);

該函式呼叫使得程序掛起乙個指定的時間,如果指定掛起的時間到了,該呼叫返回0;如果該函式呼叫被訊號所打斷,則返回剩餘掛起的時間數(指定的時間減去已經掛起的時間)。

exit

系統呼叫exit的功能是終止本程序,其函式原型為:

void _exit(int status);

_exit會立即終止發出呼叫的程序,所有屬於該程序的檔案描述符都關閉。引數status作為退出的狀態值返回父程序,在父程序中通過系統呼叫wait可獲得此值。

wait

wait系統呼叫包括:

pid_t wait(int *status);

pid_t waitpid(pid_t pid, int *status, int options);

wait的作用為發出呼叫的程序只要有子程序,就睡眠到它們中的乙個終止為止; waitpid等待由引數pid指定的子程序退出。

Linux程式設計基礎之三 Linux檔案管理操作

檔案系統中乙個目錄所包含的目錄項組成的檔案。目錄檔案只允許系統進行修改。使用者程序可以讀取目錄檔案,但不能對它們進行修改。特別地 代表目錄本身 代表父目錄 字元裝置檔案和塊裝置檔案。linux把對裝置的 i o作為對檔案的讀取 寫入操作核心提供了對裝置處理和對 檔案處理的統一介面。裝置檔案沒有檔案長...

LINUX下的C程式設計實戰(一) 開發平台搭建

1.引言 linux作業系統在伺服器領域的應用和普及已經有較長的歷史,這源於它的開源特點以及其超越windows的安全性和穩定性。而近年來,linux作業系統在嵌入式系統領域的延伸也可謂是如日中天,許多版本的嵌入式linux系統被開發出來,如uclinux rtlinux arm linux等等。在...

linux下的C 程式設計

第一步,要安裝c 的編譯器g 使用如下命令 root wl ms 7673 home wl 桌面 c apt get install g 第二步,開始我們的hello world 使用vim建立helloworld.cpp,輸入如下 root wl ms 7673 home wl 桌面 c cat ...