一、进程组
1、理解
2、举例
上图就是用管道创建了一个进程组。
3、结论
二、会话
1、理解
2、Linux会话结构
首先我们登录Linux之后,先有 bash 进程创建终端文件,此时 bash 不仅是进程组组长,bash 进程组还是会话组长,我们大部分的工作都是在 bash 创建的会话里面完成的。建立与控制终端连接的会话首进程是控制进程 bash。
3、结论
(1)在同一个会话中允许存在多个进程组。
(2)但是前台进程只有一个,后台进程可以有多个
例如我在前台启动一个进程组,输入指令 bash 由于在后台,就不会处理指令。
(3)前后台:前台可以从标准输入中获取数据。进程在启动时最后带上&就是后台进程
(4)无论何时,发送的中断信号只能给前台进程。
三、控制终端
四、作业与作业控制
1、理解
2、作业控制操作
jobs:查看作业。-p查看作业pid
fg 作业号:把一个指定作业提到前台(接受用户输入的数据)
把作业放回后台:(1)作业暂停(ctrl z,也叫挂起)之后OS就会把作业放回后台。(2)bg 作业号:在后台启动作业
3、作业状态
4、举例
五、守护进程
1、理解
一个服务进程独立成为一个会话,就不会受原来会话的影响。
2、把进程变成守护进程
不是进程组组长调用函数 setsid 就可以把自己变成守护进程。调用的进程就会变成新会话的首进程,此时会话中只有一个进程,也是一个进程组的组长。
做法:父进程先 fork 子进程,父进程退出,子进程调用函数,由于子进程继承父进程的进程组id,进程id被重新分配,所以守护进程是孤儿进程。
这种进程已经不属于 bash 会话,只能用 kill 杀死进程。
3、代码实现
#pragma once
#include <iostream>
#include <cstdlib>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
const char *root = "/";
// 路径/dev/null 文件里面的数据不能被读取,相当于是被丢弃
const char *dev_null = "/dev/null";
void Daemon(bool ischdir, bool isclose)
{
// 1. 忽略可能引起程序异常退出的信号
signal(SIGCHLD, SIG_IGN);
signal(SIGPIPE, SIG_IGN);
// 2. 让自己不要成为组长,父进程退出
if (fork() > 0)
exit(0);
// 3. 设置让自己成为一个新的会话, 后面的代码其实是子进程在走
setsid();
// 4. 每一个进程都有自己的 CWD,是否将当前进程的 CWD 更改成为 / 根目录
if (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);
}
}
}