Linux网络编程——守护进程

本章Gitee仓库:守护进程

1. 前台 & 后台进程

在Linux系统登陆的时候,它会给我们形成一次会话,会话在系统里面会创建一个bash进程,这个bash就会给用户提供命令行服务。

**在一个会话里面,只能存在一个前台进程,但可以有多个后台进程。**键盘信号只能发给前台进程。

GIF 2024-2-17 12-53-38

当我们直接运行程序的时候,系统就会自动把bash调到后台,然后把我们的程序调到前台,所以我们程序执行的时候,输入一些指令就没有翻译,当我们终止我们的进程的时候,此时没有前台进程,所以操作系统会将bash又重新调回前台。

如果加上&选项,就是让程序以后台进程的形式运行,此时输入一些指令就可以执行

GIF 2024-2-17 12-56-27

不管是前台还是后台进程,都可以向显示器输出内容,这就会影响到我们输入一些指令,所以我们可以采取重定向的方式,让后台进程将内容输出要一个文本里面。

所以显示器并不是区分前台和后台的一个重要指标,而是谁拥有标准输入(键盘文件),谁就是前台

启动一个后台进程时,会显示一个后台任务号

image-20240217130632303

这个后面跟的一串数字就是进程的pid

如果要查看所有的后台进程,采用指令jobs

image-20240217130733061

我们可以直接用kill命令杀掉这个后台进程,当然也可以用fg 后台任务号将这个后台进程调到前台来,然后采用ctrl + c终止

image-20240217130943019

如果又想将这个任务放回后台,ctrl + z

image-20240217131926596

当这个进程被暂停了,那么bash就又会被调到前台,但此时这个进程在后台是处于暂停状态的,采用指令bg 后台进程任务号,可重新将这个进程启动起来

image-20240217132202704

2. Linux进程之间的关系

这里单独创建了一个后台进程和连续创建了三个后台进程:

image-20240217133236068

这里单独创建的进程,自成一组;而用管道建立起的进程,它们几个是同一个组,组长是第一个创建的

image-20240217133742903

任务和进程组的关系:

任务是具体要完成的事情,而这个事情是有谁来完成,是可以指定的,可以一个进程独立完成,也可以一个进程组协同完成

所以这里可以稍微纠正一下前面的内容,一般叫做前台任务和后台任务

这里的会话ID和这些进程的父进程ID是一样的,这个就是ID就是bashpid

当关闭当前会话时,这些后台进程就会被系统回收或者是直接被杀掉,也就是说,这些后台进程是受当前会话影响的

Windows中,我们的注销,就可以理解为关闭当前会话,这里会杀掉我们打开的进程:

image-20240217140205810

如果想让进程不受会话影响,我们就需要将这个进程守护进程化

3. 守护进程

如果将一个进程(组)自称会话,也就是不需要和键盘显示器进行关联,这个就叫做守护进程

image-20240217140615269

#include <unistd.h>
pid_t setsid(void);

image-20240217152749720

这里要形成的新会话不能说这个进程组的组长,但如果这个进程组只有一个进程,那么就需要创建子进程了

所以守护进程的本质,也是孤儿进程

守护进程之后,按理说也不应该占用标准输入输出及错误,如果直接将标准输入输出错位关闭,这就需要对原来的代码进行改动。

在系统中存在一个/dev/null

image-20240217161831463

它就相当于一个垃圾桶一样,向这里面写的内容全部会自动丢弃

大部分情况下都是将日志写入文件而不是向显示屏输出,因为这样方便排查信息

守护进程代码Daemon.hpp

#include<iostream>
#include<unistd.h>
#include<signal.h>
#include<string>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

const std::string nullfile = "/dev/null";

void Daemon(const std::string &cwd = "")
{
    //忽略常见异常信号
    signal(SIGCLD, SIG_IGN);
    signal(SIGPIPE, SIG_IGN);
    signal(SIGSTOP, SIG_IGN);

    //核心 变成独立会话
    if(fork() > 0)  exit(0);
    setsid();
    
    //更改工作目录
    if(!cwd.empty())    chdir(cwd.c_str());

    //重定向标准输入 输出 错位 >> /dev/null
    int fd = open(nullfile.c_str(), O_RDWR);
    if(fd > 0)
    {
        dup2(fd, 0);
        dup2(fd, 1);
        dup2(fd, 2);
        close(fd);
    }
}

以之前写的翻译服务器为例,要想守护进程化,可以在main函数初始化之后加上这个功能:

int main(int argc, char *argv[])
{

    if(argc != 2)
    {
        Usage(argv[0]);
        exit(USAGE_ERR);
    }
    uint16_t port = std::stoi(argv[1]);
    std::unique_ptr<TcpServer> tcpSvr(new TcpServer(port));
    tcpSvr->Init();
    Daemon();
    tcpSvr->Start();
    return 0;
}

也可也直接在启动的时候守护进程:

void Start()
{
    Daemon();
    signal(SIGPIPE, SIG_IGN);
    threadPool<Task>::GetInstance()->Start();
    //signal(SIGCHLD, SIG_IGN);  //直接忽略进程等待 V2
    log(Info, "server is running...");
    while(true)
    {
        //...
    }

我们能远程登录linux,这是因为系统里面会默认起一个服务,我们每次登录就会发起一个会话:

image-20240217164044217

守护进程名字一般以d结尾

image-20240217164656439

服务端以日志形式记录:

image-20240217202318092

在系统当中有一个daemon函数,这样就不用我们自己写了(但一般情况下都是自己写):

image-20240217202431753

3号手册,是C的库函数

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

加法器+

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

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

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

打赏作者

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

抵扣说明:

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

余额充值