Linux Process Manage

Linux 下的工做都是依靠進程來執行的,控制了進程就至關於控制了 Linux 系統了。這篇博客將經過 Linux 系統的啓動登陸來探討進程管理機制,看這種機制如何支撐和左右進程的命運。linux

什麼是進程

先來了解了解什麼是進程,程序這個詞比較好理解,一般的程序是靜態實體,進程是正在運行的程序實體,而且包括這個運行的程序中佔據的全部系統資源,好比說CPU(寄存器),IO,內存,網絡資源等。進程描述符(PID)是惟一用來標識進程的。c++

進程的建立

fork 和 exec 分別是進程的分身和變身

在運行級別3下啓動 Linux,出現命令行界面須要在「login: 」提示符處輸入用戶名登陸,能夠另外找一臺機子ssh遠程鏈接,查看一下mingetty進程的執行狀況:git

# ps -ef|grep mingett[y]
root     14450     1  0 14:10 tty1     00:00:00 /sbin/minagetty --noclear tty1 linux
root     14566     1  0 14:13 tty2     00:00:00 /sbin/minagetty --noclear tty2 linux
root     14589     1  0 14:16 tty3     00:00:00 /sbin/minagetty --noclear tty3 linux
root     14591     1  0 14:16 tty4     00:00:00 /sbin/minagetty --noclear tty4 linux
root     14593     1  0 14:16 tty5     00:00:00 /sbin/minagetty --noclear tty5 linux
root     14595     1  0 14:16 tty6     00:00:00 /sbin/minagetty --noclear tty6 linux

這裏有6個mingetty進程,對應CTRL+ALT+F1~F6六個虛擬控制檯。github

在tty1輸入用戶名並按回車,這裏先不要輸入密碼,回到ssh遠程登陸終端上,再看看mingetty進程,會發現少了PID爲14450的mingetty進程,能夠利用ps命令檢索PID。shell

# ps -ef|grep 1445[0]
root     14450     1  0 15:36 tty1     00:00:00 /bin/login --

PID爲14450的進程變成了login進程了。這是由於mingetty進程在exec()系統調用的做用下,轉變成了login進程。bash

exec的做用是捨棄進程原先攜帶的信息,在進程執行時用新的程序代碼替代調用進程的內容。網絡

能夠分析一下mingetty進程中運行exec的部分源碼:session

exec(loginprog, loginprog, autologin? "-f" : "--", logname, NULL);

mingetty進程的工做是接收登陸用戶名,以後的密碼驗證處理工做則是 login 進程的工做,當驗證結束後,便啓動用戶的bash進程。ssh

一樣的再次檢索同一個PID會發現 login 進程保留了原先的相同的進程,並且還多了一個 bash 進程。這是由於 bash 進程的父進程ID是14450,這說明bash進程是做爲 login 進程的子進程開始啓動的。函數

exec 和 fork 中進程的變化

┌─────┐
│進程  
│PID=X  
│程序=A  
└─────┘
   │
   ↓
┌─────┐
│進程     
│PID=X    
│程序=B   
└─────┘
┌─────┐
│父進程  
│PID=X    ──┓ 
│程序=A      │
└─────┘     │ fork
   │        │
   ↓         ↓
┌─────┐   ┌─────┐
│父進程    │子進程  
│PID=X    │ PID=Y  
│程序=A    │程序=A   
└─────┘   └─────┘

一般fork一個進程是指經過父進程建立一個子進程,生成的子進程與父進程只有PID不同,login 進程經過fork生成一個自身的副本後,還會在子進程經過exec啓動 bash 。這樣的機制叫作「fork-exec」

childPid = fork();//建立子進程
    if (childPid < 0) {
        int errsv = errno;
        fprintf(stderr, _("login: failure forking: %s"), strerror(errsv));
        PAM_END;
        exit(0);
    }
    
    if (childPid) {//父進程,等待子進程退出
    /* parent - wait for child to finish, then cleanup session */
        signal(SIGHUP, SIG_IGN);
        signal(SIGINT, SIG_IGN);
        signal(SIGQUIT, SIG_IGN);
        signal(SIGTSTP, SIG_IGN);
        signal(SIGTTIN, SIG_IGN);
        signal(SIGTTOU, SIG_IGN);//忽略以上信號
        
        wait(NULL);//等待子進程結束
        PAM_END;//PAM結束
        exit(0);
    }
    //如下是子進程
    /* child */
    
    //(省略部分源碼)
    childArgv[childArgc++] = NULL;
    //登陸成功,執行/bin/sh進入shell
    execvp(childArgv[0], childArgv + 1);

上面是login.c的源碼,能夠知道父進程會一直等待子進程結束(wait),父進程纔會結束。

進程的結束

在已登陸的控制檯上輸入 exit 進行用戶註銷,此時exit()系統調用,bash進程會被終止,同時發送CHLD信號給父進程login。接收到CHLD信號的父進程login會退出wait函數,同時結束進程。wait是一個函數,它讓父進程在接收子進程CHLD信號以前一直保持休眠狀態。

另外一方面,子進程在向父進程發送CHLD信號,直到父進程接收爲止,子進程一直保持殭屍狀態。

博客地址 https://github.com/Junnplus/blog/issues/6

相關文章
相關標籤/搜索