轉自:http://shake863.javaeye.com/blog/187085
將闡述Linux核心中的如下幾個概念
1) 進程組
2) 會話
3) 控制終端
1.概念:
a)進程組
Shell 上的一條命令列形成一個進程組
每個進程屬於一個進程組
每個進程組有一個領頭進程
進程組的生命週期到組中最後一個進程終止, 或加入其他進程組為止
getpgrp: 獲得進程組 id, 即領頭進程的 pid
setpgid: 加入進程組和建立新的進程組
前台進程組和後台進程組
#include <unistd.h> int setpgid (pid_t pid, pid_t pgid); pid_t getpgid (pid_t pid); int setpgrp (void); pid_t getpgrp (void);
進程只能將自身和其子進程設定為進程組 id.
某個子進程調用 exec 函數之後, 就不能再將該子進程的 id 作為進程組 id.
===============================================================================
b)會話
一次登入形成一個會話
一個會話可包含多個進程組, 但只能有一個前台進程組.
setsid 可建立一個新的會話
===============================================================================
#include <unistd.h>
pid_t setsid(void);
-------------------------------------------------------------------------------
如果調用進程不是進程組的領頭進程, 該函數才能建立新的會話.
調用 setsid 之後, 進程成為新會話的領頭進程.
進程成為新進程組的領頭進程.
進程失去控制終端
===============================================================================
c)控制終端
會話的領頭進程開啟一個終端之後, 該終端就成為該會話的控制終端 (SVR4/Linux)
與控制終端建立串連的會話領頭進程稱為控制進程 (session leader)
一個會話只能有一個控制終端
產生在控制終端上的輸入和訊號將發送給會話的前台進程組中的所有進程
終端上的串連斷開時 (比如網路斷開或 Modem 斷開), 掛起訊號將發送到控制進程(session leader)
總的來說,由於 Linux 是一個多使用者系統,同一時刻,系統中運行有屬於不同使用者的多個進程。那麼,當處於某個終端上的使用者按下了 Ctrl+C 鍵時(產生 SIGINT 訊號),系統如何知道將該訊號發送到哪個進程,從而不影響由其他終端上的使用者啟動並執行進程呢。
Linux 核心通過維護會話和進程組而管理多使用者進程。如下圖所示,每個進程是一個進程組的成員,而每個進程組又是某個會話的成員。一般而言,當使用者在某個終端上登入時,一個新的會話就開始了。進程組由組中的領頭進程標識,領頭進程的進程標識符就是進程組的群組識別碼。類似地,每個會話也對應有一個領頭進程。
同一會話中的進程通過該會話的領頭進程和一個終端相連,該終端作為這個會話的控制終端。一個會話只能有一個控制終端,而一個控制終端只能控制一個會話。使用者通過控制終端,可以向該控制終端所控制的會話中的進程發送鍵盤訊號。
同一會話中只能有一個前台進程組,屬於前台進程組的進程可從控制終端獲得輸入,而其他進程均是後台進程,可能分屬於不同的後台進程組。
2. Linux中的實現舉例,用以驗證上述規則:
asmlinkage long sys_getpgid(pid_t pid) { if (!pid) { return current->pgrp; } else { int retval; struct task_struct *p; read_lock(&tasklist_lock); p = find_task_by_pid(pid); retval = -ESRCH; if (p) retval = p->pgrp; read_unlock(&tasklist_lock); return retval; } } /* * This needs some heavy checking ... * I just haven't the stomach for it. I also don't fully * understand sessions/pgrp etc. Let somebody who does explain it. * * OK, I think I have the protection semantics right.... this is really * only important on a multi-user system anyway, to make sure one user * can't send a signal to a process owned by another. -TYT, 12/12/91 * * Auch. Had to add the 'did_exec' flag to conform completely to POSIX. * LBT 04.03.94 */ asmlinkage long sys_setpgid(pid_t pid, pid_t pgid) { struct task_struct * p; int err = -EINVAL; if (!pid) pid = current->pid; if (!pgid) pgid = pid; if (pgid < 0) return -EINVAL; /* From this point forward we keep holding onto the tasklist lock * so that our parent does not change from under us. -DaveM */ read_lock(&tasklist_lock); /*第一前提: 先要驗證要設定的進程是否存在,不存在的話不能做事*/ err = -ESRCH; p = find_task_by_pid(pid); if (!p) goto out; /* 第二前提: 先要檢查做這個操作的許可權: 當前進程只能將自身和其子進程設定為進程組id,並且 當前進程和其子進程必須屬於同一次會話 (同組的進程一定屬於同一次會話) */ if (p->p_pptr == current || p->p_opptr == current) { err = -EPERM; /*如果不屬於同一次會話(同一次控制台),不可以*/ if (p->session != current->session) goto out; err = -EACCES; /*某個子進程調用 exec 函數之後, 就不能再將該子進程的 id 作為進程組 id*/ if (p->did_exec) goto out; } else if (p != current) goto out; err = -EPERM; /*boolean value for session group leader */ /*如果是一次會話的leader,也不可以 注意進程組的首領進程也是可以改變組id的*/ if (p->leader) goto out; /*好!幾個前提條件全滿足了,要做正事了: 但是是不是組號的合法性還沒有驗證?見後話!*/ /*要設進程號不是要設定的組號,如果是,直接設,因為這 意味著是增加了以自己的pid作為新的組號的進程組,這個 進程也將成為新進程組的首領進程,所以在此根本不用比較 會話號,自己對自己肯定是同一次會話.如果條件不滿足,則 要做這些判斷*/ if (pgid != pid) { struct task_struct * tmp; for_each_task (tmp) { /*能不能找到一個進程,組號正好是要設定的組號, 並且和要設定的進程屬於同一個控制台(同一個會話) 找到才可以設定,其實這裡就是要判定組號的合法性, 即必須是一個已經存在的組,而且和當前同一次會話才 可以操作,這個也不能忘記,其實就是說:同組的進程 一定屬於同一次會話*/ if (tmp->pgrp == pgid && tmp->session == current->session) goto ok_pgid; } goto out; } ok_pgid: p->pgrp = pgid; err = 0; out: /* All paths lead to here, thus we are safe. -DaveM */ read_unlock(&tasklist_lock); return err; } asmlinkage long sys_getsid(pid_t pid) { if (!pid) { return current->session; } else { int retval; struct task_struct *p; read_lock(&tasklist_lock); p = find_task_by_pid(pid); retval = -ESRCH; if(p) retval = p->session; read_unlock(&tasklist_lock); return retval; } } asmlinkage long sys_setsid(void) { struct task_struct * p; int err = -EPERM; read_lock(&tasklist_lock); for_each_task(p) { /*如果當前進程是一個進程組的首領進程, 則不能建立一個新的會話*/ if (p->pgrp == current->pid) goto out; } /*將新建立會話的leader設定為建立者就是當前進程*/ current->leader = 1; /*清楚看見一個新的進程組誕生了 當前進程成為新進程組的首領進程 新會話的id 是當前進程號,也是新會話的leader */ current->session = current->pgrp = current->pid; /*當前進程失去控制終端*/ current->tty = NULL; current->tty_old_pgrp = 0; err = current->pgrp; out: read_unlock(&tasklist_lock); return err; }
3. 關於進程,進程組,會話的一些理解
轉自:http://linux.chinaunix.net/bbs/viewthread.php?tid=766191
(1)進程必定屬於一個進程組,也只能屬於一個進程組。
一個進程組中可以包含多個進程。
進程組的生命週期從被建立開始,到其內所有進程終止或離開該組。
擷取當前進程所在進程組ID使用函數getpgrp
建立或加入其他組使用函數setpgid
(2)假設條件:pid1進程屬於pgid1進程組;pid2屬於pgid2進程組,並且是pgid2進程組組長;另有進程組pgid3,
在pid1進程中調用setpgid(pid2,pgid3);
a)當pid2和pgid3都>0且不相等時
功能:將pid2進程加入到pgid3組。此時pid2進程脫離pgid2進程組,進入pgid3進程組。
b)當pid2和pgid3都>0且相等時
功能:pid2進程建立新進程組,成為新進程組長(pgid3=pid2)。
c)當pid2==0,pgid>0時
功能:將調用進程pid1加入到pgid3中。此時pid1脫離pgid1,進入pgid3。
d)當pid2>0,pgid==0時
功能:將pid2加入到調用進程所在的pgid1進程組。此時pid2脫離pgid2,進入pgid1。
e)當pid2和pgid3都==0時,返回錯誤。
(3)一次登入就形成一次會話,交談群組長即建立會話的進程。
只有不是進程組長的進程才能建立新會話。
(4)如果pid1進程屬於pgid1進程組,且不是組長,屬於會話sid1。
在pid1進程中調用setsid();
功能:pid1進程脫離pgid1進程組,建立一個新的會話sid2(sid2沒有控制終端),pid1進程加入到pgid2組(pgid2==pid1)。