[Computer Network] Logs and daemons

log

Generally, cout is used for printing, but cout printing is not standardized.
In fact, logs are used for printing.

log creation

Create a log.hpp


The log has its own log level

Through enumeration, they are debugging general alarms general errors fatal errors unknown errors


logmessage function

Define a function logmessage, the parameter level is the log level,
in order to format the output according to the way of variable parameters, so set a format and ... variable parameters ( you can pass any number of parameters to the c function)

The left part of the log is implemented

Type man snprintf

Display the contents of variable parameters into the str string


get log level

Set a string level_string, and convert the number to a string through the tolevelstring function


Get Time

Type man localtime

Convert time_t to struct tm structure type


This structure contains seconds, minutes, hours and days


Type man 3 time


insert image description here
Get the time through the gettime function


The right part of the log is realized

In order to deal with the variable parameter part, so use vsprintf
to enter man snprintf


Put the written data into logRight

full code

log.hpp (overall implementation)

#pragma once 
#include<iostream>
#include<string.h>
#include<cstdio>
#include<cstring>
#include<cstdarg>
#include<unistd.h>
#include<sys/types.h>
#include<time.h>

const std::string  filename="tecpserver.log";

//日志等级
enum{
    
    
 DEBUG=0, // 用于调试
 INFO  ,  //1 常规
 WARNING, //2 告警
 ERROR ,  //3  一般错误
 FATAL ,  //4 致命错误
 UKNOWN//未知错误
};

static  std::string tolevelstring(int level)//将数字转化为字符串
{
    
    
  switch(level)
  {
    
    
     case DEBUG : return "DEBUG";
     case INFO  : return "INFO";
     case WARNING : return "WARNING";
     case  ERROR : return "ERROR";
     case FATAL : return "TATAL";
     default: return "UKNOWN";
  }
}
std::string gettime()//获取时间
{
    
    
   time_t curr=time(nullptr);//获取time_t
   struct tm *tmp=localtime(&curr);//将time_t 转换为 struct tm结构体
   char buffer[128];
   snprintf(buffer,sizeof(buffer),"%d-%d-%d %d:%d:%d",tmp->tm_year+1900,tmp->tm_mon+1,tmp->tm_mday,
   tmp->tm_hour,tmp->tm_min,tmp->tm_sec);
   return buffer;

}
void logmessage(int level, const char*format,...)
{
    
    
   //日志左边部分的实现
   char logLeft[1024];
   std::string level_string=tolevelstring(level);
   std::string curr_time=gettime();
   snprintf(logLeft,sizeof(logLeft),"%s %s %d",level_string.c_str(),curr_time.c_str());

   //日志右边部分的实现
   char logRight[1024]; 
   va_list p;//p可以看作是1字节的指针
   va_start(p,format);//将p指向最开始
   vsnprintf(logRight,sizeof(logRight),format,p);
   va_end(p);//将指针置空
   
   //打印日志 
   printf("%s%s\n",logLeft,logRight);

   //保存到文件中
   FILE*fp=fopen( filename.c_str(),"a");//以追加的方式 将filename文件打开
   //fopen打开失败 返回空指针
   if(fp==nullptr)
   {
    
    
      return;
   }
   fprintf(fp,"%s%s\n",logLeft,logRight);//将对应的信息格式化到流中
   fflush(fp);//刷新缓冲区
   fclose(fp);
}

err.hpp (error message enumeration)

#pragma once 

enum
{
    
    
  USAGE_ERR=1,
  SOCKET_ERR,//2
  BIND_ERR,//3
  LISTEN_ERR,//4
  SETSID_ERR,//5
  OPEN_ERR//6
};


daemon process

The network service must be accessible at any time, so this service cannot be affected by any user's login or logout behavior,
so the process needs to be daemonized

Introduction to PGID SID TTY

run sleep 10000 in the background


PPID is the PID value of bash
PGID is the process group (the same PGID means the same process group, named from the first process)
SID is the session ID
TTY is the terminal If it is ?, it means it has nothing to do with the terminal, if it is specific Such as pts/5, it is a terminal file


Enter in terminal 2, and you can see in terminal 1 that the PGIDs of the two are the same, so they belong to the same process group, and sleep 1000 is used as the group leader


By querying the session ID 21668, it is found that the PID PGUD SID of bash is 21668

How to control the process group in the shell

Query background task jobs


When sleep 5000 is entered again to run in the background, it is found that the previous number becomes 2,
which is the task number


Bring a task to the foreground to run fg + task number

When the No. 1 task is brought to the foreground, use jobs to query the background task again, and the No. 1 task will not be found
, and other tasks will not be affected


Bring the No. 2 task to the foreground, use ctrl z to suspend the service
, after the suspension, the task will automatically switch to the background


Enter bg 2 to let task 2 run in the background

in conclusion

1. The process group is divided into foreground tasks and background tasks

Create a background task and a foreground task in terminal 2, query in terminal 1 and find that the (PGID) process group and (SID) session ID of the background task are the same, but different from the background task


2. If the background task is referred to the foreground, the old task of the day before yesterday cannot be run

