將闡述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)
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;
}