Linux进程组
每个进程都有一个进程组号(PGID)
-
进程组:一个或多个进程的集合(集合中的进程并不孤立)
-
进程组中的进程通常存在父子关系,兄弟关系或功能相近
进程组可方便进程管理(如:同时杀死多个进程,发送一个信号给多个进程)
-
每个进程必定属于一个进程组,也只能属于一个进程组
-
进程除了有PID外,还有PGID(唯一,但可变)
-
每个进程组有一个进程组长,进程组长的PID和PGID相同
- pid_t getpgid(pid_t pid); //获取指定进程的组标识
- int setpgid(pid_t pid, pid_t pgid); //设置进程的组标识
- pid == pgid,将pid指定的进程设为组长
- pid == 0 设置当前进程的组标识
- pgid == 0 则将pid作为组标识
获取调用进程进程组id(getpgrp)
每个进程组都有唯一的进程组ID(整数,也可以存放在pid_t类型中)。进程组由进程组ID来唯一标识。除了进程号(PID)之外,进程组ID也是一个进程的必备属性之一。
getpgrp: 获得进程组 id, 即领头进程的 pid
#include <unistd.h>
pid_t getpgrp(void);
//返回值;调用进程的进程组ID
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
int main(void)
{
int pid = 0;
int i = 0;
printf("parent = %d, ppid = %d, pgid = %d\n", getpid(), getppid(), getpgrp());
while( i < 5 )
{
if( (pid = fork()) > 0 )
{
printf("new: %d\n", pid);
}
else if( pid == 0 )
{
sleep(1);
printf("child = %d, ppid = %d, pgid = %d\n", getpid(), getppid(), getpgrp());
sleep(60);
printf("last -- pgid = %d\n", getpgrp());
break;
}
else
{
printf("fork error...\n");
}
i++;
}
if( pid )
{
sleep(60);
}
return 0;
}
深入理解进程组
-
进程组长终止,进程组依然存在(进程组长仅用于创建新进程组)
-
父进程创建子进程后立即通过setpgid()改变其组标识(PGID)
-
同时,子进程能需要通过setpgid() 改变自身组标识(PGID)
-
子进程调用exec()
-
父进程无法通过setpid()改变其组标识(PGID)
-
只能自身通过setpgid()改变其组标识()
-
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
int main(void)
{
int pid = 0;
printf("parrent = %d, ppid = %d, pgid = %d\n", getpid(), getppid(), getpgrp());
if( (pid = fork()) > 0 )
{
int r = setpgid(pid, pid);
printf("new: %d, r = %d\n", pid, r);
}
else if( pid == 0 )
{
setpgid(pid, pid);
sleep(1);
printf("child = %d, ppid = %d, pgid = %d\n", getpid(), getppid(), getpgrp());
}
else
{
printf("fork error...\n");
}
sleep(60);
return 0;
}
如果pid是0,则使用调用者的进程ID。
另外,如果pgid是0,则由pid指定的进程ID被用作为进程组ID。(如setpgid(0, 0),将调用进程设置为进程组组长,进程组id为调用进程的进程号)
setpgid(0, 0),将调用进程设置为进程组组长,进程组id为调用进程的进程号
子进程调用exec代码示例:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main(void)
{
sleep(5);
printf("child = %d, ppid = %d, pgid = %d\n", getpid(), getppid(), getpgrp());
printf("hello world\n");
sleep(30);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
int main(void)
{
int pid = 0;
printf("parrent = %d, ppid = %d, pgid = %d\n", getpid(), getppid(), getpgrp());
if( (pid = fork()) > 0 )
{
int r = 0;
sleep(1);
r = setpgid(pid, pid);
printf("new: %d, r = %d\n", pid, r);
}
else if( pid == 0 )
{
char* out = "./helloworld.out";
char* const ps_argv[] = {out, NULL};
char* const ps_envp[] = {"PATH=/bin:/usr/bin",NULL};
execve(out, ps_argv, ps_envp);
// setpgid(pid, pid);
// sleep(1);
// printf("child = %d, ppid = %d, pgid = %d\n", getpid(), getppid(), getpgrp());
}
else
{
printf("fork error...\n");
}
sleep(60);
return 0;
}
一个进程只能为它自己或它的子进程设置进程组ID
。在它的子进程调用了exec后,它就不再能改变该子进程的进程组ID。
Linux会话(session)
-
用户通过终端登录系统后会产生一个会话
-
会话是一个或多个进程组的集合
-
每个会话有一个会话标识(SID)
-
终端登录后的第一个进程成为会话首进程,通常是一个shell/bash
-
对于会话首进程(session leader),其PID与SID相等
-
-
通常情况下,会话与一个终端(控制终端)相关联用于执行输入输出操作
-
会话首进程建立与控制终端的连接(会话首进程又叫控制进程)
-
会话中的进程组可分为
-
前台进程组:可接收控制终端中的输入,也可输出数据到控制终端
-
后台进程组:所有进程后台运行,无法接受终端的输入,但可输出数据到终端
-
-
每个会话最多只有1个前台进程组,可以有多个后台进程组
问题:在终端中输入命令后,发生了什么?
-
当命令行(shell)运行命令后创建一个新的进程组
-
如果运行的命令中有多个子命令则创建多个进程(处于新建的进程组中)
-
命令不带&
-
shell 将新建的进程组设置为前台进程组,并将自己暂时设置为后台进程组
-
-
命令带&
-
shell 将新建的进程组设置为后台进程组,自己依旧是前台进程组
-
什么是终端进程组标识(TPGID) ?
-
标识进程是否处于一个和终端相关的进程组中
-
前台进程组:TPGID == PGID
-
后台进程组:TPGID != PGID
-
若进程和任何终端无关:TPGID == -1
Linux 会话接口
- #include <unistd.h>
- pid_t getsid(pid_t pid) //获取指定进程的SID,(pid == 0)->当前进程
- pid_t setsid(void) //调用进程不能是进程组长
- 创建新会话, SID == PID,调用进程成为会话首进程
- 创建新进程组,PGID == PID,调用进程成为进程组长
- 调用进程没有控制终端,若调用前关联了控制终端,调用后与控制终端断链
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
int main(void)
{
int pid = 0;
int i = 0;
if( (pid = fork()) > 0 )
{
printf("parrent = %d, ppid = %d, pgid = %d, sid = %d\n", getpid(), getppid(), getpgrp(), getsid(getpid()));
printf("new: %d\n", pid);
}
else if( pid == 0 )
{
setsid();
sleep(3);
printf("child = %d, ppid = %d, pgid = %d, sid = %d\n", getpid(), getppid(), getpgrp(), getsid(getpid()));
}
else
{
printf("fork error...\n");
}
sleep(120);
return 0;
}