@bit::Shadow
✧(≖ ◡ ≖✿
目录
[jobs 的使用](#jobs 的使用)
[bg 的使用](#bg 的使用)
进程组
进程组是以一个/多个进程为单位的多/单进程载体。
进程组ID(PGID)若进程组ID相同代表它们是属于一个进程组。
像自定义协议服务中一个服务端,多个客户端就是一个进程组。
进程组的分类
每个会话内只有两种进程组类型:前台进程组、后台进程组。
前台进程组:可以接受键盘输入的进程组(任务)。(像shell登录时创建的含-bash进程的进程组)
【shell启动进程组查询图】
后台进程组:即在后台运行的进程组(通过ps ajx查询),不接受键盘指令。(运行方式:./a.out &)在运行指令后加上"&"符号。
sleep的后台运行举例
运行如下指令指在后台进程组运行
bash
sleep 1000 | sleep 2000 | sleep 3000 &

进程组VS任务
进程组是操作系统层面的概念而任务是用户层面的概念。
(本问题:具备混淆性,难以区分。)
组长进程
PGID与PID相同的进程为组长进程。(最多一个)
进程组的生命周期:子进程组创建开始到组内最后一个进程离开为止。
组长进程的作用:代表整个进程组接受信号(接收到后分发给组员)。获取控制终端,设置前台进程组:
{
tcsetpgrp(fd, pgid); // 将进程组设为前台
}
会话
会话、任务、进程组辨识
一个会话内可以可以有多个进程组。
会话也有自己的ID叫做"SID"。
图示关系

一个会话中可以有多个进程组,但只有一个前台进程组,可以有多个后台进程组。
常用指令
jobs 显示后台任务列表。
fg 任务号 将后台任务提到前台。
Ctrl + c 终止前台任务。(资源回收)
Ctrl + z 暂停 进程/进程组,自动切换到后台。
bg 任务号 让任务运行在后台。
jobs 的使用
bash
[ubuntu@localhost ~]$ jobs
[1] Running sleep 100 &
[2] Running tail -f server.log
[3]+ Stopped vim CMakeLists.txt
[4]- Running ./serverTCP 8080
[5] Done ls -l | grep txt
[6] Stopped nano .gitignore
bg 的使用
bash
# 1. 启动一个前台任务
$ sleep 300
# 2. 按 Ctrl+Z 暂停它
^Z
[1]+ Stopped sleep 300
# 3. 使用 bg 让它在后台继续运行
$ bg %1
[1]+ sleep 300 &
# 4. 现在这个任务在后台运行,你可以继续输入其他命令
$
守护进程(精灵进程)
解决目标:目前我们创建的程序++受用户登录/退出影响++,像启动的计算服务一旦xshell关闭客户端使用必定失效。为解决用户状态导致的服务端终止因此产生了精灵进程
定义:将需要不断运行的(不受用户登录、退出的影响的)进程(组)开辟至一新会话中,此会话就不会受用户的影响了。(在后台运行)
守护进程具有:"独立性" "孤儿性" 。
守护进程VS后台进程VS前台进程
后台、前台进程是隶属于一个会话的相对概念(的进程组)。
代码创建
为当前进程++创建一个新的会话++ ,并将当前进程PID设置为新会话ID(SID)。
cpp
#include<unistd.h>
pid_t setsid();
注意:
1. 调用setsid()的进程不能为组长进程 (否则报错)。(因此我们常采用夫父进程直接退出,子进程创建新会话的操作)
**2.****守护进程无控制终端。**如果存在在调用后会切断。
信号忽略
常设置信号忽略SIGCHLD与SIDPIPE。
设置 SIGCHLD 为 SIG_IGN,子进程会自动被回收,不会产生僵尸进程。
当进程向一个已关闭socket 写入数据时,内核 会发送SIGPIPE信号到写入进程导致服务端崩溃!!
工作路径、日志系统
工作路径:指进程组运行的根路径。(常切换(chdir)到根目录"/")
1.所有路径索引均基于工作路径!!!
2.此外还会影响编译时文件的链接。
日志系统基于工作路径的索引文件,将日志写入。(open dup2() close)
模拟实现守护进程
cpp
/*守护进程译作:daemon (dei men)*/
const char *workpath = "/";
void Daemon (int cdir, int noclose)
{
//解决IO问题
signal (SIGCHLD, SIG_IGN);
signal (SIGPIPE, SIG_IGN);
if (fork () > 0)
exit (0);
//子进程创建作为新会话组组长
pid_t ret = setsid ();
if(ret < 0)
RETFATAL ("setsid");
//工作路径是否切换?(即守护进程运行的路径)
if(cdir == 0)
chdir (workpath);
// 重定向标准输入、标准输出、标准错误到my.log文件(日志文件)
// 这样做的要求是守护进程特征(默认关闭终端)
if(noclose == 0)
{
int fd = open ("/my.log", O_RDWR);//读写
if(fd < 0)
RETFATAL ("open");
int ret1 = dup2 (fd, 0);
int ret2 = dup2 (fd, 1);
int ret3 = dup2 (fd, 2);
if(ret1 < 0 || ret2 < 0 || ret3 < 0)
RETFATAL ("dup2");
//打开的fd需要关闭吗?--->关闭后会影响写入吗?不会!!!
// int ret4 = close (fd);
// if(ret4 < 0)
// RETFATAL ("close");
}
}
极其容易混淆的点:int dup2(int oldfd, int newfd); 此处是将newfd的指向重定向到oldfd。(而非旧到新)
设备文件:/dev/null
文件黑洞,文件垃圾箱。写入的内容全部丢弃,读取的内容全部为空。

感谢支持,长期连载
欢迎关注
