广告
返回顶部
首页 > 资讯 > 操作系统 >[Linux]----守护进程
  • 327
分享到

[Linux]----守护进程

linux服务器运维 2023-09-30 22:09:20 327人浏览 泡泡鱼
摘要

文章目录 前言一、什么是守护进程?二、会话和进程组会话进程组 三、守护进程的编程流程四、命令行指令让进程守护化总结总结 前言 这节课我来给大家讲解在linux下如何让进程守护化,运行

文章目录


前言

这节课我来给大家讲解在linux下如何让进程守护化,运行在后台,处理我们的任务.


正文开始!

一、什么是守护进程?

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

Linux系统启动是会启动很多服务清楚,这些系统服务进程没有控制终端,不能直接和用户交互.其他进程都是在用户登录或运行程序时创建.在运行结束或者用户注销时终止,但系统服务进程不受用户登录注销的影响,它们一直运行这.这种进程都有一个名称叫守护进程(Daemon).

例如:udevd负责维护/dev目录下的设备文件,acpid负责电源管理,syslogd负责维护/var/log下的日志文件,可以看出守护进程通常采用以d结尾的名称,表示Daemon.

一般以服务器的方式工作,对外提供服务的服务器,都是以守护进程(精灵进程)的方式在服务器中工作的,一旦启动之后,除非用户主动关闭.否则,一直会运行.

二、会话和进程组

会话

每打开一个控制中断,或者在用户登录时,系统就会创建新会话.
每个会话通常都与一个控制中断相关联.
在该会话中允许的第一个进程称作会话首进程,通常这个首进程就是shell.

下面是与会话相关的系统调用:
代表创建新会话,当前调用进程不能是组长进程
在这里插入图片描述

在这里插入图片描述

举个栗子:

在这里插入图片描述

进程组

每个进程都属于某个进程组,进程组是由一个或多个相互间有关联的进程组成的,他的目的是为了进行作业控制.
进程租的主要特征就是信号可以发给进程组中的所有进程:这个信号可以使同一个进程组中的所有进程终止,停止或者继续运行.
每个进程组都由进程组id唯一标识,并且有一个组长进程.进程组id就是组长进程的pid.只要在某个进程组中还有一个进程存在,则该进程组就存在.
即使组长进程终止了,在#include pid_t setsid(void);创建新会话,当前调用进程不能是组长进程pid_t getsid(pid_t isd);获取进程的所属会话id,pid=0是代表当前进程进程组依然存在.

在这里插入图片描述

三、守护进程的编程流程

必须要调用一个函数setsid():将调用进程设置称为独立的会话
这个函数要求进程组的组长不能调用.

我如何不成为组长呢?
你可以称为进程组内的第二个进程!—>常规做法,fork()子进程,子进程就不再是组长进城啦,他就可以成功调用setsid();

必须要做的:
if(fork()>0) exit(0);
setsid();

选做的内容:
之前我们在学习管道的时候,写端一直在写,读端关闭,写端会被终止,被信号SIGPIPE;

server一直向已经关闭的client写入,server也会收到SIGPIPE.

忽略SIGPIPE信号!

更改进程的工作目录,如何更改进程的工作目录呢?—>chdir

一般必做的:

  1. close(0,1,2)[很少有人这样做]

    Linux下的"信息黑洞或者垃圾桶",就是向这个文件里面读写的内容都被丢弃了!

  2. 打开/dev/null,并且进行对0,1,2进行重定向.

代码如下
dameonize.hpp

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

servertcpd.cc

