c - 使用 pipeline() 和 execvp() 杀死我的程序

标签 c shell

当我运行这段代码时,我的程序被终止,但我似乎不明白为什么。

int pipefd[2];
pipe(pipefd);
pid = fork();

if(pid == 0){                      //child process, receives data from pipe for execution   
  close(0);
  dup(pipefd[0]);                       
  close(pipefd[1]);
  execvp(pipeCom2[0], pipeCom2);
}
else if (pid > 1){                //parent process writes data to pipe for execution 
  close(1);
  dup(pipefd[1]);                                      
  close(pipefd[0]);
  execvp(pipeCom1[0], pipeCom1);
  waitpid(pid, &status, WUNTRACED);
}

我一直在使用 cat project1.c | 进行测试grep include 命令。它正确执行并显示我的程序中的所有包含语句。

所以 pipelineCom1 包含 {"cat", "project1.c"}

并且 pipelineCom2 包含 {"grep", "include"}

在程序的前面,我使用此代码覆盖输出,并在程序结束时恢复正常。因此,当进程被终止时,输出会变得困惑。

newConfig.c_lflag &= ~(ICANON| ECHOE | ECHO);
tcsetattr(0, TCSANOW, &newConfig);

最佳答案

将评论转换为答案。

关闭管道

您没有关闭足够的管道文件描述符。如果将描述符复制到标准输入或输出,则应关闭两个管道描述符。

Which other pipe file descriptors should I be closing?

你有:

int pipefd[2];
pipe(pipefd);
pid = fork();

if (pid == 0)                      //child process, receives data from pipe for execution
{   
    close(0);
    dup(pipefd[0]);                       
    close(pipefd[1]);
    execvp(pipeCom2[0], pipeCom2);
}
else if (pid > 1)                 //parent process writes data to pipe for execution
{
    close(1);
    dup(pipefd[1]);                                      
    close(pipefd[0]);
    execvp(pipeCom1[0], pipeCom1);
    waitpid(pid, &status, WUNTRACED);
}

调用 dup() 后,您需要关闭两组代码中的 pipefd[0]pipefd[1]。您也可以使用 dup2() 代替 dup() ;这通常更简单。

在这个带有试用管道的程序中,很有可能在不添加缺少的 close() 操作的情况下一切都会好起来。然而,一般来说,关闭不需要的管道描述符是至关重要的,因为打开的描述符意味着进程不会得到它期望的 EOF,或者在应该得到 SIGPIPE 信号或写入错误时不会得到,因为进程的错误管道末端仍然打开。

父级调用execvp()

此外,父进程包含一个 execvp() 调用;以下代码仅在 exec 失败时执行。当进程运行 execvp()(或任何其他 exec*() 函数)时,仅当执行操作失败时,调用才会返回。

如果您需要重置终端,请在其中一个 exec 调用之前执行此操作,或者创建两个子级并让父级关闭其所有管道描述符并等待子级完成(死亡),然后再重置终端。考虑信号处理。在执行其他程序之前重置终端会更简单。

Earlier in the program I override output with this code and return to normal at the end of my program. So when the process is killed output is messed up.

newConfig.c_lflag &= ~(ICANON| ECHOE | ECHO);
tcsetattr(0, TCSANOW, &newConfig);

假设您有一个带有 tcgetattr() 输出的 oldConfig,您应该这样做:

  tcsetattr(0, TCSANOW, &oldConfig);

在父进程运行execvp()之前。

以空结尾的命令参数列表

你说:

pipeCom1 contains {"cat", "project1.c"}

and pipeCom2 contains {"grep", "include"}

pipeCom1pipeCom2 都具有以空(指针)结尾的参数字符串列表,这对于正确操作至关重要。例如,您可以使用如下所示的硬连线参数列表:

char *pipeCom1[] = { "cat", "project1.c", 0 };
char *pipeCom2[] = { "grep", "include", 0 };

如果您愿意,可以用 NULL 代替 0。

execvp()失败时报告错误并退出

您需要考虑 execvp() 失败后会发生什么。正确的答案很少是“继续,就像没有出问题一样”(这就是您的代码所做的)。

您应该考虑向标准错误报告错误。通常,您应该以失败状态退出,使用 exit(EXIT_FAILURE);_exit(EXIT_FAILURE); 或这些的一些相关项。

汇总所有这些更改

#include <stdio.h>
#include <termios.h>
#include <stdlib.h>
#include <unistd.h>

int main(void)
{
    struct termios oldConfig;
    struct termios newConfig;
    tcgetattr(0, &oldConfig);           // Error check omitted
    newConfig = oldConfig;
    newConfig.c_lflag &= ~(ICANON| ECHOE | ECHO);
    tcsetattr(0, TCSANOW, &newConfig);  // Error check omitted

    /* Do stuff without echoing enabled */

    /* Now do some execution */

    char *pipeCom1[] = { "cat", "project1.c", 0 };
    char *pipeCom2[] = { "grep", "include", 0 };

    int pipefd[2];                      // Error check omitted
    pipe(pipefd);
    pid_t pid = fork();

    if (pid == 0)                       //child process
    {   
        close(0);
        dup(pipefd[0]);                       
        close(pipefd[1]);
        close(pipefd[0]);
        execvp(pipeCom2[0], pipeCom2);
        perror(pipeCom2[0]);
        exit(EXIT_FAILURE);
    }
    else if (pid > 1)                   //parent process
    {
        close(1);
        dup(pipefd[1]);                                      
        close(pipefd[0]);
        close(pipefd[1]);
        tcsetattr(0, TCSANOW, &oldConfig);
        execvp(pipeCom1[0], pipeCom1);
        perror(pipeCom1[0]);
        exit(EXIT_FAILURE);
    }
}

示例运行

假设 project1.c 是上面源代码的副本,则命令的输出为:

#include <stdio.h>
#include <termios.h>
#include <stdlib.h>
#include <unistd.h>
    char *pipeCom2[] = { "grep", "include", 0 };

进程结束后终端处于正确模式。

关于c - 使用 pipeline() 和 execvp() 杀死我的程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39715220/

相关文章:

c - 获取数组输入时出现段错误

c - 找到无法偿还贷款的确切金额(避免无限循环)

swift - 从 Xcode 按钮运行 Shell 脚本

c - 在数组初始值设定项列表中提供的初始值设定项是否多于数组中的元素是语法错误吗?

c - 如何解决打开文件堆错误

c - 如何在 64 位 Windows 上用 C 代码发出 0x2D 中断?

bash - 如何在 Shell 脚本中为 "for"语句使用 2 个变量

java - 导入mysql转储文件的shell脚本

linux - 在 shell 命令中格式化十进制

linux - 使用 grep 仅打印匹配项的一部分