Linux的exit()和wait()系統調用

2020-12-10 閒聊代碼

Linux的exit()是進程退出用的,wait()則是父進程用來回收已退出的子進程的資源的。

進程已經調用了exit()退出,而父進程還沒有調用wait()回收資源的狀態,就是殭屍狀態(task zombie)。這時該進程已經不能運行,但還佔著待回收資源。

先看看這兩個函數的man手冊介紹,

exit的參數是退出狀態碼,一般正常退出傳0,出錯退出傳-1,無返回值,因為進程調用了exit()之後就不再返回了。

wait的參數是返回子進程退出狀態的指針,是個傳出參數,返回值是wait到的子進程的pid。

這兩個模擬一下。

先給我們的task_t加兩個成員變量,exit_code表示進程的退出碼,status表示進程的運行狀態。

把他們加在結構體的尾部,這樣不會影響到之前變量的偏移量。

定義兩個進程狀態,TASK_RUN和TASK_ZOMBIE,只有TASK_RUN時才可以被調度執行。

定義SIG_CHILD信號,在子進程退出時發送給父進程。

下圖為exit的實現t_exit()函數,為了簡單依然把task_t的指針傳過來,而不是拿著進程號去找它。

退出碼固定設為-1,設置進程狀態為TASK_ZOMBIE,這時它已經不會被再次調度運行了。

給父進程發送SIG_CHILD信號,然後調用t_schedule()函數進行進程切換,再也沒法切換回來了

因為我們只有2個進程,0號是父進程不能退出,只能1號子進程退出,省略了進程選擇機制,直接切換到0號進程執行。

然後,切換到父進程執行,先進行信號處理,如果是SIG_CHILD信號則使用wait()回收資源並獲取子進程的退出信息。

t_wait()函數先判斷子進程的狀態,如果是TASK_ZOMBIE則讀取它的退出碼,然後把id設為-1,status設為0。

這時這個task_t結構就可以再次使用了。

我們在t_fork()裡查找可用的task_t結構是根據它的id為-1來判斷可用的。

在Linux裡,子進程調用exit(),它的用戶空間的內存、文件描述符等資源是它自己在內核態清理的。清理完畢之後給父進程發信號SIG_CHILD,然後執行schedule()放棄CPU,切換到其他進程。

子進程沒法自己清理的是它的task_struct結構,這個結構在內核裡表示該進程本身,它沒法在運行時釋放這個結構,必須先把自己設置為TASK_ZOMBIE狀態,通知父進程來回收。

下圖為main函數,和之前的差不多,加了行設置0號進程的狀態的代碼,列印了兩個進程的棧底信息。

t0_run()函數的代碼,在子進程退出之後會再次把它fork出來,如果兩進程都在運行則各自列印信息,子進程3秒後退出,如此循環運行。

這是運行的效果,子進程退出之後,父進程會再次用t_fork()把它創建出來,具體代碼在t0_run()函數裡。

rsp顯示的是父子兩進程的棧指針,輪流切換。