#include "util.hpp"#include "log.hpp"#include "ThreadPool.hpp"#include "Task.hpp"#include"daemonize.hpp"using namespace std;class ServerTcp;struct ThreadData{    ThreadData(int sock, string clientIp, uint16_t clientPort, ServerTcp *ts)        : _sock(sock), _clientIp(clientIp), _clientPort(clientPort), _this(ts)    {    }    int _sock;    string _clientIp;    uint16_t _clientPort;    ServerTcp *_this;};//大小写转化// TCP && UDP支持全双工void transService(int sock, const string &clientIp, uint16_t clientPort){    assert(sock > 0);    assert(!clientIp.empty());    assert(clientPort > 1024);    char inbuffer[BUFFER_SIZE];    while (true)    {        ssize_t s = read(sock, inbuffer, sizeof(inbuffer) - 1); //我们认为读到的都是字符串        if (s > 0)        {            // read success            inbuffer[s] = '\0';            if (strcasecmp(inbuffer, "quit") == 0)            {                logMessage(DEBUG, "client quit -- %s[%d]", clientIp.c_str(), clientPort);                break;            }            logMessage(DEBUG, "trans before: %s[%d]>> %s", clientIp.c_str(), clientPort, inbuffer);            //可以进行大小写转化了            for (int i = 0; i < s; i++)            {                if (isalpha(inbuffer[i]) && islower(inbuffer[i]))                    inbuffer[i] = toupper(inbuffer[i]);            }            write(sock, inbuffer, sizeof(inbuffer));            logMessage(DEBUG, "trans after: %s[%d]>> %s", clientIp.c_str(), clientPort, inbuffer);        }        else if (s == 0)        {            // pipe:读端一直在读,写端不写了,并且关闭了写端,读端会如何?--->s==0,代表对端关闭            // s==0,代表对方关闭,Client退出            logMessage(DEBUG, "client quit -- %s[%d]", clientIp.c_str(), clientPort);            break;        }        else        {            logMessage(DEBUG, "%s[%d] -- read: %s", clientIp.c_str(), clientPort, strerror(errno));            break;        }    }    //只要走到这里,一定是client退出了,服务到此结束    close(sock); // 如果一个进程对应的文件fd,打开了没有被归还,文件描述符泄露!    logMessage(DEBUG, "server close %d done", sock);}void execCommand(int sock, const string &clientIp, uint16_t clientPort){    assert(sock > 0);    assert(!clientIp.empty());    assert(clientPort > 1024);    char command[BUFFER_SIZE];    while (true)    {        ssize_t s = read(sock, command, sizeof(command) - 1); //我们认为读到的都是字符串        if (s > 0)        {            command[s]='\0';            logMessage(DEBUG,"[%s:%d] exec [%s]",clientIp.c_str(),clientPort,command);            std::string safe;            if((safe.find("rm")!=std::string::npos)||(safe.find("unlink")!=std::string::npos))            {                break;            }            // 我们是以r方式打开的文件,没有写入            //所以我们无法通过dup2的方式得到对应的结果            FILE* fp=popen(command,"r");            if(fp==nullptr)            {                logMessage(WARINING,"exec %s failed, because: %s",command,strerror(errno));                break;            }            char line[1024];            while(fgets(line,sizeof(line)-1,fp)!=nullptr)            {                write(sock,line,strlen(line));            }            // dup2(sock,fp->_fileno);            // fflush(fp);            pclose(fp);            logMessage(DEBUG,"[%s]:%d exec [%s]... done",clientIp.c_str(),clientPort,command);        }        else if (s == 0)        {            // pipe:读端一直在读,写端不写了,并且关闭了写端,读端会如何?--->s==0,代表对端关闭            // s==0,代表对方关闭,Client退出            logMessage(DEBUG, "client quit -- %s[%d]", clientIp.c_str(), clientPort);            break;        }        else        {            logMessage(DEBUG, "%s[%d] -- read: %s", clientIp.c_str(), clientPort, strerror(errno));            break;        }    }    //只要走到这里,一定是client退出了,服务到此结束    close(sock); // 如果一个进程对应的文件fd,打开了没有被归还,文件描述符泄露!    logMessage(DEBUG, "server close %d done", sock);}class ServerTcp{public:    ServerTcp(uint16_t port, string ip = "")        : _listenSock(-1), _port(port), _ip(ip), _tp(nullptr)    {    }    ~ServerTcp()    {    }public:    void init()    {        // 1.创建Socket        _listenSock = socket(AF_INET, SOCK_STREAM, 0);        if (_listenSock < 0)        {            logMessage(FATAL, "socket:%s", strerror(errno));            exit(SOCKET_ERR);        }        logMessage(DEBUG, "socket:&s,%d", strerror(errno), _listenSock);        // 2.bind绑定        // 2.1填充服务器        struct sockaddr_in local; //用户栈        memset(&local, 0, sizeof local);        local.sin_family = AF_INET;        local.sin_port = htons(_port);        _ip.empty() ? (local.sin_addr.s_addr = INADDR_ANY) : (inet_aton(_ip.c_str(), &local.sin_addr));        // 2.2本地socket信息,写入_sock对应的内核区域        if (bind(_listenSock, (const sockaddr *)&local, sizeof local) < 0)        {            logMessage(FATAL, "bind: %s", strerror(errno));            exit(BIND_ERR);        }        logMessage(DEBUG, "bind: %s", strerror(errno));        // 3.监听socket,为何要监听呢?tcp是面向连接的!        if (listen(_listenSock, 5 ) < 0)        {            logMessage(FATAL, "listen: %s", strerror(errno));            exit(LISTEN_ERR);        }        logMessage(DEBUG, "listen: %s", strerror(errno));        //允许别人来连接你了        // 4.加载线程池        _tp = ThreadPool<Task>::getInstance();    }    void loop()    {        // signal(SIGCHLD,SIG_IGN);//只在Linux下有效        _tp->start();        logMessage(DEBUG, "thread pool start success,thread num: %d", _tp->ThreadNum());        while (true)        {            struct sockaddr_in peer;            socklen_t len = sizeof(peer);            // 4.获取连接,accept的返回值是一个新的socket fd??            // 4.1 _listenScok:监听&&获取新的连接--->sock            // 4.2 serviceSock:给用户提供新的socket服务            int serviceSock = accept(_listenSock, (struct sockaddr *)&peer, &len);            if (serviceSock < 0)            {                //获取连接失败                logMessage(WARINING, "accept: &s[%d]", strerror(errno), serviceSock);                continue;            }            // 4.1获取客户端基本信息            uint16_t peerPort = ntohs(peer.sin_port);            string peerIp = inet_ntoa(peer.sin_addr);            logMessage(DEBUG, "accept: %s | %s[%d],socker fd: %d",                       strerror(errno), peerIp.c_str(), peerPort, serviceSock);            // 5.提供服务,小写转大写            // // 5.0 v0版本----单进程--一点进行transService,主执行流就无法进行向后执行,只能提供完毕服务后才能进行accept            // transService(serviceSock, peerIp, peerPort);            // //5.1 V1---多进程版本---父进程打开的文件会被子进程继承!            // pid_t id=fork();            // assert(id!=-1);            // if(id==0)            // {            //     close(_listenSock);            //     //子进程            //     transService(serviceSock, peerIp, peerPort);            //     exit(0);            // }            // //父进程            // close(serviceSock);//这一步是一定要做的!            // //waitpid();默认是阻塞等待!WNOHANG            // //方案1            // // //5.1 V1.1---多进程版本            // //爷爷进程            // pid_t id=fork();            // if(id==0)            // {            //     //爸爸进程            //     close(_listenSock);            //     //又进行了一次fork            //     if(fork>0) exit(0);            //     //孙子进程--就没有爸爸进程了--孤儿进程--被系统领养了--回收问题就交给了系统来回收            //     transService(serviceSock, peerIp, peerPort);            //     exit(0);            // }            // close(serviceSock);            // //爸爸进程直接终止,立马得到退出码,释放僵尸进程状态            // pid_t ret=waitpid(id,nullptr,0);//就用阻塞式等待            // (void)ret;            // //5.2 v2版本---多线程            // //这里不需要关闭文件描述符了!            // ThreadData* td=new ThreadData(serviceSock,peerIp,peerPort,this);            // pthread_t tid;            // pthread_create(&tid,nullptr,startRountine,(void*)td);             5.3 v3版本 --- 线程池版本              5.3.1 构建任务              5.3 v3.1            // Task t(serviceSock, peerIp, peerPort, std::bind(&ServerTcp::transService, this,std::placeholders::_1,std::placeholders::_2,std::placeholders::_3));            // _tp->push(t);             5.3 v3.2            // Task t(serviceSock, peerIp, peerPort, transService);            // _tp->push(t);            5.3 v3.3            Task t(serviceSock, peerIp, peerPort, execCommand);            _tp->push(t);            // logMessage(DEBUG,"server provide service start ...");            // sleep(1);        }    }    // static void *startRountine(void *args)    // {    //     pthread_detach(pthread_self());    //     ThreadData *td = static_cast(args);    //     td->_this->transService(td->_sock, td->_clientIp, td->_clientPort);    //     delete (td);    // }    // TCP && UDP支持全双工    // void transService(int sock, const string &clientIp, uint16_t clientPort)    // {    //     assert(sock > 0);    //     assert(!clientIp.empty());    //     assert(clientPort > 1024);    //     char inbuffer[BUFFER_SIZE];    //     while (true)    //     {    //         ssize_t s = read(sock, inbuffer, sizeof(inbuffer) - 1); //我们认为读到的都是字符串    //         if (s > 0)    //         {    //             // read success    //             inbuffer[s] = '\0';    //             if (strcasecmp(inbuffer, "quit") == 0)    //             {    //                 logMessage(DEBUG, "client quit -- %s[%d]", clientIp.c_str(), clientPort);    //                 break;    //             }    //             logMessage(DEBUG, "trans before: %s[%d]>> %s", clientIp.c_str(), clientPort, inbuffer);    //             //可以进行大小写转化了    //             for (int i = 0; i < s; i++)    //             {    //                 if (isalpha(inbuffer[i]) && islower(inbuffer[i]))    //                     inbuffer[i] = toupper(inbuffer[i]);    //             }    //             write(sock, inbuffer, sizeof(inbuffer));    //             logMessage(DEBUG, "trans after: %s[%d]>> %s", clientIp.c_str(), clientPort, inbuffer);    //         }    //         else if (s == 0)    //         {    //             // pipe:读端一直在读,写端不写了,并且关闭了写端,读端会如何?--->s==0,代表对端关闭    //             // s==0,代表对方关闭,Client退出    //             logMessage(DEBUG, "client quit -- %s[%d]", clientIp.c_str(), clientPort);    //             break;    //         }    //         else    //         {    //             logMessage(DEBUG, "%s[%d] -- read: %s", clientIp.c_str(), clientPort, strerror(errno));    //             break;    //         }    //     }    //     //只要走到这里,一定是client退出了,服务到此结束    //     close(sock); // 如果一个进程对应的文件fd,打开了没有被归还,文件描述符泄露!    //     logMessage(DEBUG, "server close %d done", sock);    // }private:    int _listenSock;    uint16_t _port;    string _ip;    //引入线程池    ThreadPool<Task> *_tp;};static void Usage(string proc){    cerr << "Usage\n\t" << proc << " port ip" << endl;    cerr << "Example\n\t" << proc << " 8080  127.0.0.1\n"         << endl;}// ./serverTcp local_port [local_ip]int main(int argc, char *argv[]){    if (argc != 2 && argc != 3)    {        Usage(argv[0]);        exit(USAGE_ERR);    }    uint16_t port = stoi(argv[1]);    string ip;    if (argc == 3)    {        ip = argv[2];    }    daemonize();//我们的进程就会成为守护进程    ServerTcp svr(port, ip);    svr.init();    svr.loop();    return 0;}

