Linux C++ 通信 - 守护进程

Linux C++ 通信 - 守护进程

明镜止水,长话短说。


目录

  • Linux C++ 通信 - 守护进程
    • 普通进程
    • 守护进程
    • 守护进程编写

普通进程

写一段简单代码模拟持续运行的程序:

#include
#include
#include
#include
#include

using namespace std;

int main(){
    cout << "程序开始运行!" << endl;

    while (1){
        sleep(1);
        cout << "休眠1s!" << endl;
    }

    return 0;
}

编译执行:

root@ubuntu:/home/qiye# g++ -o t143 /mnt/hgfs/c/t143.cpp
root@ubuntu:/home/qiye# ./t143
程序开始运行!
休眠1s!
休眠1s!
休眠1s!
休眠1s!
ll休眠1s!     #在前台持续运行的程序的终端使用ll命令不会有结果
休眠1s!

进程查看:

root@ubuntu:/home/qiye# ps -eo pid,ppid,sid,tty,pgrp,comm,stat | grep -E 'bash|PID|t143'
    PID    PPID     SID TT          PGRP COMMAND         STAT
   2631    2626    2631 pts/0       2631 bash            Ss
   2700    2699    2631 pts/0       2700 bash            S+
   2988    2987    2988 pts/1       2988 bash            Ss
   3162    3161    2988 pts/1       3162 bash            S
   6974    3162    2988 pts/1       6974 t143            S+

观察发现普通进程的特点:
1.程序进程是有对于终端的,终端一旦退出,对于的进程也会跟着消失。
2.程序的前台运行导致终端无法使用其他命令。

守护进程

为了解决普通进程的弊端,我们需要使用守护进程。

守护进程是一种长期运行的进程,这种进程在后台运行,不与任务终端关联。守护进程特点如下:

  • 生存期长,一般操作系统启动的时候它就启动,操作系统关闭时它就关闭。
  • 守护进程与终端无关,哪个终端的退出都不影响守护进程。
  • 守护进程是在系统后台运行的,不是在终端后台运行的。
#查看开头几个进程
root@ubuntu:/home/qiye# ps -efj
UID          PID    PPID    PGID     SID  C STIME TTY          TIME CMD
root           1       0       1       1  0 04:56 ?        00:00:02 /sbin/init auto noprompt
root           2       0       0       0  0 04:56 ?        00:00:00 [kthreadd]
root           3       2       0       0  0 04:56 ?        00:00:00 [rcu_gp]
root           4       2       0       0  0 04:56 ?        00:00:00 [rcu_par_gp]  
  • PPID为0的时内核进程,随操作系统的启动而启动,生命周期贯穿整个系统,他不与终端挂钩,属于超级用户特权进程。
  • CMD字段里许多带中括号的叫内核守护进程。
  • init进程是系统守护进程,负责启动各运行层次特定的系统服务,它会收留孤儿进程。
  • 还有一些用户级的守护进程,如sshd、rsyslogd、cron等。

守护进程编写

调用umask(0)

umask既是命令,也是个函数,都是用来限制文件权限的,正常使用时,传进去的参数一般为0,目的是为了不让它限制文件权限。

fork出的子进程为守护进程

理由:

  • 进程是通过shell启动的,那么父进程的终止会让shell以为这条命令执行完毕,会把终端空出来,终端就能做其他事。
  • 父进程无法使用setsid函数建立新会话,只能是子进程来调用1setsid函数来成为一个新会话。
  • 子进程调用setsid创建新会话,会拥有一个单独的SID,成为一个新进程组的组长,并且没有所属的终端。

代码编写:

#include
#include
#include
#include
#include
#include
//守护进程编写
int ngx_daemon(){
    int fd;

    switch(fork()){
        case -1:
            //创建子进程失败,这里可以写进日志
            return -1;
        
        case 0:
            //子进程,走到这里直接break
            break;

        default:
            //父进程,直接退出
            exit(0);
    }

    //只有子进程流程才能到这
    if ( setsid() == -1 ){       //脱离终端,关闭终端,将与此子进程无关
        //记录错误日志
        return -1;
    }
    umask(0);                   //设置为0,不要让他来限制文件权限,以免引起混乱

    fd = open("/dev/null", O_RDWR);   //打开黑洞设备(以读写方式打开)
    if ( fd == -1 ){
        //记录错误日志
        return -1;
    }

    if ( dup2( fd, STDIN_FILENO ) == -1 ){        //先关闭STDIN_FILENO(这是规矩,已经打开的描述符,
                                                //改动之前先关闭),类似于指针指向null,让/dev/null成为标准输入
        //记录错误日志
        return -1;
    }

    if ( dup2( fd, STDOUT_FILENO ) == -1 ){       //先关闭STDOUT_FILENO,类似于指针指向null,让/dev/null成为标准输出
        //记录错误日志
        return -1;
    }

    if ( fd > STDERR_FILENO ){
        if ( close(fd) == -1 ){
            //记录错误日志
            return -1;
        }
    }

    return 1;
}

int main(int argc, char * const * argv){
    if ( ngx_daemon() != 1 ){
        //创建守护进程失败,可以做失败后的处理(如写日志等)
        return 1;
    }else{
        //创建守护进程成功,执行守护进程中要做的工作
        for (;;){
            sleep(1);
            printf("休息1s, 进程id=%d!\n", getpid());     // 即使打印也没用,现在标准输出指向黑洞,打印不出任何结果
        }
    }
    return 0;
}

编译运行并查看该进程:

root@ubuntu:/home/qiye# g++ -o t_d /mnt/hgfs/c/nginx/d/d.cpp 
root@ubuntu:/home/qiye# ./t_d 
root@ubuntu:/home/qiye# 

root@ubuntu:/home/qiye# ps -eo pid,ppid,sid,tty,pgrp,comm,stat | grep -E 'PID|t_d'
    PID    PPID     SID TT          PGRP COMMAND         STAT
  76169       1   76169 ?          76169 t_d             Ss

可以发现,现在这个进程的父进程为init,没有所属终端,且没有输出内容,因为内容都输出到黑洞/dev/null中去了。

你可能感兴趣的:(Linux,C++,通信,Linux,linux,c++,运维)