轉自:http://shake863.javaeye.com/blog/187085
看了《Unix高級環境編程》,不是很清楚這三個概念。看了這篇文章,感覺寫得不錯,就轉載了。
將闡述Linux內核中的如下幾個概念
1) 進程組
2) 會話
3) 控制終端
1.概念:
a)進程組
Shell 上的一條命令行形成一個進程組
每個進程屬於一個進程組
每個進程組有一個領頭進程
進程組的生命週期到組中最後一個進程終止, 或加入其他進程組爲止
getpgrp: 獲得進程組 id, 即領頭進程的 pid
setpgid: 加入進程組和建立新的進程組
前臺進程組和後臺進程組
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 內核通過維護會話和進程組而管理多用戶進程。如下圖所示,每個進程是一個進程組的成員,而每個進程組又是某個會話的成員。一般而言,當用戶在某個終端上登錄時,一個新的會話就開始了。進程組由組中的領頭進程標識,領頭進程的進程標識符就是進程組的組標識符。類似地,每個會話也對應有一個領頭進程。
同一會話中的進程通過該會話的領頭進程和一個終端相連,該終端作爲這個會話的控制終端。一個會話只能有一個控制終端,而一個控制終端只能控制一個會話。用戶通過控制終端,可以向該控制終端所控制的會話中的進程發送鍵盤信號。
同一會話中只能有一個前臺進程組,屬於前臺進程組的進程可從控制終端獲得輸入,而其他進程均是後臺進程,可能分屬於不同的後臺進程組。
Linux-會話-進程組關係
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;
}
關於進程,進程組,會話的一些理解
轉自: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)。