Linux並不把進程的樹形結構導出給普通用戶,然而在內核中,它卻使用樹形結構來管理進程。linux內核使用“子進程退出,父進程收屍,父進程退出,子進程被過繼” 這種方式來管理進程的死亡,然而卻少了一種,那就是父進程不給子進程收屍的情況 ,這就是僵屍進程的原因。
既然知道了僵屍進程為何產生,那麼想干掉它們就簡單了。記住:任何沒有人為因素的純技術問題都是可以解決的!如何操作呢?很簡單,就三步:
1.將僵屍進程從樹形進程組織中摘除;
2.將僵屍進程過繼給一個特定的進程;
3.該特定進程調用wait來回收掉它。
這三步豈不是很麻煩,直接干掉它的父進程不就得了,這樣內核會自己將僵屍進程過繼給別的進程或者init進程,然而有時我們不能這麼做,如果它的父進程是個很重要的進程咋辦,我們不能因為父輩拋棄了過早去世的孩子而責怪父親,如果那樣,linux內核的法律豈不是比我們還嚴重...既然父親不要孩子了,那麼建立一個收容所是必要的,使用上述三個步驟完成子進程空殼的過繼和回收!這個收容所可以在內核空間也可以在用戶空間,這不是最重要的。本文給出了一個預研例子:
1.首先給出一個用戶態進程代碼:
- #include <unistd.h>
- int main()
- {
- int pid = 0;
- pid = fork();
- if (pid == 0) { //子進程將瞬間變成僵屍,因為www.linuxidc.com:1.父進程不回收;2.父進程不忽略
- } else {
- while (1) {
- //I'm VIP,though I am always sleeping!
- sleep(1);
- }
- }
- }
- #include <unistd.h>
- int main()
- {
- int pid = 0;
- pid = fork();
- if (pid == 0) { //子進程將瞬間變成僵屍,因為:1.父進程不回收;2.父進程不忽略
- } else {
- while (1) {
- //I'm VIP,though I am always sleeping!
- sleep(1);
- }
- }
- }
2.然後給出一個內核模塊代碼:
- unsigned long pid; //參數保存結束的僵屍進程的進程號
- module_param(pid, long, S_IRUSR);
- MODULE_PARM_DESC(pid, "pid");
- struct task_struct *(*find)(struct pid *pid, enum pid_type type);
- struct pid *(*get)(pid_t nr);
- long (*wait1)(pid_t pid, void *v, int options, void *ru);
- int __init rm_init(void){
- find = 0xc1041aed; //根據pid結構得到task_t函數的地址
- get=0xc1041b81; //根據pid得到pid結構體函數的地址
- wait1 = 0xc1032e02;
- struct pid* spid = (*get)(pid);
- struct task_struct *tsk = (*find)(spid, PIDTYPE_PID);
- tsk->real_parent = current;
- tsk->parent = current;
- list_del(&tsk->sibling);
- list_add_tail(&tsk->sibling, &tsk->real_parent->children);
- (*wait1)(pid, NULL, 0, NULL);
- return 0;
- }
- void __exit rm_exit(void){
- }
- module_init(rm_init);
- module_exit(rm_exit);
- MODULE_LICENSE("GPL");
- unsigned long pid; //參數保存結束的僵屍���程的進程號
- module_param(pid, long, S_IRUSR);
- MODULE_PARM_DESC(pid, "pid");
- struct task_struct *(*find)(struct pid *pid, enum pid_type type);
- struct pid *(*get)(pid_t nr);
- long (*wait1)(pid_t pid, void *v, int options, void *ru);
- int __init rm_init(void){
- find = 0xc1041aed; //根據pid結構得到task_t函數的地址
- get=0xc1041b81; //根據pid得到pid結構體函數的地址
- wait1 = 0xc1032e02;
- struct pid* spid = (*get)(pid);
- struct task_struct *tsk = (*find)(spid, PIDTYPE_PID);
- tsk->real_parent = current;
- tsk->parent = current;
- list_del(&tsk->sibling);
- list_add_tail(&tsk->sibling, &tsk->real_parent->children);
- (*wait1)(pid, NULL, 0, NULL);
- return 0;
- }
- void __exit rm_exit(void){
- }
- module_init(rm_init);
- module_exit(rm_exit);
- MODULE_LICENSE("GPL");
上述的模塊實現了僵屍進程的回收,雖然還不是很完美,然而起碼證實了可行性,www.linuxidc.com我們一些函數的地址還是通過procfs得到的。具體在代碼潤色方面,我有四個建議,這四個方式無論哪一個都是可行的,而且花不了太多時間,這裡代碼就從略了,如果寫一下的話,充其量也只能鍛煉一下c語言編程能力:
1.實現一個內核線程,專門實現模塊init函數的邏輯,需要干掉的僵屍進程號通過procfs傳入內核,然後在write例程中喚醒回收僵屍進程的內核線程;
2.實現一個用戶態進程U,掛載一個信號A的處理函數,內部實現waitpid,通過procfs傳入或者通過netlink傳入內核的僵屍進程號代表的進程過繼給用戶態進程U,然後向U發送信號A;
3./dev/mem的機器碼編程或者直接釋放僵屍進程的task_t。
4.在/proc/<pid>/目錄中加入kill-if-jiangshi文件,寫入1如果該進程是僵屍,那麼就調用上述模塊的邏輯殺死它