文章目录
- 1、进程间关系
- 1.1 进程组
- 1.2 组长进程
- 2、会话?
- 2.1 查看会话
- 2.2 创建会话
- 3、控制终端
- 4、作业控制
- 4.1 前台/后台进程
- 5、守护进程
- 5.1 如何创建守护进程?
- 5.2 杀掉守护进程
1、进程间关系
主要描述两个名称概念:即进程组和组长进程。
1.1 进程组
什么是进程组?通过名称可以看到它包含很多进程,所有进程在一个组内,当然名称虽然是进程组,其实在Linux中,单个进程也是有自己的组。首先,通过看一个进程在Linux中是如何描述的?
PPID:
表示该运行的进程所属的父进程ID信息,即该进程的父亲。
PID:
表示该运行进程的唯一标识符。
PGID:
表示改进程所属进程组的ID,(由于是单进程因此进程组ID就是自己的PID,也就是为什么说进程组中可以有一个进程。)
SID:
表示会话ID信息
TTY:
表示该进程所属于哪一个终端,(显示的是终端号,本质就是在/dev/pts下的一个文件)
UUID:
表示是哪一个用户执行的该进程,即用户名,只不过Linux中是用数字描述的。
我们看到描述一个进程的信息有PPID、PID、PGID等,接下来要讲述的就是PGID?
首先来看一个指令,该指令是由3个相同的指令组合,用管道连接的 ,我们知道在Linux中一条运行的指令就是一个进程。
从图中可以看到启动了3个进程,他们同属于一个组,因为PGID都是393911
,仔细观察看到该进程组ID就是第一个运行起来的进程PID。
总结就是:当多个进程在运行时候,他们有各自的PPID、PID,但是PGID是相同的,因为是一个进程组,并且规定进程组ID就是第一进程执行时的PID。
上面看到的是由多个进程组成的进程组,所描述的信息就是上面所展示的,那么只有一个进程运行的时候又是如何描述的?如下显示的:
上面就是单个进程运行时,他的进程组就是自己的PID,也符合:第一个进程运行的PID作为PGID。
看到多个进程和单个进程的进程组描述如上,那么当我们在一个进程中又创建了子进程,然后让父进程直接退出,此时进程又是如何描述的?如下:
可以看到当一开运行进程的时候,父进程在程序中设置的是延时5秒,此时进程有自己的PPID、PID、PGID,由于是单进程组成的进程组,所以是自己的PID,当5秒结束后,程序中紧接着创建子进程,并且让父进程直接退出,此时该进程就变为了孤儿进程,被1号进程领养,但所属的进程组ID是不变的,还是第一个进程运行的PID,只是PID变为了子进程的PID了。
总结:在多进程中实际上以进程组的关系执行的,进程组就是一个或多个进程的集合,一个进程组可以有多个进程,也可以只有一个进程。PGID就是第一运行进程的PID,同时只有当最后一个进程退了,该组才全部退出,与组长进程是否退出没有关系。
1.2 组长进程
通过上面的描述,显而易见组长进程就是在进程组中第一个运行的进程,对应的PGID就是该进程的PID。
查看对应列属性的数据指令:ps -eo 对应进程列属性
2、会话?
会话和上面的进程组有关系,即在一个会话中可以包含一个或者多个进程组,因此一个或多个进程组集合就是一个会话。
2.1 查看会话
通过上面的图可以看出,每创建一个终端登录界面,登录后就会有对应的SID,因此会话就可以理解为从用户登录该终端开始到该终端退出关闭,在该过程中所操作的所有事。通过SID,即会话ID来区分不同会话区。
2.2 创建会话
在看到会话所表示的区域后,我们自己如何创建新的会话呢?
即通过setseid
函数创建会话。该函数使用前提就是不能让组长进程来创建会话。
#include <unistd.h>
/*
*功能:创建会话
*返回值:创建成功返回 SID, 失败返回-1 */
pid_t setsid(void);
为了不让创建的进程是组长进程,通常是在进程中创建子进程来创建会话。
3、控制终端
简单来说,控制终端就是用户在通过终端登录系统过后得到的一个shell进程,登陆后所在的终端就是该shell的控制终端。控制终端保存在PCB中的,因此在shell中启动的所有进程都属于该登录的终端。 每个进程的标准输入、输出、错误都会指向该控制终端。在理解控制终端后,它还和会话、进程组有这些关系:
①一个会话拥有一个控制终端,通常打开的首个会话进程打开终端后,该终端就是该会话的控制终端。
②和控制终端建立的会话的首进程叫控制进程。
③在一个会话中的多个进程组中可以被分为一个前台进程组和一个或多个后台进程组。控制终端的指令只能发送给前台进程组。
4、作业控制
什么是作业?每个学生都知道作业这件事,但这里的作业是指用户为了完成某件事而启动的进程。一个作业可以包含一个进程或者是进程组,进程之间相互协调。
作业控制?
谁控制?在Shell中分前后台来控制,控制的就是作业或者是进程组。一个前台作业可以是多个进程组,后台也可以是多个。Shell可以同时运行一个前台作业或多个作业前台作业,即为作业控制。
4.1 前台/后台进程
我们用户输入执行程序指令时大多数都是前台运行的,前台运行有个特点就是只有等前台运行结束后用户再输入指令才有用,才会被控制终端接收。
./exe:
该方式运行的就是前台进程。
变为后台进程方式:./exe &
在同一个会话中,可以允许多个后台进程(组),但前台进程(组)只允许一个。
前后台进程的标志:看谁应该从标准输入中获取数据。明确的是只有前台进程才可以获取。
把后台变为前台:fg 进程名
,也可以通过作业号进行移动。
前台变为后台:先停止前台进程(ctrl+z),在bg 进程名
5、守护进程
守护进程不受控制终端关闭的影响,而是长时间运行的。我们就是把在会话的中的进程组拿出来放在单独的放到独立的会话中,此时该进程组就是守护进程。
那么如何创建会话,前面说了通过setsid()
函数,注意不能是组长进程。
既然不能是组长进程,我们在写程序的时候,主函数中就是父进程,第一个先运行的,即为组长进程,因此不能在主函数中创建会话,需要在子进程中创建会话。让父进程直接退出。此时就会想到孤儿进程,因此守护进程就是孤儿进程的一种特殊。
5.1 如何创建守护进程?
步骤:
①忽略信号。
②创建子进程,在其中创建会话。
③更改工作目录。
④重定向标准输入输出到/dev/null。
#pragma once#include<iostream>
#include<string>
#include <iostream>
#include <cstdlib>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>//更改工作目录
const char *root="/";//根目录
const char *dev_null="/dev/null";void Demo(bool isChdir,bool isClose)
{//1、忽略可能引起程序错误的信号signal(SIGCHLD,SIG_IGN);signal(SIGPIPE,SIG_IGN);//2、创建子进程if(fork()>0){exit(0);//父进程退出}//3、子进程中创建会话setsid();//4、查看是否更改CWDif(isChdir){chdir(root);//更改为根目录}//5、此时已经是守护进程了,不需要和用户的标准输入、输出、错误关联if(isClose){close(0);close(1);close(2);}else{//也可以重定向int fd=open(dev_null,O_RDWR);if(fd>0){//后者重定向到前者dup2(fd,0);dup2(fd,1);dup2(fd,2);close(fd);}}
}
通过调用编写的函数,把进程变为守护进程
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <unistd.h>
#include "Demo.hpp"
int main()
{// sleep(5);// if(fork()>0)// {// exit(1);// }// while(1)// {// std::cout<<"child PPID:"<<getppid()<<" PID:"<<getpid()<<std::endl;// sleep(1);// }//Demo(false, false);while (1){std::cout << "hello" << std::endl;sleep(1);}return 0;
}
5.2 杀掉守护进程
kill -9 PID