【问题标题】:read() output from execl command and print it using only write() syscallexecl 命令的 read() 输出并仅使用 write() 系统调用打印它
【发布时间】:2020-03-03 08:40:01
【问题描述】:

我正在尝试使用 execl() 在 tar 文件上执行 /bin/tar -tf。我希望它读取其内容,并仅在我的 main() 函数中使用 write() 系统调用将其打印到终端屏幕。

我下面的代码以某种方式读取内容并将其同时写入终端,而无需求助于 write() 系统调用。

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>

int pfd[2];

// Problem 1:
// ERROR : read() function reads and writes to the terminal.
// I would like to write the contents of the 'buffer' variable with the
// write(fd, buffer, strlen(buffer)) syscall to the terminal preferably
// in the main() function.
// Problem 2:
// is the 'status' variable along with the _exit function useful here?
// I am not sure if a pipe is needed?
int show_tar(pid_t *pid, char *buffer, int *bytes_read, char **tar_name)
{
    int status = 0;
    pipe(pfd);
    if ((*pid = fork()) == 0) {
        dup2(0, pfd[0]);
        execl("/bin/tar", "tar", "-tf", tar_name[1], (char *)NULL);
    } else {
        close(pfd[1]);
        *bytes_read = read(pfd[0], buffer, sizeof(buffer));
        wait(NULL);
    }
    _exit(status);
    return status;
}

int main(int argc, char **argv)
{
    char buffer[1024];
    int bytes_read = 0;
    pid_t pid;
    int status = show_tar(&pid, buffer, &bytes_read, argv);
    wait(&status);
    // write contents of 'buffer' to the terminal with the
    // write(fd, buffer, strlen(buffer)) function like so:
    // int ch;
    // while (1) {
    //     if ((ch = getchar()) == 27) // Press <Escape> to quit program
    //         break;
    //     else  
    //         write(1, buffer, strlen(buffer));
    // }
    return 0;
}

在 Linux 上编译并执行,argv[1] 是 tarball:

gcc buffer.c -o buffer && ./buffer "$HOME/<your tarball>.tar.gz"

输出显示了 tarball 的全部内容,而无需求助于 write() 系统调用。

【问题讨论】:

  • tar 写信给stdout
  • 您没有在孩子中关闭足够的文件描述符。 经验法则:如果您将管道的一端dup2() 连接到标准输入或标准输出,请尽快关闭pipe() 的两个原始文件描述符。特别是,这意味着在使用任何exec*() 系列函数之前。该规则也适用于dup()fcntl()F_DUPFD
  • 还要确保在 exec*() 系列函数之一返回时处理错误情况。它只在错误时返回;无需测试返回值。但是您通常应该至少以非零状态退出;通常,您应该写一条关于标准错误失败的消息,然后退出。是使用exit() 还是_exit() 可能会令人担忧——使用_exit() 通常(通常)没问题,而使用exit() 有时是个坏主意。

标签: c fork posix pid


【解决方案1】:

编辑:这需要大量的工作,但它仍然不存在,但这应该让你开始。

我认为您的管道有点混乱:它们是单向通道,pfd[1] 上写的任何内容都可以在 pfd[0] 上读取,因此它们没有正确使用管道。

父母和孩子应该始终关闭他们不使用的管道的“另一端”,并且您必须安排写入管道实际上是数字 1 (stdout)。

int show_tar(pid_t *pid, char *buffer, int *bytes_read, char **tar_name)
{
    int status = 0;
    int buffer_size = *bytes_read;  // pass IN the buffer size
    int pfd[2];
    pipe(pfd);
    if ((*pid = fork()) == 0) {
        close(pfd[0]);  // child doesn't need read pipe
        dup2(pfd[1], 1); // insure write pipe is at stdout (fd#1)
        dup2(pfd[1], 2); // stderr goes to the pipe also (optional)
        close(pfd[1]);  // child doesn't need write pipe any more
        execl("/bin/tar", "tar", "-tf", tar_name[1], (char *)NULL);
        _exit(1);
    } else {
        close(pfd[1]);  // parent doesn't need write pipe
        *bytes_read = read(pfd[0], buffer, buffer_size);

        // insure NUL byte at the end
        if (*bytes_read >= 0) buffer[*bytes_read] = 0;

        wait(NULL);
    }
//  _exit(status);    // we don't need this!
    return status;
}

int main(int argc, char **argv)
{
    char buffer[1024];
    int bytes_read = sizeof buffer;
    pid_t pid;

    int status = show_tar(&pid, buffer, &bytes_read, argv);
    wait(&status);

    // print the value here
    return 0;
}

这有点过分简化了管道描述符管理,因为如果在 stdin/stdout 已经关闭的环境中运行,那么管道可能实际上使用 fd#0 和 fd#1。

最后,您有时必须通过检查文件描述符以查看它们是否已经是您想要的,并在冲突中duping 来做一些棘手的事情。但如果从终端会话运行,这很好开始。

另外:请注意标准错误流 (fd#2) 仍连接到原始终端,因此此机制不会捕获来自 tar 的错误消息等内容。同样,更棘手的管道描述符管理。

编辑:我刚刚注意到show_tar() 中的所有路径都流经_exit(),所以它永远不会返回。

编辑:将int pfd[2]; 的定义移动到内部 show_tar() - 它不需要在文件范围内。

编辑:刚刚意识到show_tar() 中的sizeof(buffer) 没有按照您的想法进行;指针的大小始终为 4 或 8(取决于平台),而不是可用的字节数 - 这必须作为参数传递。我重载了*bytes_read 参数以传递in可用的字节数,而您已经在使用它来传递返回读取的数字。

父级还必须循环,从管道读取字节,直到它到达文件结尾,因为来自tar 的所有内容不太可能在一次读取中进行。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2014-02-22
  • 1970-01-01
  • 1970-01-01
  • 2011-04-21
  • 1970-01-01
  • 2011-02-04
  • 2013-05-21
  • 1970-01-01
相关资源
最近更新 更多