在这里插入图片描述
此时服务器就部署在了Linux中.

注意:
进程守护化以后,只能使用kill命令杀掉该进程!
在这里插入图片描述

四、命令行指令让进程守护化

nohup [进程名] &

hello.cc

#include#include#includeint main(){    while(true)    {        std::cout<<"hello rose"<<std::endl;        sleep(1);    }    return 0;}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
重新登录用户之后
在这里插入图片描述
这个进程已经成为了话首了!!!

这样就让进程守护化啦!!!

总结

进程守护化的三种方法

  1. 自己写(严重推荐)
  2. 系统调用daemon()函数
    在这里插入图片描述
  3. nohup ./command &,默认形成日志; nohup.out

总结

(本章完!)

来源地址:https://blog.csdn.net/m0_61560468/article/details/128519109

--结束END--

本文标题: [Linux]----守护进程

本文链接: https://www.lsjlt.com/news/422076.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • [Linux]----守护进程
    文章目录 前言一、什么是守护进程二、会话和进程组会话进程组 三、守护进程的编程流程四、命令行指令让进程守护化总结总结 前言 这节课我来给大家讲解在Linux下如何让进程守护化,运行在...
    99+
    2023-09-30
    linux 服务器 运维
  • linux 守护进程详解及建立守护进程
    linux 守护进程详解及建立守护进程 守护进程是一种后台运行并且独立于所有终端控制之外的进程。   守护进程的启动   要启动一个守护进程,可以采取一下几种方式: 在系统期间通过系统的初始化脚本启动...
    99+
    2022-06-04
    进程 详解 linux
  • linux守护进程怎么启动
    在Linux中,守护进程的启动通常有以下几种方式: 使用命令行启动:在终端中使用命令行启动守护进程,例如: ./daemon ...
    99+
    2023-10-23
    linux
  • linux守护进程怎么创建
    这篇文章主要讲解了“linux守护进程怎么创建”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“linux守护进程怎么创建”吧!在linux中,守护进程也称“精灵进程”,是一个在后台运行且不受任...
    99+
    2023-06-29
  • Linux守护进程如何启动
    本文小编为大家详细介绍“Linux守护进程如何启动”,内容详细,步骤清晰,细节处理妥当,希望这篇“Linux守护进程如何启动”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。&quo...
    99+
    2022-10-19
  • linux shell实现守护进程脚本
    嵌入式初学者,第一次上传代码。昨天做了一个udhcpd与udhcpc的守护,目前只会用shell模仿编写,还有什么方法可以做守护呢? #! /bin/sh #进程名字可修改 PRO_NAME=udhcp...
    99+
    2022-06-04
    脚本 进程 linux
  • linux中怎么创建守护进程
    这期内容当中小编将会给大家带来有关linux中怎么创建守护进程,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。创建子进程,父进程退出   这是编写守护进程的第一步。由于守护进程是脱离控制终端的,因此,完成第...
    99+
    2023-06-09
  • 如何启动Linux的守护进程
    这篇文章主要介绍“如何启动Linux的守护进程”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“如何启动Linux的守护进程”文章能帮助大家解决问题。Linux Daemon(守护进程)是运行在后台的一...
    99+
    2023-06-27
  • linux中如何建立守护进程
    小编给大家分享一下linux中如何建立守护进程,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!linux 守护进程详解及建立守护进程守护进程是一种后台运行并且独立于...
    99+
    2023-06-09
  • Linux中守护进程如何启动
    这篇文章将为大家详细讲解有关Linux中守护进程如何启动,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。护进程也称精灵进程(Daemon),是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任...
    99+
    2023-06-27
  • Python 守护进程
     nohup 可以使程序后台运行不受终端影响,但想使程序运行后就脱离终端Python需要用到os.fork来实现,例子如下: daemonize.py #!/usr/bin/python #coding:utf-8  import sys ...
    99+
    2023-01-31
    进程 Python
  • python守护进程
    假如写一段服务端程序,如果ctrl+c退出或者关闭终端,那么服务端程序就会退出,于是就想着让这个程序成为守护进程,像httpd一样,一直在后端运行,不会受终端影响。守护进程英文为daemon,像httpd,mysqld,最后一个字母d其实就...
    99+
    2023-01-31
    进程 python
  • Linux系统守护进程怎么理解
    这篇文章主要为大家分析了Linux系统守护进程怎么理解的相关知识点,内容详细易懂,操作细节合理,具有一定参考价值。如果感兴趣的话,不妨跟着跟随小编一起来看看,下面跟着小编一起深入学习“Linux系统守护进程怎么理解”的知识吧。什么是守护进程...
    99+
    2023-06-28
  • linux下的守护进程实例分析
    本篇内容主要讲解“linux下的守护进程实例分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“linux下的守护进程实例分析”吧!shell控制的php常驻进程...
    99+
    2022-10-19
  • [转]Python 守护进程
    守护进程:通常被定义为一个后台进程,而且它不属于任何一个终端会话(terminal session)。许多系统服务由守护程序实施;如网络服务,打印等。  下面是转自一位网友写的编写守护进程的步骤: 1. 调用fork()以便父进程可...
    99+
    2023-01-31
    进程 Python
  • Python setdaemon守护进程
    setdaemon守护进程#_*_coding:utf-8_*_ __author__ = 'gaogd' import time import threading ''' 守护进程,如果主线程down了,子线程也就没有了。 下...
    99+
    2023-01-31
    进程 Python setdaemon
  • python 守护进程(daemon)
    守护进程的编写步骤: 1、fork子进程,然后父进程退出,此时子进程会被init进程接管。 2、修改子进程的工作目录,创建新进程组合新会话,修改umask。 3、子进程再次fork一个进程,这个进程可以称为孙子进程,然后子进程退出。 4、重...
    99+
    2023-01-31
    进程 python daemon
  • linux 创建守护进程的相关知识
    创建子进程,父进程退出   这是编写守护进程的第一步。由于守护进程是脱离控制终端的,因此,完成第一步后就会在Shell终端里造成一程序已经运行完毕的假象。之后的所有工作都在子进程中完成,而用户在Shell终...
    99+
    2022-06-04
    相关知识 进程 linux
  • Linux下以守护进程方式运行.NET6
    前言 ​ 在《步步入门》 .NET 6 部署到Linux 一文中只是演示了控制终端方式运行ASP.ENT,在实际的应用中,这种方式不能确保服务延续性。如果控制终端关闭,或者服务...
    99+
    2022-11-12
  • golang 守护进程 部署
    随着互联网技术的日新月异,各种高性能的语言层出不穷。其中,Golang 以其高效的并发机制和简洁的语法成为了越来越多开发者的首选语言。在实际生产环境中,我们经常需要将我们的 Golang 应用以守护进程的形式部署在服务器上,以便实现自动启动...
    99+
    2023-05-15
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作