LV030-进程的关系
本文主要是进程——进程的关系与守护进程的相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。
一、进程的关系
1. 无关系
两个进程之间没有任何关系,相互独立。
2. 父子进程
两个进程间构成父子进程关系,例如一个进程创建出了另一个进程,那么这两个进程间就构成了父子进程关系,如果“生父”先于子进程结束,那么 init 进程(“养父”)就会成为子进程的父进程,它们之间同样也是父子进程关系。
3. 进程组
3.1 进程组概念
每个进程除了有一个进程 ID 、父进程 ID 之外,还有一个进程组 ID ,用于标识该进程属于哪一个进程组,进程组( Process Group )是一个或多个进程的集合,这些进程并不是孤立的,它们彼此之间或者存在父子、兄弟关系,或者在功能上有联系。
【注意】
(1)每个进程必定属于某一个进程组、且只能属于一个进程组。
(2)每一个进程组有一个组长进程,组长进程的 ID 就等于进程组 ID ,组长进程不能再创建新的进程组。
(3)在组长进程的 ID 前面加上一个负号即是操作进程组。
(4)只要进程组中还存在一个进程,则该进程组就存在,这与其组长进程是否终止无关。
(5)默认情况下,新创建的进程会继承父进程的进程组 ID 。
(6)一个进程组可以包含一个或多个进程,进程组的生命周期从被创建开始,到其内所有进程终止或离开该进程组。
3.2 进程组常用函数
3.2.1 getpgid()
3.2.1.1 函数说明
在 linux 下可以使用 man getpgid 命令查看该函数的帮助手册。
#include <sys/types.h>
#include <unistd.h>
pid_t getpgid(pid_t pid);【函数说明】该函数会获取指定进程所属的进程组 ID 。
【函数参数】
- pid : pid_t 类型,指定的进程 PID ,参数 pid 为 0 表示获取调用者进程的进程组 ID 。
【返回值】返回值为 pid_t 类型,成功则返回 pid 进程所属的进程组 ID ,失败则返回 -1 ,并设置 errno 。
【注意事项】 none
3.2.1.2 使用实例
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
printf("This process's gid: %d\n", getpgid(0));
return 0;
}在终端执行以下命令:
gcc test.c -Wall # 生成可执行文件 a.out
./a.out # 执行程序程序执行后,终端将会显示以下信息:
This process's gid: 7986每次运行获得的 ID 可能会不同。
3.2.2 getpgrp()
3.2.2.1 函数说明
在 linux 下可以使用 man getpgrp 命令查看该函数的帮助手册。
#include <sys/types.h>
#include <unistd.h>
pid_t getpgrp(void);【函数说明】该函数会获取指定进程所属的进程组 ID 。
【函数参数】 none
【返回值】返回值为 pid_t 类型,成功则返回当前进程所属的进程组 ID ,失败则返回 -1 ,并设置 errno 。
【注意事项】 getpgrp() 就等价于 getpgid(0) 。
3.2.2.2 使用实例
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
pid_t pid = getpid();
printf("Process group ID<%d>---getpgrp()\n", getpgrp());
printf("Process group ID<%d>---getpgid(0)\n", getpgid(0));
printf("Process group ID<%d>---getpgid(%d)\n", getpgid(pid), pid);
return 0;
}在终端执行以下命令:
gcc test.c -Wall # 生成可执行文件 a.out
./a.out # 执行程序程序执行后,终端将会显示以下信息:
Process group ID<8021>---getpgrp()
Process group ID<8021>---getpgid(0)
Process group ID<8021>---getpgid(8021)每次运行获得的 ID 可能会不同。
3.2.3 setpgid()
3.2.3.1 函数说明
在 linux 下可以使用 man setpgid 命令查看该函数的帮助手册。
#include <sys/types.h>
#include <unistd.h>
int setpgid(pid_t pid, pid_t pgid);【函数说明】该函数将 pid 进程的进程组 ID 设置成 pgid 。
【函数参数】
- pid : pid_t 类型,需要修改进程组的进程的 PID 。
- pgid : pid_t 类型,要设置目标的进程组 ID 。
【返回值】返回值为 int 类型,成功则返回目标进程组 ID ,失败则返回 -1 ,并设置 errno 。
【注意事项】
(1)如果函数两个参数相等( pid == gpid ),则由 pid 指定的进程变成为进程组的组长进程,创建了一个新的进程;如果参数pid等于0,则使用调用者的进程ID;另外,如果参数gpid等于0,则创建一个新的进程组,由参数pid指定的进程作为进程组组长进程。
3.2.3.2 使用实例
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
pid_t pid = getpid();
printf("Process group ID<%d>---getpgrp()\n", getpgrp());
printf("Process group ID<%d>---getpgid(0)\n", getpgid(0));
printf("Process group ID<%d>---getpgid(%d)\n", getpgid(pid), pid);
return 0;
}在终端执行以下命令:
gcc test.c -Wall # 生成可执行文件 a.out
./a.out # 执行程序程序执行后,终端将会显示以下信息:
Process group ID<8021>---getpgrp()
Process group ID<8021>---getpgid(0)
Process group ID<8021>---getpgid(8021)每次运行获得的 ID 可能会不同。
4. 会话
4.1 相关概念
会话( Session ):是一个或多个进程组的集合,其与进程组、进程之间的关系如下图所示:

