【问题标题】:popen returns -1 unexpectedlypopen 意外返回 -1
【发布时间】:2019-01-14 21:07:42
【问题描述】:

我有一个程序需要一个最容易由脚本生成的文件列表(除了生成列表之外,它还做了一些可配置的事情)

本质上我是这样做的

fp = popen ("thescript", "r");

然后

while (fgets (buf, 1024, fp))

并处理这些行,最后:

rv = pclose (fp);

(bash) 脚本以 exit 0 结尾。但是当“正常”运行时,pclose 调用返回-1,ECHILD:没有子进程。

我喜欢 Linux 的地方在于,我通常可以通过运行 strace 来发现此类问题并查看实际发生的情况。这次不是:以普通用户身份运行 strace 时,脚本中的挂载失败,因此脚本执行 exit 1rv 反映了这一点。当我将其更改为退出 0(当挂载失败时!)时,返回值 rv 反映了这一点(rv == 0,gui 不打印错误消息)。当我在跟踪时以 root 身份运行整个过程时,它可以工作(rv == 0,没有显示错误消息)。

我编写了简短的测试程序,一切都按预期工作。

起初我写的代码是专有的。决定发布它很简单......这是“故障”的实际代码。 “get_str_param”将返回要运行的脚本的名称。

  files=popen(get_str_param("IMPORT_LIST"),"r");
  //printf ("calling  cmd for file list: %s\n", get_str_param("IMPORT_LIST"));
  while(fgets(buf, 1024, files)) {
    if((p=strstr(buf,".apl"))) {
      *p=0;
      if(strstr(buf," ")) continue; // ignore files with spaces

      fl_add_browser_line(fd_import->applications,buf);
    }
  }
  rv = pclose(files); 
  if (rv) {
    printf ("Can't read file list! rv=%d\n", rv);

“fl_add_browser_line”来自“xforms”库。

那么...只有在不使用 strace 跟踪程序时,什么可能导致 ECHILD?

【问题讨论】:

  • 如果您无法生成minimal reproducible example 来演示问题,并且无法提供实际存在问题的代码,您希望我们在这里做什么?对我们不允许看到的代码进行心理调试?
  • 程序是否有自己的SIGCHLD 处理程序? pclose() 必须使用waitpid() 来获取子进程的退出状态。如果程序中的其他东西已经捕获了退出状态,或者将其声明为SIG_IGN,那么pclose() 将没有任何东西可以检索。
  • strace 也可能正在改变信号处理的时间,导致这个 heisenbug。
  • Barmar:你进入下一轮。 (荷兰语:“你继续洗洗衣机”)您回答了这个问题:“什么可能导致......”,可能正确的答案是:“一个 SIGCHLD 处理程序”。谢谢!我正在搜索暂时禁用 SIGCHLD 的解决方案。
  • @ShadowRanger 如果你想达到 400K 代表,你需要提高你的通灵能力。 :)

标签: c linux popen


【解决方案1】:

它没有显示在发布的代码摘录中,但显然程序的其他部分建立了一个SIGCHLD 处理程序。这会干扰pclose()system() 等功能。 documentation 说:

pclose() 函数等待关联进程终止并返回由wait4(2) 返回的命令的退出状态。

如果有另一个SIGCHLD 处理程序,它调用wait(),那么当pclose() 尝试自己获取退出状态时,子进程将消失,它会得到ECHILD

您需要在运行使用popen()pclose() 的函数时禁用此处理程序。或者编写您自己的代码来运行子进程并与处理分叉的其余代码一起工作。

您需要小心设置signal(SIGCHLD, SIG_DFL) 围绕此代码。如果在此期间有其他子进程退出,我认为您重新建立处理程序时不会通知您的常规处理程序。我认为您可以通过在添加处理程序后显式调用常规处理程序来处理此问题;当它调用wait() 时,它将拾取任何未完成的终止进程。

【讨论】:

  • 添加旧 = 信号 (SIGCHLD, SIG_DFL);之前和信号(SIGCHILD,旧);在 popen .. pclose 部分解决了问题之后。我认为如果另一个孩子在中间时间退出,它甚至不会干扰:pclose 将等待一个特定的 pid,因此其他退出的孩子最终将被信号处理程序收割。
  • 我刚刚添加了一些关于此的内容。如果另一个进程退出,您将永远不会为它们获得 SIGCHLD,因此可能不会有“最终”。您需要显式调用oldchildhandler 以使其检查退出的进程。
猜你喜欢
  • 1970-01-01
  • 2020-04-30
  • 2016-05-06
  • 2015-08-18
  • 1970-01-01
  • 1970-01-01
  • 2020-04-21
  • 2020-12-08
  • 2018-03-24
相关资源
最近更新 更多