揭陽網(wǎng)站制作案例/如何在各種網(wǎng)站投放廣告
目錄
一、守護進程的定義與特點
1、定義
2、特點
二、守護進程的原理
三、守護進程與會話(Session)的關(guān)系
四、C++實現(xiàn)守護進程
?????守護進程(Daemon Process)是一個在后臺運行、通常不與用戶直接交互的進程。守護進程是操作系統(tǒng)中非常重要的一部分,常見的應(yīng)用包括系統(tǒng)日志、網(wǎng)絡(luò)服務(wù)、數(shù)據(jù)庫管理等。在這篇博客中,我們將詳細(xì)探討守護進程的原理、如何與會話管理聯(lián)系,并通過C++實現(xiàn)一個簡單的守護進程。將服務(wù)器守護進程化的主要目的是確保服務(wù)器在后臺持續(xù)運行,并在意外崩潰或重啟后自動恢復(fù)。這樣可以使服務(wù)在沒有人工干預(yù)的情況下長期穩(wěn)定運行,并減少系統(tǒng)管理的復(fù)雜度。
一、守護進程的定義與特點
1、定義
守護進程是一個沒有終端控制的進程,它通常在系統(tǒng)啟動時啟動,獨立于任何用戶會話(session),并且在后臺持續(xù)運行。守護進程的特點是它不依賴于用戶的輸入輸出,運行時不會產(chǎn)生終端交互。
2、特點
- 后臺運行:守護進程通常在操作系統(tǒng)啟動時啟動,或者在用戶登錄后由系統(tǒng)服務(wù)啟動,并在后臺持續(xù)運行。
- 無終端:守護進程不與任何終端或用戶會話關(guān)聯(lián),它通常不與標(biāo)準(zhǔn)輸入輸出(stdin, stdout, stderr)相關(guān)聯(lián)。
- 獨立性:守護進程與用戶的登錄會話是獨立的,它在后臺靜靜運行,執(zhí)行系統(tǒng)級任務(wù),如日志記錄、定時任務(wù)、文件清理等。
- 父進程為init進程:守護進程在系統(tǒng)啟動時由父進程(通常是init進程)啟動,運行時不會退出。
- PID(進程ID):守護進程的PID是由操作系統(tǒng)分配的,它通常會被寫入到某個文件中以供后續(xù)管理和終止。
二、守護進程的原理
守護進程是通過脫離控制終端、使自己成為一個獨立的后臺進程來實現(xiàn)的。這是通過幾個步驟實現(xiàn)的:
-
創(chuàng)建子進程:守護進程首先會創(chuàng)建一個子進程,父進程退出,子進程繼續(xù)執(zhí)行,這樣可以讓守護進程避免與任何用戶會話或終端直接交互。
-
創(chuàng)建新的會話(Session):守護進程通常會調(diào)用
setsid()
系統(tǒng)調(diào)用創(chuàng)建一個新的會話,成為該會話的首進程。新會話的創(chuàng)建意味著它不再與原始的控制終端和進程組相關(guān)聯(lián)。 -
改變工作目錄:守護進程通常會調(diào)用
chdir()
來更改工作目錄。為了避免占用終端的目錄,守護進程通常會將工作目錄更改為/
。 -
關(guān)閉文件描述符:守護進程還會關(guān)閉與終端相關(guān)的文件描述符,包括標(biāo)準(zhǔn)輸入(stdin)、標(biāo)準(zhǔn)輸出(stdout)、標(biāo)準(zhǔn)錯誤(stderr)。通常會將它們重定向到
/dev/null
,以防止輸出到終端。 -
忽略信號:守護進程會設(shè)置適當(dāng)?shù)男盘柼幚?#xff0c;避免因收到如SIGHUP等信號導(dǎo)致進程退出。
void Daemon(const std::string &cwd = "")
{// 1. 忽略其他異常信號 signal(SIGCLD, SIG_IGN);signal(SIGPIPE, SIG_IGN);signal(SIGSTOP, SIG_IGN);// 2. 將自己變成獨立的會話if (fork() > 0)//>0說明是父進程,讓父進程直接退出exit(0);setsid(); //子進程// 3. 更改當(dāng)前調(diào)用進程的工作目錄if (!cwd.empty())chdir(cwd.c_str());// 4. 標(biāo)準(zhǔn)輸入,標(biāo)準(zhǔn)輸出,標(biāo)準(zhǔn)錯誤重定向至/dev/null 垃圾桶int fd = open(nullfile.c_str(), O_RDWR);if(fd > 0){dup2(fd, 0);dup2(fd, 1);dup2(fd, 2);close(fd);}
}
三、守護進程與會話(Session)的關(guān)系
會話(Session)是與進程、終端和進程組密切相關(guān)的概念。會話的作用是管理一組相關(guān)的進程。
-
會話的創(chuàng)建:每個登錄的用戶會話都有一個會話ID(Session ID),一個會話可以有多個進程組(Process Group),而每個進程組中的進程共享同一個控制終端。
-
脫離控制終端:守護進程通過
setsid()
系統(tǒng)調(diào)用來脫離當(dāng)前會話及其控制終端,成為一個新的會話的首進程。這樣,守護進程就不再與任何終端關(guān)聯(lián),它可以自由地運行而不受用戶的控制。 -
控制終端:一旦守護進程創(chuàng)建了新的會話并成為首進程,它就不再與任何控制終端關(guān)聯(lián)??刂平K端通常與用戶的登錄會話相關(guān)聯(lián),但守護進程會斷開這一關(guān)系,避免終端輸入或輸出干擾其運行。
-
進程組與信號處理:會話中的進程通常共享進程組,而進程組的控制由會話首進程管理。守護進程通常會設(shè)置信號處理機制,使其能夠管理來自進程組的信號。
四、C++實現(xiàn)守護進程
#pragma once#include <iostream>
#include <cstdlib>
#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 = "")
{// 1. 忽略其他異常信號 signal(SIGCLD, SIG_IGN);signal(SIGPIPE, SIG_IGN);signal(SIGSTOP, SIG_IGN);// 2. 將自己變成獨立的會話if (fork() > 0)//>0說明是父進程,讓父進程直接退出exit(0);setsid(); //子進程// 3. 更改當(dāng)前調(diào)用進程的工作目錄if (!cwd.empty())chdir(cwd.c_str());// 4. 標(biāo)準(zhǔn)輸入,標(biāo)準(zhǔn)輸出,標(biāo)準(zhǔn)錯誤重定向至/dev/null 垃圾桶int fd = open(nullfile.c_str(), O_RDWR);if(fd > 0){dup2(fd, 0);dup2(fd, 1);dup2(fd, 2);close(fd);}
}
#pragma once#include <iostream>
#include <string>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <pthread.h>
#include <signal.h>
#include <signal.h>
#include "Log.hpp"
#include "ThreadPool.hpp"
#include "Task.hpp"
#include "Daemon.hpp"const int defaultfd = -1;
const std::string defaultip = "0.0.0.0";
const int backlog = 10; // 但是一般不要設(shè)置的太大
extern Log lg;enum
{UsageError = 1,SocketError,BindError,ListenError,
};class TcpServer;class ThreadData
{
public:ThreadData(int fd, const std::string &ip, const uint16_t &p, TcpServer *t): sockfd(fd), clientip(ip), clientport(p), tsvr(t){}
public:int sockfd;std::string clientip;uint16_t clientport;TcpServer *tsvr;
};class TcpServer
{
public:TcpServer(const uint16_t &port, const std::string &ip = defaultip) : listensock_(defaultfd), port_(port), ip_(ip){}void InitServer(){listensock_ = socket(AF_INET, SOCK_STREAM, 0);if (listensock_ < 0){lg(Fatal, "create socket, errno: %d, errstring: %s", errno, strerror(errno));exit(SocketError);}lg(Info, "create socket success, listensock_: %d", listensock_);int opt = 1;setsockopt(listensock_, SOL_SOCKET, SO_REUSEADDR|SO_REUSEPORT, &opt, sizeof(opt)); // 防止偶發(fā)性的服務(wù)器無法進行立即重啟(tcp協(xié)議的時候再說)struct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(port_);inet_aton(ip_.c_str(), &(local.sin_addr));// local.sin_addr.s_addr = INADDR_ANY;if (bind(listensock_, (struct sockaddr *)&local, sizeof(local)) < 0){lg(Fatal, "bind error, errno: %d, errstring: %s", errno, strerror(errno));exit(BindError);}lg(Info, "bind socket success, listensock_: %d", listensock_);// Tcp是面向連接的,服務(wù)器一般是比較“被動的”,服務(wù)器一直處于一種,一直在等待連接到來的狀態(tài)if (listen(listensock_, backlog) < 0){lg(Fatal, "listen error, errno: %d, errstring: %s", errno, strerror(errno));exit(ListenError);}lg(Info, "listen socket success, listensock_: %d", listensock_);}void Start(){Daemon();ThreadPool<Task>::GetInstance()->Start();// for fork();// signal(SIGCHLD, SIG_IGN);lg(Info, "tcpServer is running....");for (;;){// 1. 獲取新連接struct sockaddr_in client;socklen_t len = sizeof(client);int sockfd = accept(listensock_, (struct sockaddr *)&client, &len);if (sockfd < 0){lg(Warning, "accept error, errno: %d, errstring: %s", errno, strerror(errno)); //?continue;}uint16_t clientport = ntohs(client.sin_port);char clientip[32];inet_ntop(AF_INET, &(client.sin_addr), clientip, sizeof(clientip));// version 4 --- 線程池版本Task t(sockfd, clientip, clientport);ThreadPool<Task>::GetInstance()->Push(t);}}~TcpServer() {}private:int listensock_;uint16_t port_;std::string ip_;
};
表示服務(wù)已經(jīng)啟動