控制终端( Controlling Terminal ):每个会话可以有一个单独的控制终端,与控制终端连接的 Leader 就是控制进程( Controlling Process )。
一个会话可包含一个或多个进程组,但只能有一个前台进程组,其它的是后台进程组;每个会话都有一个会话首领( leader ),即创建会话的进程。一个会话可以有控制终端、也可没有控制终端,在有控制终端的情况下也只能连接一个控制终端,这通常是登录到其上的终端设备(在终端登录情况下)或伪终端设备(例如通过 SSH 协议网络登录),一个会话中的进程组可被分为一个前台进程组以及一个或多个后台进程组。
会话的首领进程连接一个终端之后,该终端就成为会话的控制终端,与控制终端建立连接的会话首领进程被称为控制进程;产生在终端上的输入和信号将发送给会话的前台进程组中的所有进程,例如 Ctrl + C (产生 SIGINT 信号)、 Ctrl + Z (产生 SIGTSTP 信号)、 Ctrl + \ (产生 SIGQUIT 信号)等等这些由控制终端产生的信号。
当用户在某个终端登录时,一个新的会话就开始了;当我们在 Linux 系统下打开了多个终端窗口时,实际上就是创建了多个终端会话。一个进程组由组长进程的 ID 标识,而对于会话来说,会话的首领进程的进程组 ID 将作为该会话的标识,也就是会话 ID ( sid ),在默认情况下,新创建的进程会继承父进程的会话 ID 。
4.2 常用函数
4.2.1 getsid()
4.2.1.1 函数说明
在 linux 下可以使用 man getsid 命令查看该函数的帮助手册。
#include <sys/types.h>
#include <unistd.h>
pid_t getsid(pid_t pid);【函数说明】通过系统调用该函数获取进程的会话 ID 。
【函数参数】
- pid : pid_t 类型,如果参数 pid 为 0 ,则返回调用者进程的会话 ID ;如果参数 pid 不为 0 ,则返回参数 pid 指定的进程对应的会话 ID 。
【返回值】返回值为 pid_t 类型,成功返回会话 ID ,失败则返回 -1 、并设置 errno 。
【使用格式】一般情况下基本使用格式如下:
/* 需要包含的头文件 */
#include <sys/types.h>
#include <unistd.h>
/* 至少应该有的语句 */
getsid(pid);【注意事项】 none
4.2.1.2 使用实例
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
printf("Session ID<%d>\n", getsid(0));
return 0;
}在终端执行以下命令:
gcc test.c -Wall # 生成可执行文件 a.out
./a.out # 执行程序程序执行后,终端将会显示以下信息:
Session ID<11992>4.2.2 setsid()
4.2.2.1 函数说明
在 linux 下可以使用 man 3 setsid 命令查看该函数的帮助手册。
#include <unistd.h>
pid_t setsid(void);【函数说明】创建一个新的会话。
【函数参数】 none
【返回值】返回值为 pid_t 类型,成功将返回新会话的会话 ID ;失败将返回 -1 ,并设置 errno 。
【使用格式】一般情况下基本使用格式如下:
/* 需要包含的头文件 */
#include <unistd.h>
/* 至少应该有的语句 */
setsid();【注意事项】如果调用者进程不是进程组的组长进程,调用 setsid() 将创建一个新的会话,调用者进程是新会话的首领进程,同样也是一个新的进程组的组长进程,调用 setsid() 创建的会话将没有控制终端。也就是说,这个函数有以下作用:
(1)让进程摆脱原会话的控制
(2)让进程摆脱原进程组的控制
(3)让进程摆脱原控制终端的控制
4.2.2.2 使用实例
主要是在创建守护进程的时候使用,例子看下边守护进程一节的例子就可以啦。