相關焦點

  • linux-3.18內核系統調用
    1.系統調用基本概念系統調用是指就是函數調用,只是調用的函數是系統函數,處於內核態而已。多數位置上的sys_ni_syscal都代表了那些已經被內核中淘汰或者沒有實際用處的系統調用。如果一個系統調用被淘汰,它所對應的服務例程就要被指定為sys_ni_syscall。我們並不能將它們的位置分配給其他的系統調用,因為一些老的代碼可能還會使用到它們。否則,如果某個用戶應用試圖調用這些已經被淘汰的系統調用,所得到的結果,比如打開了一個文件,就會與預期完全不同,這將令人感到非常奇怪。
  • 詳解wait和waitpid函數
    但如果我們對這個子進程是如何死掉的毫不在意,只想把這個殭屍進程消滅掉,(事實上絕大多數情況下,我們都會這樣想),我們就可以設定這個參數為NULL,就象下面這樣:pid = wait(NULL);如果成功,wait會返回被收集的子進程的進程ID,如果調用進程沒有子進程,調用就會失敗,此時wait返回-1,同時errno被置為ECHILD。
  • 正確的使用python調用shell的姿勢
    python天生的優勢,用它來開發一些devops的自動化作業是非常方便的,當然在linux上,一般我們用shell就能寫一些簡單的自動化腳本,但如果自動化作業複雜的話,使用shell腳本就很難搞定了,一方面shell腳本量變大就會比較難以工程化,維護和閱讀,另外一個重要的原因是shell不具備正經程式語言所具備的豐富的一些類庫,比如說map類型必須得bash版本4.x以上才有,或者有序
  • Linux 內核通知鏈和例程代碼
    概念大多數內核子系統都是相互獨立的,因此某個子系統可能對其它子系統產生的事件感興趣。
  • 「正點原子Linux連載」第五十二章Linux阻塞和非阻塞IO實驗
    Linux內核提供了等待隊列(wait queue)來實現阻塞進程的喚醒工作,如果我們要在驅動中使用等待隊列,必須創建並初始化一個等待隊列頭,等待隊列頭使用結構體wait_queue_head_t表示,wait_queue_head_t結構體定義在文件include/linux/wait.h中,結構體內容如下所示:示例代碼52.1.2.1 wait_queue_head_t
  • 點亮Linux下Rootkit技能樹
    Linux下自己去「修改 | 劫持系統操作」。讓系統具有我們自己的特色的。本文章的所有代碼基於linux5.03內核的測試。模塊載入到執行的過程有一個chain的操作,涉及到消息通知;而在這之間,我們有機會修改。
  • C語言裡,main 函數中 return x和 exit(x) 到底有什麼區別?
    (註:棧會返回一個詭異一個棧地址,對於某些內核版本的實現,直接報「棧錯誤」就給跪了,然而,對於某些內核版本的實現,於是有可能會再次調用main(),於是進入了一個無限循環的結果,直到vfork 調用返回 error)好了,現在再回到 return 和 exit,return會釋放局部變量,並彈棧,回到上級函數執行。exit直接退掉。
  • 從linux源碼看epoll
    和select、poll等系統調用相比,epoll在需要監視大量文件描述符並且其中只有少數活躍的時候,表現出無可比擬的優勢。epoll能讓內核記住所關注的描述符,並在對應的描述符事件就緒的時候,在epoll的就緒鍊表中添加這些就緒元素,並喚醒對應的epoll等待進程。
  • linux字符設備驅動基本框架
    1.linux函數調用過程1.1 系統函數調用的意義在Linux的中,有一個思想比較重要:一切皆文件。也就是說,在應用程式中,可以通過open,write,read等函數來操作底層的驅動。它不能訪問內核所佔內存空間也不能調用內核函數。CPU硬體決定了這些(這就是為什麼它被稱作"保護模式")。為了和用戶空間上執行的進程進行交互,內核提供了一組接口。透過該接口,應用程式能夠訪問問硬體設備和其它作業系統資源。這組接口在應用程式和內核之間扮演了使者的角色,應用程式發送各種請求。而內核負責滿足這些請求(或者讓應用程式臨時擱置)。實際上提供這組接口主要是為了保證系統穩定可靠。
  • Linux 下的進程間通信:共享存儲 | Linux 中國
    = F_UNLCK) report_and_exit("file is still write locked...");在 fcntl 調用中的 F_GETLK 操作指定檢查一個鎖,在本例中,上面代碼的聲明中給了一個 F_WRLCK 的排斥鎖。假如特指的鎖不存在,那麼 fcntl 調用將會自動地改變鎖類型域為 F_UNLCK 以此來顯示當前的狀態。
  • 「正點原子Linux連載」第五十八章Linux INPUT子系統實驗
    本章我們就來學習一下Linux內核中的input子系統。58.1 input子系統58.1.1input子系統簡介input就是輸入的意思,因此input子系統就是管理輸入的子系統,和pinctrl和gpio子系統一樣,都是Linux內核針對某一類設備而創建的框架。
  • 對於linux下system()函數的深度理解
    這幾天調程序(嵌入式linux),發現程序有時就莫名其妙的死掉,每次都定位在程序中不同的system()函數,直接在shell下輸入system()函數中調用的命令也都一切正常.就沒理這個bug,以為是其他的代碼影響到這個,或是內核驅動文件系統什麼的異常導致,昨天有出現了這個問題,就隨手百了一下度,問題出現了,很多人都說system()函數要慎用要少用要能不用則不用
  • Linux Kernel代碼藝術——系統調用宏定義
    下面以open系統調用為例分析這個宏是如何展開的:首先在 include/linux/syscall.h 中有下面這樣的宏定義:#define SYSCALL_DEFINE3(name, ...如何在 SI 中找到系統調用原始碼回到開始的話題,既然不能直接通過系統調用聲明跳轉到定義的代碼處,那麼怎樣在 SI 快速找到系統調用的源碼呢?
  • Linux內核學習:簡單的字符設備驅動
    對於我們日常生活中存在的大量設備,如攝像頭,USB充電器,藍牙,Wi-Fi等,這些設備在電氣特性和實現原理均不相同,對Linux系統來說如何抽象和描述他們呢?Linux很早就根據設備共同特徵將其劃分為三大類型:1,字符設備;塊設備;網絡設備。
  • Linux下編譯時出現的錯誤及解決方法
    (1)由於是Linux新手,所以現在才開始接觸線程編程,照著GUN/Linux編程指南中的一個例子輸入編譯,結果出現如下錯誤:undefined reference to 'pthread_create'undefined reference to 'pthread_join'問題原因:pthread 庫不是 Linux 系統默認的庫
  • linux-kernel-pwn-csaw-2015-stringipc
    環境的配置(內核編譯以及製作文件系統)以及ko的編譯可以參照基礎知識[2]這一個章節。我的環境是內核linux-4.4.110[3],文件系統是busybox-1.31.0[4]。VDSO是內核為了減少內核與用戶空間頻繁切換,提高系統調用效率而提出的機制。特別是gettimeofday這種對時間精度要求特別高的系統調用,需要儘可能地減少用戶空間到內核空間堆棧切換的開銷。
  • 3分鐘短文|Linux 命令正確的退出方式:exit 0
    在本文中,我們將介紹Bash exit內置命令和已執行命令的退出狀態。退出狀態每個shell命令在成功終止時都會返回退出代碼。按照慣例,退出代碼為零表示命令已成功完成,非退出代碼為零表示遇到錯誤。Bash exit 指令該exit命令以 N 狀態退出 shell 程序。它具有以下語法:exit N如果N未給出,則退出狀態代碼是最後執行的命令的退出狀態代碼。