代码之家  ›  专栏  ›  技术社区  ›  Rick Jim DeLaHunt

若并没有终端连接到服务器,那个么标准的输入和输出是什么?

  •  0
  • Rick Jim DeLaHunt  · 技术社区  · 4 年前

    昨天,当我思考服务器日志记录的方法时,我想到了这个问题。

    通常,我们打开一个连接到本地计算机或远程服务器的终端,运行一个可执行文件,然后打印( printf , cout )终端中的一些调试/日志信息。

    但是,对于那些在服务器上运行但未连接到终端的进程/可执行文件/脚本,标准的输入和输出是什么?

    例如:

    1. 假设我有一个 crontab 任务,每天在服务器上多次运行程序。如果我写下 cout << "blablabla" << endl; 在节目中。会发生什么?这些产出将流入哪里?

    2. 我想知道的另一个例子是,如果我写一个CGI程序(使用 C C++ )对于Apache web服务器,我的CGI程序的标准输入和输出是什么?(据此) C++ CGI tutorial ,我猜CGI程序的标准输入和输出在某种程度上被重定向到Apache服务器。因为它正在使用 库特 输出html内容,而不是通过 return . )

    我读过这个 What is “standard input”? 在询问之前,谁告诉我标准输入不必绑定到键盘,而标准输出不必绑定到终端/控制台/屏幕。

    操作系统是Linux。

    0 回复  |  直到 4 年前
        1
  •  2
  •   Miles Budnek    4 年前

    标准输入和标准输出(以及标准错误)流基本上可以指向任何I/O设备。这通常是一个终端,但也可以是一个文件、一个管道、一个网络套接字、一台打印机等。这些流将其I/O定向到什么,通常由启动进程的进程决定,可以是shell或像cron或apache这样的守护进程,但进程可以根据自己的意愿重定向这些流。

    我将以Linux为例,但在大多数其他操作系统上,这些概念是相似的。在Linux上,标准输入和标准输出流由文件描述符表示 0 1 .宏 STDIN_FILENO STDOUT_FILENO 只是为了方便和清晰。文件描述符只是一个数字,它与操作系统内核维护的一些文件描述相匹配,这些文件描述告诉它如何写入该设备。这意味着,从用户空间进程的角度来看,你几乎可以用同样的方式编写任何东西: write(some_file_descriptor, some_string, some_string_length) (更高级别的I/O功能,如 printf cout 只是一个或多个呼叫的包装 write ).对于过程来说,什么类型的设备无关紧要 some_file_descriptor 代表。操作系统内核将为您解决这个问题,并将数据传递给相应的设备驱动程序。


    启动新流程的标准方法是致电 fork 复制父进程,然后稍后调用 exec 子进程中开始执行某些新程序的函数族。在这两者之间,它通常会关闭从父进程继承的标准流,并打开新的流,以将子进程的输出重定向到新的位置。例如,要让子系统将其输出反馈给父系统,可以在C++中执行类似的操作:

    int main()
    {
        // create a pipe for the child process to use for its
        // standard output stream
        int pipefds[2];
        pipe(pipefds);
    
        // spawn a child process that's a copy of this process
        pid_t pid = fork();
        if (pid == 0)
        {
            // we're now in the child process
    
            // we won't be reading from this pipe, so close its read end
            close(pipefds[0]);
            // we won't be reading anything
            close(STDIN_FILENO);
            // close the stdout stream we inherited from our parent
            close(STDOUT_FILENO);
            // make stdout's file descriptor refer to the write end of our pipe
            dup2(pipefds[1], STDOUT_FILENO);
            // we don't need the old file descriptor anymore.
            // stdout points to this pipe now
            close(pipefds[1]);
            // replace this process's code with another program
            execlp("ls", "ls", nullptr);
        } else {
            // we're still in the parent process
    
            // we won't be writing to this pipe, so close its write end
            close(pipefds[1]);
    
            // now we can read from the pipe that the
            // child is using for its standard output stream
            std::string read_from_child;
            ssize_t count;
            constexpr size_t BUF_SIZE = 100;
            char buf[BUF_SIZE];
            while((count = read(pipefds[0], buf, BUF_SIZE)) > 0) {
                std::cout << "Read " << count << " bytes from child process\n";
                read_from_child.append(buf, count);
            }
    
            std::cout << "Read output from child:\n" << read_from_child << '\n';
            return EXIT_SUCCESS;
        }
    }
    

    注意:为了清晰起见,我省略了错误处理

    本例创建一个子进程并将其输出重定向到管道。该程序在子进程中运行( ls )可以像对待引用终端一样对待标准输出流(不过 ls 如果检测到其标准输出不是终端,则会更改某些行为)。


    这种重定向也可以从终端完成。运行命令时,可以使用重定向操作符告诉shell将命令标准流重定向到终端以外的其他位置。例如,这里有一种复杂的方法,可以使用类似sh的shell将文件从一台机器复制到另一台机器:

    gzip < some_file | ssh some_server 'zcat > some_file'
    

    这将实现以下功能:

    1. 创建一个管道
    2. gzip 重定向其标准输入流以从“some_file”读取,并重定向其标准输出流以写入管道
    3. ssh 并重定向其标准输入流以从管道中读取
    4. 在服务器上,运行 zcat 它的标准输入重定向自ssh连接读取的数据,标准输出重定向至写入“some_文件”