每个进程都属于一个进程组。
进程组是一个或多个进程集合。通常,它们与同一个作业相关联,可以接收来自同一终端的信号。
如在shell命令行键入一个管道命令:
cat main.c | grep for | more
其中的每个命令: cat、grep和more都属于同一个进程组。
与进程相关函数有:
#include <unistd.h>
pid_t getpgrp(void); //返回调用进程所属进程组
pit_t getpgid(pid_t pid); //成功返回pid所属进程组,出错返回-1
int setpgid(pid_t pid, pid_t pgid); //成功返回0, 出错返回-1, 设置pid进程的进程组为pgid
在getpgid()函数中,参数pid若为0,则返回调用进程的进程组ID。
getpgid(0) == getpgrp()
在每个进程组中都可以有一个组长进程。组长进程ID = 进程组ID。
只要进程组中有一个进程存在,该进程组就存在,与组长进程是否终止无关。
进程组创建到最后一个进程离开的时间区间称进程组的生存期。进程组的最后一个进程可以终止或转移到其它进程组。
在setpgid()函数中,它设置pid进程的进程组ID为pgid。如果两个参数相等,则由pid指定的进程变成进程组长。如果pid为0,则使用调用进程的ID。
一个进程只能为它或它的子进程设置进程组ID。但在子进程调用了exec函数族后,它不能再改变该子进程的进程组ID。
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int main(void)
{
pid_t pid1, pid2;
if(fork() == 0) //child
{
printf("child :pid = %d\t pgid = %d\n",getpid(), getpgid(0)); //print child pid and child group pid
setpgid(0, 0); //set child pgid = child pid
printf("child :pid = %d\t pgid = %d\n",getpid(), getpgid(0));
exit(0);
}
printf("parent:pid = %d\t pgid = %d\n",getpid(), getpgid(0));
exit(0);
}
output:
parent:pid = 3434 pgid = 3434
child :pid = 3435 pgid = 3434
child :pid = 3435 pgid = 3435
上面父进程创建子进程后它们属于同一进程组,组长进程为父进程,然后子进程设置自己为组长进程。
会话(session):
会话是一个或多个进程组的集合。
通常情况下,用户登录后所执行的所有程序都属于一个会话,而其登录shell则是会话首进程。当我们退出登录时,所有属于这个会话期的进程都将被终止。这也是会话的主要用途之一。
#include <unistd.h>
pid_t getsid(pid_t pid); //成功返回会话首进程的进程组ID(会话ID),失败返回-1.
pid_t setsid(void); //成功返回进程组ID, 失败返回-1
getsid()函数中如果pid = 0,表示pid为调用进程ID。
进程调用setsid()函数建立一个会话。
如果调用次函数的进程不是进程组的组长,则创建一个新会话,然后:
1. 该进程变成新会话首进程。
2. 该进程成为一个新进程组的组长进程。
3. 该进程没有终端控制。调用setsid前该进程拥有的终端控制联系也会被切断。
但是,如果是一个组长进程调用的setsid函数,则该函数出错返回。为保证该情况不会发生,通常先调用fork,然后使父进程终止,子进程再调用setsid函数创建会话。
会话首进程ID也称会话ID。