【Linux】守护进程

任务管理

进程组

进程组是一个或多个进程的集合。每个进程除了有一个进程ID之外,还属于一个进程组。

每个进程组有一个唯一的进程组ID。每个进程组都有一个组长进程。组长进程ID等于其进程组ID。

作业

SHELL终端通过前后台来控制作业和进程组。

一个前台作业可以由多个进程组成,一个后台作业也可以由多个进程组成,Shell可以运行一个前台作业和任意多个后台作业,这称为作业控制。

会话

会话是一个或者多个进程组的集合

  • 一个会话可以有一个控制终端。建立与控制终端连接的会话首进程被称为控制进程。

  • 一个会话中的几个进程组可被分为一个前台进程组以及一个或多个后台进程组。

  • 一个会话包括:一个控制进程,一个前台进程和多个后台进程。

创建四个死循环程序

image-20221203235748418

下面将job1和job2放在一个进程组,job3和job4放在一个进程组

image-20221204000416339

  • TTY:与进程关联的终端(tty)(完整的终端名称)
  • SID:会话组ID

这些进程组的控制终端相同,它们同属于一个会话。

任务管理操作

直接运行某一可执行程序,例如./可执行程序,此时默认将程序放到前台运行,在前台运行的进程的状态后有一个+号,例如s+

image-20221204001421344

运行可执行程序时在后面加上&,可以指定将程序放到后台运行,例如./可执行程序 &

image-20221204001551468

[1] 7358
[2] 7460

每创建一个后台进程组(作业),就会提升一条[ num ] NUM的信息;这里[ num ]是作业的编号。NUM是该作业中某个进程的进程ID。

相关操作

job命令

job命令,可以查看当前会话有那些工作

image-20221204004045827

fg命令

使用fg命令(foreground),可以将某个作业提至前台运行,如果该作业正在后台运行则直接提至前台运行,如果该作业处于停止状态,则给进程组的每个进程发SIGCONT信号使它继续运行并提至前台。

image-20221204004322628

job1和job2的状态从S变为了S+。

需要注意的是:前台进程只能有一个,当一个进程变成前台进程后,bash会自动变为后台进程,此时bash就无法进行命令行解释了。

image-20221204004652440

bash相关进程的+都没有了。

bg命令

使用bg命令,可以让某个停止的作业在后台继续运行(Running),本质就是给该作业的进程组的每个进程发SIGCONT信号。

image-20221204005329747

守护进程

守护进程也称精灵进程(Daemon),是运行在后台的一种特殊进程,它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。

Linux系统启动时会启动很多系统服务进程,这些系统服务进程没有控制终端,不能直接和用户交互。

其他进程都是在用户登录或运行程序时创建,在运行结束或用户注销时终止,但系统服务进程不受用户登录注销的影响,它们一直在运行着,这种进程有一个名称叫守护进程。

Linux系统中存在大量的守护进程,下面使用ps ajx命令查看。

image-20221204005744803

  • TPGID为终端的前台进程组。TPGID为-1的就是守护进程

创建守护进程

守护进程的创建步骤如下:

  1. 设置文件掩码为0。
  2. 忽略SIGCHLD信号。
  3. fork,终止父进程,保持子进程不是会话首进程,从而保证后续不会再和其他终端相关联。
  4. 将子进程设置为一个独立的会话
  5. 将标准输入、标准输出、标准错误重定向到/dev/null。

为什么需要将子进程设置为一个单独的会话,而不是父进程?

一般在一个多进程工作中,父进程即为进程组的组长。而组长管理整个进程组。

一个守护进程的父进程是init进程,需要单独设为一个会话,不与终端交互;一旦一个进程组的组长被设置为一个守护进程,那么原进程组就没有进程组组长。

#include <cstdio>
#include <iostream>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
void daemonize()
{
    umask(0);
    int fd = 0;
    // 1. 忽略SIGPIPE
    signal(SIGPIPE, SIG_IGN);
    signal(SIGCHLD,SIG_IGN);	
    // 2. 更改进程的工作目录
    // chdir();
    // 3. 让自己不要成为进程组组长
    if (fork() > 0){
        exit(1);
    }
    
    // 4. 设置自己是一个独立的会话
    setsid();
    // 5. 重定向0,1,2
    if ((fd = open("/dev/null", O_RDWR)) != -1) // fd == 3
    {
        dup2(fd, STDIN_FILENO);
        dup2(fd, STDOUT_FILENO);
        dup2(fd, STDERR_FILENO);
        // 6. 关闭掉不需要的fd
        if(fd > STDERR_FILENO) close(fd);
    }
}

关于/dev/null

/dev/null可以理解为Linux下的垃圾回收箱。我们可以通过把命令的输出重定向到 /dev/null 来丢弃脚本的全部输出。

守护进程不能直接和用户交互,所以可以把守护进程的输出都重定到/dev/null文件中。

daemon函数创建守护进程

int daemon(int nochdir, int noclose);

参数说明:

  • 如果参数nochdir为0,则将守护进程的工作目录该为根目录,否则不做处理。
  • 如果参数noclose为0,则将守护进程的标准输入、标准输出以及标准错误重定向到/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;
}

image-20221204013602018

使用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;
}

image-20221204014153066

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

影中人lx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值