【linux】守护进程(精灵进程)

这篇具有很好参考价值的文章主要介绍了【linux】守护进程(精灵进程)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、TCP服务器日志

上一章我们写了一个TCP网络服务器【网络编程】demo版TCP网络服务器实现。
为了方便观察到每一步我们可以封装一个日志:

#pragma once

#include <iostream>
#include <string>
#include <cstdarg>
#include <time.h>
#include <ctime>
#include <unistd.h>
#include <cstdio>

#define DEBUG   0// 调试
#define NORMAL  1// 正常
#define WARNING 2// 警告
#define ERROR   3// 错误
#define FATAL   4// 致命错误

const char* to_string_level(int level)
{
    switch(level)
    {
        case DEBUG: return "DEBUG";
        case NORMAL: return "NORMAL";
        case WARNING: return "WARNING";
        case ERROR: return "ERROR";
        case FATAL: return "FATAL";
        default : return nullptr;
    }
}

void logMessage(int level, const char* format, ...)
{
    // [日志等级][时间][pid][信息]
    char logprefix[1024];
    time_t now;
    time(&now);
    struct tm *ptm = localtime(&now);
    char timebuf[1024];
    snprintf(timebuf, sizeof timebuf, "%d年%d月%d日 %d:%d:%d", ptm->tm_year + 1900, ptm->tm_mon + 1, ptm->tm_mday, ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
    snprintf(logprefix, sizeof logprefix, "[%s][%s][pid: %d]", to_string_level(level), timebuf, getpid());
    char logline[1024];
    va_list arg;
    va_start(arg, format);
    vsnprintf(logline, sizeof logline, format, arg);
    std::cout << logprefix << logline << std::endl;
}

运行结果:
【linux】守护进程(精灵进程)

二、守护进程预备知识

2.1 守护进程概念

但是我们发现这个服务器存在问题,但是我们如果把云服务器关掉的话服务器就自动退出了

服务器肯定不是这样运行的,服务器应该是启动后不再受用户的登录和注销的影响。除非我们不想用这个服务器了,把它kill掉。
我们把这种进程就叫做守护进程

2.2 前台任务和后台任务

当Xhell连接云服务器时,Linux服务器会提供一个bash(命令行解释),我们可以启动一系列前台和后台任务。我们把所有的前台后台任务叫做会话。
【linux】守护进程(精灵进程)
这里的bash就是前台任务,一个会话允许存在一个前台任务和多个(或0个)后台任务。

在命令的后边加上&符号就可以把这个进程运行在后台。
【linux】守护进程(精灵进程)
【linux】守护进程(精灵进程)
jobs命令用于显示Linux中的任务列表及任务状态,包括后台运行的任务。
【linux】守护进程(精灵进程)

我们把红色框框的部分称作作业,最左边的序号就是作业号

2.3 进程组与组长ID

创建一批后台进程:
【linux】守护进程(精灵进程)
可以看到PGID相同的就属于同一个进程组。而PID和PGID相同的就是组长进程

同一个组的成员共同完成一个作业。

这里的SID就是会话ID。所有的进程属于同一个会话。其实就是bash的ID。

2.4 前台进程后台进程的切换

使用fg + 作业号就可以把一个作业放在前台。如果再[Ctrl] + z暂停就会又回到后台
bg 命令用于将作业放到后台运行,使前台可以执行其他任务。该命令的运行效果与在运行命令后面添加符号 & 的效果是相同的,都是将其放到系统后台执行。

使用这些命令就可以让进程进行前后台切换。

综上可得知前台只能允许一个进程运行,当我们把一个作业切换到前台,此时的前台进程就会被自动切换到后台。

而如果我们关闭Xshell,全部的进程都会被清理掉(会受到用户登录和注销的影响)。
如果不想受到影响就不能放到前台或者后台,而是要自成会话。

2.5 自成会话

【linux】守护进程(精灵进程)
当一个进程自成会话了,就不会受到终端设备(Xshell)的影响了。

三、实现守护进程

3.1 自建会话setsid

#include <unistd.h>

pid_t setsid(void);

RETURN VALUE
Upon  successful  completion,  setsid()  shall  return  the value of the new process group ID of the calling process.  
Otherwise, it shall return (pid_t)-1 and set errno to indicate the error.

这个函数的作用就是新建一个会话,如果只有一个进程,那么组长就是自己。
这里有一个要求,调用该函数的进程不能是组长

3.2 守护进程的条件

1️⃣ 要让进程忽略掉异常信号,因为客户端关闭的时候我们正在写就会发送异常导致服务端退出。
2️⃣ 必须让进程不是组长。
3️⃣ 守护进程必须脱离终端,所以要关闭或者重定向以前进程默认打开的文件(0, 1, 2)。

这里不建议直接关闭文件描述符,因为有可能会导致写入不存在的文件(显示器)导致报错。我们可以重定向到/dev/null

如果希望执行某个命令,但又不希望在屏幕上显示出输出的结果,那么可以将输出重定向到/dev/null
/dev/null是一个特殊的文件,写入到它的内容都会被丢弃;如果尝试从该文件读取内容,那么什么也读取不到。
但是/dev/null文件非常的有用,将命令的输出重定向到它,会起到“静止输出”的效果。

3.3 代码实现

#pragma once

#include <unistd.h>
#include <signal.h>
#include <cassert>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "daemon.hpp"

void Daemon()
{
    signal(SIGPIPE, SIG_IGN);
    // 保证不是组长
    if(fork() > 0) exit(1);
    // 子进程
    pid_t n = setsid();
    assert(n != -1);
    int fd = open("/dev/null", O_RDWR);
    if(fd < 0)
    {
        // 无奈之举
        close(0);
        close(1);
        close(2);
    }
    else
    {
        dup2(fd, 0);
        dup2(fd, 1);
        dup2(fd, 2);
    }
}

// TCPServeer.cc
#include "TCPServer.hpp"
#include <memory>
#include "daemon.hpp"

int main(int argc, char *argv[])
{
    if(argc != 2)
    {
        std::cout << "incorrect number of parameters" << std::endl;
        exit(1);
    }
    uint16_t port = atoi(argv[1]);
    std::unique_ptr<TCPServer> ptr(new TCPServer(port));
    ptr->InitServer();
    Daemon();
    ptr->start();
    return 0;
}

【linux】守护进程(精灵进程)
而为了获取服务器的日志信息,我们可以重定向到文件中。

// log.hpp
#pragma once

#include <iostream>
#include <string>
#include <cstdarg>
#include <time.h>
#include <ctime>
#include <unistd.h>
#include <cstdio>

#define DEBUG   0// 调试
#define NORMAL  1// 正常
#define WARNING 2// 警告
#define ERROR   3// 错误
#define FATAL   4// 致命错误

#define LOG_NOR "log.txt"
#define LOG_ERR "log.error"

const char* to_string_level(int level)
{
    switch(level)
    {
        case DEBUG: return "DEBUG";
        case NORMAL: return "NORMAL";
        case WARNING: return "WARNING";
        case ERROR: return "ERROR";
        case FATAL: return "FATAL";
        default : return nullptr;
    }
}

void logMessage(int level, const char* format, ...)
{
    // [日志等级][时间][pid][信息]
    char logprefix[1024];
    time_t now;
    time(&now);
    struct tm *ptm = localtime(&now);
    char timebuf[1024];
    snprintf(timebuf, sizeof timebuf, "%d年%d月%d日 %d:%d:%d", ptm->tm_year + 1900, ptm->tm_mon + 1, ptm->tm_mday, ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
    snprintf(logprefix, sizeof logprefix, "[%s][%s][pid: %d]", to_string_level(level), timebuf, getpid());
    char logline[1024];
    va_list arg;
    va_start(arg, format);
    vsnprintf(logline, sizeof logline, format, arg);
    // std::cout << logprefix << logline << std::endl;
    FILE *nor = fopen(LOG_NOR, "a");
    FILE *err = fopen(LOG_ERR, "a");
    if(nor && err)
    {
        if(level == DEBUG || level == NORMAL || level == WARNING)
        {
            fprintf(nor, "%s%s\n", logprefix, logline);
        }
        else
        {
            fprintf(err, "%s%s\n", logprefix, logline);
        }
        fclose(nor);
        fclose(err);
    }
}

客户端:
【linux】守护进程(精灵进程)
服务端:
【linux】守护进程(精灵进程)文章来源地址https://www.toymoban.com/news/detail-463657.html



到了这里,关于【linux】守护进程(精灵进程)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 守护进程(精灵进程)

    目录 前言 1.如何理解前台进程和后台进程 2.守护进程的概念 3.为什么会存在守护进程 4.如何实现守护进程 5.测试 总结         今天我们要介绍的是关于守护进程如何实现,可能有小伙伴第一次听到守护进程这个概念,感觉很懵,知道进程的概念,但是不知道守护进程是什么

    2024年02月11日
    浏览(25)
  • 基于linux下的高并发服务器开发(第一章)- Linux系统IO函数

     (1)man 2 open 打开一个已经存在的文件 int open(const char *pathname, int flags); 参数:             pathname:要打开文件路径             - flags:对文件的操作权限设置还有其他的设置             O_RDONLY,O_WRONLY,O_RDWR 这三个设置是互斥的 返回值:             返回一个新的文件描述

    2024年02月16日
    浏览(44)
  • 基于linux下的高并发服务器开发(第一章)- fcntl函数

    #include unistd.h #include fcntl.h int fcntl(int fd, int cmd, ...); 参数:     fd : 表示需要操作的文件描述符     cmd: 表示对文件描述符进行如何操作          - F_DUPFD : 复制文件描述符,复制的是第一个参数fd,                               得到一个新的文件描述符(返回值)   

    2024年02月16日
    浏览(31)
  • 基于linux下的高并发服务器开发(第一章)- 目录操作函数

     (1)int mkdir(const char* pathname,mode_t mode); #include sys/stat.h #include sys/types.h int mkdir(const char *pathname, mode_t mode);     作用:创建一个目录     参数:          pathname: 创建的目录的路径         mode: 权限,八进制的数     返回值:          成功返回0, 失败返回-1  (

    2024年02月16日
    浏览(33)
  • Linux高性能服务器编程 学习笔记 第一章 TCP/IP协议族

    现在Internet使用的主流协议族是TCP/IP协议族,它是一个分层、多协议的通信体系。 TCP/IP协议族包含众多协议,我们只详细讨论IP协议和TCP协议,因为它们对编写网络应用程序有最直接的影响。如果想系统学习网络协议,RFC(Request For Comments,评论请求)是首选资料。 TCP/IP协议

    2024年02月09日
    浏览(49)
  • 基于linux下的高并发服务器开发(第一章)- Makefile(2)1.11

    ◼ 命令在执行之前,需要先检查规则中的依赖是否存在      如果存在,执行命令      如果不存在,向下检查其它的规则,检查有没有一个规则是用来生成这个依赖的,如  果找到了,则执行该规则中的命令。 ◼ 检测更新,在执行规则中的命令时,会比较目标和依赖文

    2024年02月16日
    浏览(39)
  • linux定时删除服务器日志

    不说废话。直接进入操作流程 linux 定时任务是用的crontab 查看 crontab是否启动 dead 死的 启动crontab 再次查看状态 running  运转的 查看 crontab 查看 crontab任务 编辑 crontab任务 创建了一个任务  0 1 * * *  sh /workspace/java/del_log/dele_log.sh 每天1点自动执行 其中: 第一个号表示时间中的

    2024年02月09日
    浏览(39)
  • linux并发服务器 —— 多进程并发(四)

    程序是包含一系列信息的文件,描述了如何在运行时创建一个进程; 进程是正在运行的程序的实例,可以用一个程序来创建多个进程; 用户内存空间包含程序代码以及代码所使用的变量,内核数据结构用于维护进程状态信息; 进程控制块(PCB):维护进程相关的信息,tas

    2024年02月11日
    浏览(40)
  • 基于linux下的高并发服务器开发(第一章)- 模拟实现 ls-l 命令

     这一小节会用到上面两张图的红色框里面的变量 任务: 模拟实现 ls -l 指令 -rw-rw-r-- 1 nowcoder nowcoder 12 12月  3 15:48 a.txt    

    2024年02月16日
    浏览(32)
  • Linux服务器定时执行脚本清理日志

    程序的日志不正确或者启动脚本命令不正确,随着程序的持续运行,日志文件越来越大,持续占用设备硬盘,如果定期手动清理日志又比较占用大脑CPU和内存,如果忘了就是事故,所以写一个定时执行的脚本去清理日志很有必要。 清理日志有两种方式,一种是清空文件,主要

    2024年02月05日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包