在之間的章節我們了解了程序的建立和執行,在程序執行完畢或者發生一些意料之外的事之後難免會被結束,因此linux提供了exit()
的系統呼叫來完成程序結束以及釋放占用資源的功能。其中有兩種系統呼叫能用來完成結束程序或者執行緒的功能。
sys_exit()
可以用來結束單個程序或執行緒,sys_exit_group()
可以用來結束乙個執行緒組,均需要傳入乙個錯誤碼(error_code)來執行。 宣告如下:(路徑:/kernel-4.19/includelinuxsyscalls.h)
asmlinkage long sys_exit(int error_code);
asmlinkage long sys_exit_group(int error_code);
實現如下:(路徑:/kernel-4.19/kernel/exit.c)
syscall_define1(exit, int, error_code)
...syscall_define1(exit_group, int, error_code)
從以上可以看出,系統呼叫sys_exit()
是呼叫了do_exit()
函式,系統呼叫sys_exit_group()
是呼叫了do_group_exit()
函式。
接下來再分別看看do_exit()
以及do_group_exit()
函式的實現過程。
do_exit()
的作用是結束當前的程序,部分**如下[1]:(路徑:/kernel-4.19/kernel/exit.c)
void __noreturn do_exit(long code)
//如果此pf_exiting標識未被設定, 則通過exit_signals來設定
exit_signals(tsk); /* sets pf_exiting */
/*記憶體屏障,用於確保在它之後的操作開始執行之前,它之前的操作已經完成*/
smp_mb();
/* 一直等待,直到獲得current->pi_lock自旋鎖 */
raw_spin_lock_irq(&tsk->pi_lock);
raw_spin_unlock_irq(&tsk->pi_lock);
.../* 釋放線性區描述符和頁表 */
exit_mm();
/* 輸出程序審計資訊 */
if (group_dead)
acct_process();
trace_sched_process_exit(tsk);
/* 釋放使用者空間的「訊號量」 */
exit_sem(tsk);
/* 釋放鎖 */
exit_shm(tsk);
/* 釋放檔案物件相關資源 */
exit_files(tsk);
exit_fs(tsk);
/* 脫離控制終端 */
if (group_dead)
disassociate_ctty(1);
/* 釋放命名空間 */
exit_task_namespaces(tsk);
exit_task_work(tsk);
/* 釋放task_struct中的thread_struct結構 */
.../* 更新所有子程序的父程序 */
exit_notify(tsk, group_dead);
/* 程序事件聯結器(通過它來報告程序fork、exec、exit以及程序使用者id與組id的變化) */
proc_exit_connector(tsk);
mpol_put_task_policy(tsk);
.../* 釋放struct io_context結構體所占用的記憶體 */
if (tsk->io_context)
exit_io_context(tsk);
/* 釋放與程序描述符splice_pipe欄位相關的資源 */
if (tsk->splice_pipe)
free_pipe_info(tsk->splice_pipe);
.../* 檢查有多少未使用的程序核心棧 */
check_stack_usage();
...do_task_dead();
}
在最後的do_task_dead()
中呼叫了__schedule()
切換到了其他就緒程序,至此該程序在作業系統中的生命週期就完全結束了。
我們如果了解linux的執行緒實現機制的話,會知道所有的執行緒是屬於乙個執行緒組的。即使不是執行緒, linux也允許多個程序組成程序組,多個程序組組成乙個會話,因此我們了解到不管是多執行緒還是程序組,其本質都是多個程序組成的乙個集合,那麼我們的應用程式在退出的時候,自然希望一次性的退出組內所有的程序,do_group_exit()
也就應運而生了[2]。
do_group_exit()
函式將會殺死屬於當前執行緒組的所有執行緒,它具體執行的是下述操作[2]:
檢查當前程序的signal_group_exit
標誌是否不為0,如果不為0,說明核心已經開始為線性組執行退出的過程。在這種情況下,就把存放在current
->signal
->group_exit_code
的值當作退出碼,然後跳轉到第4步。
否則,設定程序的signal_group_exit
標誌並把終止代號放到current
->signal
->group_exit_code
字段。
呼叫zap_other_threads()
函式殺死current
執行緒組中的其它程序。為了完成這個步驟,函式掃瞄與current
->tgid
對應的pidtype_tgid
型別的雜湊表中的pid
鍊錶,向表中所有不同於current
的程序傳送sigkill
訊號,所有這樣的程序都將執行do_exit()
函式,從而被結束。
呼叫do_exit()
函式,把程序的終止**傳遞給它。正如我們將在之前看到的,do_exit()
結束程序而且不再返回。
do_group_exit()
的部分**如下:(路徑:/kernel-4.19/kernel/exit.c)
void do_group_exit(int exit_code)
spin_unlock_irq(&sighand->siglock);
}/* 呼叫do_exit() */
do_exit(exit_code);
}
在上述的函式中,清除了當前程序所占用或者與它相關的資料結構,最後執行的一步都將是schedule()
函式,在這個函式中就將跳轉到其他的程序。由於當前程序的各種相關的資料結構都不再存在,因此永遠也不會再跳轉到這個程序來執行,也就是程序被永遠宣告了死亡。
在本章我們了解了程序結束的過程,分別學習了do_exit()
和do_group_exit()
函式,簡而言之,他們的作用都是清除關於程序或程序組所佔據的資源並開啟排程。關於程序的生命週期(建立、執行、結束)的講解到這裡就告一段落,在下一章我們將開始對程序位址空間的學習。
參考:
[1][3]《深入linux核心架構》
作用 和會話期 程序組 程序組和會話
程序組 程序組是指乙個或多個程序的集合。通常與乙個作業相關聯,可以接收來自同一終端的訊號。每個程序組有乙個唯一的程序組id,它類似於程序id,是乙個正整數 其實就是組長程序的程序id 可以通過函式獲得 include pid t getpgrp void 程序組都有乙個組長,組長程序的標識是其程序組...
Linux程序組和會話
linux的程序相互之間有一定的關係。比如說,在linux程序基礎中,我們看到,每個程序都有父程序,而所有的程序以init程序為根,形成乙個樹狀結構。我們在這裡講解程序組和會話,以便以更加豐富的方式了管理程序。每個程序都會屬於乙個程序組 process group 每個程序組中可以包含多個程序。程序...
程式設計訓練第五十期 合併區間
以陣列 intervals 表示若干個區間的集合,其中單個區間為 intervals i starti,endi 請你合併所有重疊的區間,並返回乙個不重疊的區間陣列,該陣列需恰好覆蓋輸入中的所有區間。1.排序 雙指標 如果我們按照區間的左端點排序,那麼在排完序的列表中,可以合併的區間一定是連續的。我...