【Linux网络编程】日志与守护进程

日志是网络服务器程序在后台以守护进程的形式运行时,处理情况的描述被打印到了日志文件里面,方便维护人员查看。

1.前台进程与后台进程

左边会话输入命令 sleep 10000 & 代表进程后台运行,右边会话输入命令 sleep 20000可以看到命令行解释器直接卡住了。

STAT S+就是前台进程的意思,STAT S就是后台进程。可以看到后台运行的进程并不影响命令行解释器bash进程的运行,而前台进程sleep 20000运行的时候,bash进程无法运行了。所以能得出结论,一个会话中只能有一个前台进程。

2. 会话与终端

PPID:父进程ID

PID:进程ID

PGID:进程组ID

SID:会话

TTY:终端

会话是一个进程组(任务)的集合。在xshell模拟的Linux终端上,一个会话在被打开的时候就会启动一个bash进程,bash进程是用于对用户输入的命令进行分析解释并返回结果到该会话的终端文件TTY中的。那么bash进程自然就是该会话的第一个进程。可以看到上图红色标记,COMMAND-sleep 10000与COMMAND - -bash的SID都是2917,而bash的PID和PGID也都是2917。这是什么意思呢?一个会话中第一个进程的PID就会作为SID,同理一个进程组中第一个进程PID就是进程组的PGID,一个进程组至少有一个进程。

可以看到我们把hello world重定向到了/dev/pts/4这个文件下,结果在右边的会话中显示出来了。这是为什么呢?在/dev/pts/路径下存在着各个会话的终端文件,在A会话中输入命令,bash进程对命令进行解释后返回的结果就默认输出到A会话的终端文件。

3.前台任务与后台任务切换

jobs 查看当前会话进程组

可以看到每个进程组(任务)前面都有[1] [2]这样的任务编号。

fg 后台任务切换成前台任务

ctrl+z 暂停前台任务

bg 在后台运行任务

4.为什么要创建守护进程?

创建一个会话就会可以创建新的前后台任务,那么销毁会话可能就会影响会话内的所有任务。所以一般网络服务器为了不受到用户的登录注销的影响都是以守护进程的方式运行。

守护进程就是为了让任务不受所在会话的注销的影响,为该进程可以创建一个新的会话。

cpp 复制代码
 #include <unistd.h>

       pid_t setsid(void);

setsid() 创建一个新会话,如果调用进程不是进程组组长。调用进程是新会话的领导者,也是新进程组的组长,并且没有控制终端。 这调用进程的进程组 PGID 和会话 SID 被设置为 调用进程的PID。

进程pcb里面是会包含当前进程的工作路径的,这也就是我们在open一个文件的时候,如果没有就创建,文件就会在当前路径下的原因。

/dev/null是一个"黑洞"文件,不能从中读取任何数据也不能向其写入任何数据。

5.如何创建守护进程?

cpp 复制代码
#pragma once
// 1. setsid();
// 2. setsid(), 调用进程,不能是组长!我们怎么保证自己不是组长呢?
// 3. 守护进程a. 忽略异常信号 b. 0,1,2要做特殊处理 c. 进程的工作路径可能要更改 /
#include <signal.h>
#include <unistd.h>
#include <cstdlib>

#include "log.hpp"
#include "err.hpp"
#include <cerrno>
#include <cstring>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

void Daemon()
{

    // 忽略信号
    signal(SIGCHLD, SIG_IGN);
    signal(SIGPIPE, SIG_IGN);

    // 让子进程去setsid
    if (fork() > 0)
        exit(0);

    // 为当前进程组(任务)创建新的会话;
    pid_t ret = setsid();
    if ((int)ret == -1)
    {

        logMessage(Fatal, "setsid error,code:%d ,string: %s\n", errno, strerror(errno));
        exit(SETSID_ERR);
    }

    // 更改工作路径到根目录
    // chdir("/");

    //0,1,2做特殊处理
    int fd = open("/dev/null",O_RDWR);//可读可写方式打开黑洞文件
    if(fd<0)
    {
        logMessage(Fatal, "open error,code:%d ,string: %s\n", errno, strerror(errno));
        exit(OPEN_ERR);
    }
    dup2(fd,0);
    dup2(fd,1);
    dup2(fd,2);
    close(fd);

}

由于进程组组长长不能单起一个会话的原则,所以创建子进程作为守护进程,守护进程是一个孤儿进程。

其实也可以直接用Unix标准库函数daemon函数创建守护进程:

NAME

daemon - run in the background

SYNOPSIS

#include <unistd.h>

int daemon(int nochdir, int noclose);

当nochdir为0时,daemon将更改进程的工作路径为根目录。

当noclose为0时,daemon将进城的STDIN, STDOUT, STDERR都重定向到/dev/null。一般情况下,这个参数都是设为0的。

相关推荐
LH_R14 小时前
OneTerm开源堡垒机实战(四):访问授权与安全管控
运维·后端·安全
用户311879455921814 小时前
Kylin Linux 10 安装 glib2-devel-2.62.5-7.ky10.x86_64.rpm 方法(附安装包)
linux
Raymond运维14 小时前
MariaDB源码编译安装(二)
运维·数据库·mariadb
涛啊涛15 小时前
Centos7非LVM根分区容量不足后扩容,对调硬盘挂载/
linux·磁盘管理
JuiceFS1 天前
从 MLPerf Storage v2.0 看 AI 训练中的存储性能与扩展能力
运维·后端
CYRUS_STUDIO1 天前
用 Frida 控制 Android 线程:kill 命令、挂起与恢复全解析
android·linux·逆向
熊猫李1 天前
rootfs-根文件系统详解
linux
chen9452 天前
mysql 3节点mgr集群部署
运维·后端
LH_R2 天前
OneTerm开源堡垒机实战(三):功能扩展与效率提升
运维·后端·安全
dessler2 天前
Hadoop HDFS-高可用集群部署
linux·运维·hdfs