进程组是一个或多个进程的集合。每个进程除了有一个进程ID之外,还属于一个进程组。
每个进程组有一个唯一的进程组ID。每个进程组都有一个组长进程。组长进程ID等于其进程组ID。
SHELL终端通过前后台来控制作业和进程组。
一个前台作业可以由多个进程组成,一个后台作业也可以由多个进程组成,Shell可以运行一个前台作业和任意多个后台作业,这称为作业控制。
会话是一个或者多个进程组的集合
一个会话可以有一个控制终端。建立与控制终端连接的会话首进程被称为控制进程。
一个会话中的几个进程组可被分为一个前台进程组以及一个或多个后台进程组。
一个会话包括:一个控制进程,一个前台进程和多个后台进程。
创建四个死循环程序
下面将job1和job2放在一个进程组,job3和job4放在一个进程组
这些进程组的控制终端相同,它们同属于一个会话。
直接运行某一可执行程序,例如./可执行程序
,此时默认将程序放到前台运行,在前台运行的进程的状态后有一个+
号,例如s+
。
运行可执行程序时在后面加上&,可以指定将程序放到后台运行,例如./可执行程序 &
[1] 7358
[2] 7460
每创建一个后台进程组(作业),就会提升一条[ num ] NUM的信息;这里[ num ]是作业的编号。NUM是该作业中某个进程的进程ID。
job命令
job命令,可以查看当前会话有那些工作
fg命令
使用fg
命令(foreground),可以将某个作业提至前台运行,如果该作业正在后台运行则直接提至前台运行,如果该作业处于停止状态,则给进程组的每个进程发SIGCONT信号使它继续运行并提至前台。
job1和job2的状态从S变为了S+。
需要注意的是:前台进程只能有一个,当一个进程变成前台进程后,bash会自动变为后台进程,此时bash就无法进行命令行解释了。
bash相关进程的+
都没有了。
bg命令
使用bg
命令,可以让某个停止的作业在后台继续运行(Running),本质就是给该作业的进程组的每个进程发SIGCONT信号。
守护进程也称精灵进程(Daemon),是运行在后台的一种特殊进程,它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。
Linux系统启动时会启动很多系统服务进程,这些系统服务进程没有控制终端,不能直接和用户交互。
其他进程都是在用户登录或运行程序时创建,在运行结束或用户注销时终止,但系统服务进程不受用户登录注销的影响,它们一直在运行着,这种进程有一个名称叫守护进程。
Linux系统中存在大量的守护进程,下面使用ps ajx命令查看。
守护进程的创建步骤如下:
为什么需要将子进程设置为一个单独的会话,而不是父进程?
一般在一个多进程工作中,父进程即为进程组的组长。而组长管理整个进程组。
一个守护进程的父进程是init进程,需要单独设为一个会话,不与终端交互;一旦一个进程组的组长被设置为一个守护进程,那么原进程组就没有进程组组长。
#include
#include
#include
#include
#include
#include
#include
void daemonize()
{umask(0);int fd = 0;// 1. 忽略SIGPIPEsignal(SIGPIPE, SIG_IGN);signal(SIGCHLD,SIG_IGN); // 2. 更改进程的工作目录// chdir();// 3. 让自己不要成为进程组组长if (fork() > 0){exit(1);}// 4. 设置自己是一个独立的会话setsid();// 5. 重定向0,1,2if ((fd = open("/dev/null", O_RDWR)) != -1) // fd == 3{dup2(fd, STDIN_FILENO);dup2(fd, STDOUT_FILENO);dup2(fd, STDERR_FILENO);// 6. 关闭掉不需要的fdif(fd > STDERR_FILENO) close(fd);}
}
关于/dev/null
/dev/null可以理解为Linux下的垃圾回收箱。我们可以通过把命令的输出重定向到 /dev/null 来丢弃脚本的全部输出。
守护进程不能直接和用户交互,所以可以把守护进程的输出都重定到/dev/null文件中。
int daemon(int nochdir, int noclose);
参数说明:
/dev/null
,否则不做处理。项目部署中很重要的一步就是将服务后台化,这里的后台化就是将进程**变成守护进程,**我们以一个TCP服务端为例
源码地址:
socket · 影中人/test - 码云 - 开源中国 (gitee.com)
int main(int argc, char *argv[])
{if (argc != 2 && argc != 3){Usage(argv[0]);exit(3);}uint16_t port = atoi(argv[1]);std::string ip;if (argc == 3){ip = argv[2];}//将进程守护进程化daemonize();//创建TCP服务器并运行Tcpserver svr(port, ip);svr.init();svr.start();return 0;
}
使用daemon守护进程化
int main(int argc, char *argv[])
{if (argc != 2 && argc != 3){Usage(argv[0]);exit(3);}uint16_t port = atoi(argv[1]);std::string ip;if (argc == 3){ip = argv[2];}//守护进程化daemon(0,0);//创建服务器和运行Tcpserver svr(port, ip);svr.init();svr.start();return 0;
}
上一篇:数据结构——链表
下一篇:【C语言航路】第六站:指针初阶