一、進程組,作業,會話的概念
1、進程組:是一個或多個進程的集合。通常,與同一作業相關聯,可以接收來自同一終端的各種信號。每個進程都有一個唯一的進程組ID。每個進程組都可以有一個組長進程。組長進程的標識是,其進程組ID等於其進程ID。組長進程可以創建一個進程組,創建該組中的進程,然後終止。只要在某個進程組中有一個進程存在,則該進程組就存在,這與組長進程是否終止無關。
2、作業:Shell分前後臺來控制的不是進程而是作業(job)或者進程組。一個前臺作業可以由多個進程組成,一個後臺也可以由多個進程組成,shell可以運行一個前臺作業和任意多個後臺作業,這稱爲作業控制。
作業與進程組的區別:如果作業中的某個進程又創建了子進程,則子進程不屬於作業。
一旦作業運行結束,Shell就把自己提到前臺,如果原來的前臺進程還存在(如果這個子進程還沒終止),它自動變爲後臺進程組。
3、會話:是一個或多個進程組的集合。一個會話可以有一個控制終端。建立與控制終端連接的會話首進程被稱爲控制進程。一個會話中的幾個進程組可被分爲一個前臺進程組以及一個或多個後臺進程組。所以一個會話中,應該包括控制進程(會話首進程),一個前臺進程組和任意後臺進程組。
****************(進程組就相當於一個班級,組長進程就像當於班長。會話就相當於是一個年級。一個年級有一個教務處。(控制終端))。
二、終端
1、終端的概念:用戶通過終端登錄系統後得到一個Shell進程,這個終端稱爲Shell進程的控制終端,
每個進程都可以通過一個特殊的設備文件/dev/tty訪問它的控制終 端。事實上每個終端設備
都對應一個不同的設備文件,/dev/tty提供了一個通用的接口,一個進程要訪問它的控制終端既可以通過/dev/tty也可以通過該終端設備所對應的設備文件來訪問。ttyname函數可以由文件描述符查出對應的文件名,該文件描述符必須指向一個終端設備而不能是任意文件。
****************查看一下各種不同的終端所對應的設備文件名。
1 #include<stdio.h> 2 #include<unistd.h> 3 int main() 4 { 5 printf("fd: %d -> %s\n",0,ttyname(0)); 6 printf("fd: %d -> %s\n",1,ttyname(1)); 7 printf("fd: %d -> %s\n",2,ttyname(2)); 8 }
2、終端登錄過程:
一臺PC通常只有一套鍵盤和顯示器,也就是隻有一套終端設備,但是可以通過Ctrl-Alt-F1~Ctrl-Alt- F6切換到6個字符終端,相當於有6套虛擬的終端設備,它們共用同一套物理終端設備,對應的設備 文件分別是/dev/tty1~/dev/tty6,所以稱爲虛擬終端(Virtual Terminal)。設備文件/dev/tty0表 示當前虛擬終端,比如切換到Ctrl-Alt-F1的字符終端時/dev/tty0就表示/dev/tty1,切換到Ctrl-Alt- F2的字符終端時/dev/tty0就表示/dev/tty2,就像/dev/tty一樣也是一個通用的接口,但它不能表示圖形終端窗口所對應的終端。
a、系統啓動時,init進程根據配置文件/etc/inittab確定需要打開哪些終端。
b、getty根據命令行參數打開終端設備作爲它的控制終端,把文件描述符0、1、2都指向控制
終端,然後提示用戶輸入帳號。用戶輸入帳號之後,getty的任務就完成了,它再執行login程序:
execle("/bin/login", "login", "-p", username, NULL, envp);
c、login程序提示用戶輸入密碼(輸入密碼期間關閉終端的回顯),然後驗證帳號密碼的正確性。 如果密碼不正確,login進程終止,init會重新fork/exec一個getty進程。如果密碼正確,login程序設置一些環境變量,設置當前工作目錄爲該用戶的主目錄,然後執行Shell:
execl("/bin/bash", "-bash", NULL);
三、守護進程
1、 守護進程也稱精靈進程(Daemon),是運行在後臺的一種特殊進程。它獨立於控制終端並且週期性地執行某種任務或等待處理某些發生的事件。
2、用ps axj | grep -E 'd$'查看守護進程
參數a表示不僅列當前用戶的進程,也列出所有其他用戶的進程,參數x表示不僅列有控制終端的進程,也列出所有無控制終端的進程,參數j表示列出與作業控制相關的信息。
3、創建守護進程
調用函數setsid函數創建一個新的Session,併成爲Session Leader(會話首進程),調用成功返回新創建的Session id,出錯返回-1;
a、調用umask將文件模式創建屏蔽字設置爲0.
b、調用fork,父進程退出(exit)。原因:
1)如果該守護進程是作爲一條簡單的shell命令啓動的,那麼父進程終止使得shell認爲該命令已經執行完畢。
2)保證子進程不是一個進程組的組長進程。
c、調用setsid創建一個新會話。setsid會導致:
1)調用進程成爲新會話的首進程。
2)調用進程成爲一個進程組的組長進程 。
3)調用進程沒有控制終端。(再次fork一次,保證daemon進程,之後不會打開tty設備)
d、將當前工作目錄更改爲根目錄。
e、關閉不在需要的文件描述符。
f、其他:忽略SIGCHLD信號。
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<unistd.h> 4 void mydeamon(void) 5 { 6 pid_t id=fork(); 7 umask(0);//將文件模式創建屏蔽字設置爲0. 8 if(id>0) 9 { 10 exit(0);//調用fork,父進程退出(exit) 11 } 12 setsid();//調用setsid創建一個新會話 13 chdir("/");//將當前工作目錄更改爲根目錄。 14 close(0);//關閉不在需要的文件描述符。 15 close(1); 16 close(2); 17 } 18 int main() 19 { 20 mydeamon(); 21 while(1); 22 return 0; 23 }
用ps axj |grep "文件名"查看剛纔創建的守護進程