暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Windows下如何让命令行程序在后台运行

生有可恋 2022-09-09
17528

在Linux下有守护进程的概念,即程序执行后在后台运行。许多服务端程序都是守护进程,不需要在命令行进行交互。这种程序的行为在Windows下也是类似的。

首先我们观察一下在前台进行的程序在进程树中是什么样的。这里写了一个测试用的命令行程序rest.exe,它是作用就是死循环模拟一些需要长时间运行的服务,当在命令行执行该程序时,我们在procExp.exe中对其进行观察:

    Microsoft Windows [版本 10.0.19041.208]
    (c) 2020 Microsoft Corporation. 保留所有权利。


    C:\Users\Administrator>cd /d d:\
    d:\>rest.exe

    在procExp.exe中,可以看到rest.exe进程之上还有一个conhost.exe父进程,其中conhost.exe是console host process的意思,是微软在win7之后引入的,起到安全隔离的作用。所有命令行程序都会启动一个conhost.exe的进程作为父进程。此时rest.exe进程是在前台运行的。

    如果想让rest.exe进程在后台运行,此时可以调用 start b 让其在后台运行:

      start b rest.exe

      此时可以看到 rest.exe 进程已经在后台进行了,不再占用命令行的位置,可以在命令行中执行其它命令,比如 ping 命令。我们再在后台看下进程树有什么变化:

      可以看到 rest.exe 的进程仍然是 conhost.exe,如果此时把 cmd.exe 的窗口叉掉,cmd.exe 的所有子进程都会死掉,包括在后台运行的 rest.exe 进程。此时使用 start b 启动的程序还不是守护进程,要想让进程变成守护进程还需要额外多做一步。在讲这一步之前,我们需要了解Linux下的守护进程是怎么写的。

      Linux下的守护进程是剥离进程组的会话,如果想让一个进程变成守护进程,要在它的子进程中把父进程干掉。一般标准的守护进程的程序是这样写的:

        void daemon() {
        // fork 出一个子进程。
        pid_t pid = fork();


        // fork 失败。
        if( pid == -1) {
        perror("fork");
        exit(1);
        }


        // 退出父进程。
        if(pid) {
        exit(0);
        }


        // 创建新会话。
        // 该子进程会成为新的会话和进程组的组长。
        if(setsid() == -1) {
        perror("setsid");
        exit(1);
        }


        // 再 fork 一次,新的子进程不再是会话组长。
        pid = fork();


        if( pid == -1) {
        perror("fork");
        exit(1);
        }


        if(pid) {
        exit(0);
        }

        // 关掉从父进程继承的文件描述符。
        int max_fd = sysconf(_SC_OPEN_MAX);
        for(int i = 0; i < max_fd; ++i) {
        close(i);
        }


        // 重定向文件描述符 0, 1, 2 到 dev/null
        open("/dev/null", O_RDWR);
        dup(0);
        dup(0);


        // 设置文件创建权限掩码,不希望被父进程的掩码限制。
        umask(0);


        // 将当前工作目录设置为系统根目录。
        chdir("/");}

        当主动结束掉父进程,那么守护进程的父进程就会继承1号进程作为父进程。而在Windows下命令行下的程序的父进程是conhost.exe,此时如果把conhost.exe进程结束掉,那么其下的子进程就会变成守护进程,即后台进程。

          tasklist | findstr conhost.exe
          taskkill /im conhost.exe /f

          执行完上面的杀进程的命令后,所有 conhost.exe 进程都被杀掉了,同时消失掉的还是命令行窗口。此时已经看不到命令行程序,但在任务管理器中可以看到,我们还是在procExp.exe中查看,因为可以看到进程树。

          从 procExp.exe 中还可以看到 rest.exe 和 ping.exe 程序还在运行,命令行窗口已经找不到了。以上方法比较暴力,如果试过在 procExp.exe 中结束掉 conhost.exe 进程,可以看到 rest.exe 进程会成为独立的进程,没有父进程,此时也是处于后台在运行。以上通过结束 conhost.exe 的方式比较粗暴,还有一种方式可以让命令行程序成为后台进程,也是通过结束父进程的方式,是以脚本方式运行的,脚本如下:

            @ECHO OFF
            %1 start mshta vbscript:createobject("wscript.shell").run("""%~0"" ::",0)(window.close)&&exit
            start /b d:\rest.exe

            把命令包一层,调用vbscript杀父进程。把脚本保存为 test.bat,执行后 rest.exe 变成了独立的进程,同时 conhost.exe 进程也保留了下来。如果在系统中有很多 conhost.exe 进程说明也有其它后台服务是这么干的。

            参考:

            • https://cloud.tencent.com/developer/article/1635805

            • https://blog.csdn.net/surfirst/article/details/113123446


            全文完。

            如果转发本文,文末务必注明:“转自微信公众号:生有可恋”。

                   
            文章转载自生有可恋,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

            评论