Use fg to bring the background task with task number 1 to the foreground, and input commands such as ls pwd will have no effect.
In a session, only one foreground task is running,
so when you use ctrl c to exit task 1, bash puts itself became a foreground task, so it can run again

Why do daemons exist?

If login is to create a session, start the process, there are bash tasks inside the session, and create new front and back tasks in the current session, what if you exit?

When exiting, the session is destroyed which may affect all tasks inside the session

In order not to be affected by user login and logout, the web server usually runs as a daemon process

Creation of the daemon

Type man 2 setsid

Set up a session with the ID of the process group leader as the new session ID

If the return is successful, it returns the PID of the calling process; if it fails, it returns -1 and sets the error code


If you want to call setsid, you cannot be the group leader

For example: You are the team leader in a company, and one day you want to quit and go out to start a business, because you have a lot of team members under your hands, so if you want to
successfully go out and start a business, you must step down as the team leader

Conditions for using daemons

1. Ignore the exception
2. Do special processing for 0 (standard input) 1 (standard output) 2 (standard error)
3. The working path of the process may need to be changed
4. The daemon process is a global process and does not want to be in a certain user's directory, so some files are indexed from the very beginning across the system

Daemonized functions

Enter man daemon to provide daemonized functions

The first parameter indicates whether to change the working directory, do not change it by default, change it to 1 to indicate true
The second parameter indicates whether to close 0 1 2, the default is off

In most cases, implement the daemon process yourself instead of calling this function

Daemonize it yourself

Solve the problem of the team leader

When starting, a new task is created in bash, and only one process forms its own process group, so it becomes the group leader, and the operation is not allowed

The group leader is generally the first process in the group, so just make it not the first process


Enter man fork to create a child process

The return value of fork: the parent process returns the PID value of the child process, the child process returns 0, and the failure returns -1


When fork>0, it means the parent process, then let the parent process exit, leaving only the child process, the child process is not the first of the process, it is not the group leader, you can successfully call setsid

ignore signal

The first parameter of signal represents the signal, and the second parameter represents setting a custom processing action for the signal of the specified action


SIGPIPE indicates signal number 13


SIG_IGN is a custom processing signal processing function

Casting 1 into a function pointer type ignores the signal

Ignore signal 13


The SIGCHLD signal
child process will exit when it is running. If the parent process does not care about the exit of the child process, the child process will become a zombie. The parent process
must use wait/waitpid to wait for the child process to recycle the zombie, and obtain the exit result of the child process,
which is the parent process Blocking waiting (do nothing, just wait for the exit result of the child process)
When the child process is about to exit, it will send a signal SIGCHLD to the parent process

So also ignore the SIGCHLD signal


Dealing with 0 1 2 problems

Use log printing, so there are a lot of output results, but the output results do not want to be printed on the display, so you need to process standard input, standard output, and standard error


The Linux system provides a dev null character device

Writing to dev null will be discarded, and nothing can be read from this file, and it will return immediately


Enter man 2 open to open the file

If the return is successful, the file descriptor will be returned; if the return fails, -1 will be returned and the error code will be returned to
O_RDWR: the way of reading and writing


Redirection function: enter man dup2

You can directly open the file and use dup2 redirection.
The file descriptor corresponding to the output redirection is 1.
Assume that the file descriptor is fd and
newfd is a copy of oldfd. In the end, only
oldfd is left. Redirect to file descriptor fd


exit daemon

Enter kill -9 + the PID of the daemon process to exit the daemon process

full code

err.hpp (error information enumeration)

#pragma once 

enum
{
    
    
  USAGE_ERR=1,
  SOCKET_ERR,//2
  BIND_ERR,//3
  LISTEN_ERR,//4
  SETSID_ERR,//5
  OPEN_ERR//6
};


daemon.hpp (overall implementation)

#pragma once
#include<unistd.h>
#include<signal.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include"log.hpp"
#include"err.hpp"

  void Daemon()//自己实现服务器的守护进程化
  {
    
    
    //1.忽略信号
    signal(SIGPIPE,SIG_IGN);//忽略信号
    signal(SIGCHLD,SIG_IGN);

    //2.不要成为组长    
    if(fork()>0)//说明为父进程,则让父进程直接退出
    {
    
    
       exit(0);
    }
    //只剩下子进程

    //3.新建会话,自己成为会话的话首进程
    pid_t ret=setsid();
    if((int)ret==-1)//守护进程失败
    {
    
    
        logmessage(FATAL,"deamon error,code:%d,string :%s",errno,strerror(errno));
        exit(SETSID_ERR);//终止程序
    }

    //4.可以更改守护进程的工作路径
    

    //5.处理 0 1 2 问题
    int fd=open("/dev/null",O_RDWR);//以读写的方式打开字符设备
    if(fd<0)
    {
    
    
       logmessage(FATAL,"deamon error,code:%d,string :%s",errno,strerror(errno));
        exit(OPEN_ERR);//终止程序
    }   
    //将标准输入 输出错误 重定向到字符设备中
     dup2(fd,0);
     dup2(fd,1);
     dup2(fd,2);
     close(fd);

  } 


Guess you like

Origin blog.csdn.net/qq_62939852/article/details/132429542