为什么除非在popen之后调用被延迟,否则pclose实现为什么会在ECHILD早期返回?

如何解决为什么除非在popen之后调用被延迟,否则pclose实现为什么会在ECHILD早期返回?

我最近想弄清楚如何派生/执行子进程并重定向其stdin,stdout和stderr,以此编写自己的popen()pclose()受到open-source implementation of popen() and pclose()启发的类似函数my_popen()my_pclose()

通过人工检查-例如在另一个终端中运行ps来查找期望的子进程-popen()似乎可以正常工作,因为期望的子进程出现了。

问题:如果我在my_pclose()之后立即调用errno == 10 (ECHILD),为什么my_popen()立即返回?我的期望是my_pclose()会等到子进程结束。

问题:鉴于上述情况,为什么my_pclose()会按预期方式返回-在子进程正常结束之后-如果我在my_popen()和{{之间插入延迟, 1}}?

问题my_pclose()需要进行哪些更正才能仅在子进程结束后才可靠地返回,而无需任何延迟或其他麻烦?>


下面的MCVE。

某些情况:我希望my_pclose()允许用户1)写入子进程“ my_popen(),2)读取子进程” stdin,3)读取子进程进程'stdout,4)知道子进程'stderr,5)在fork / exec'ed进程可能是子进程或子进程的环境中运行,并且能够杀死其中的子进程后者的情况(因此pid_t)。

setpgid()

执行失败的失败:

// main.c

#include <errno.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

typedef int Pipe[2];

typedef enum PipeEnd {
  READ_END  = 0,WRITE_END = 1
} PipeEnd;

#define INVALID_FD (-1)
#define INVALID_PID (0)

typedef struct my_popen_t {
  bool  success;  ///< true if the child process was spawned.
  Pipe  stdin;    ///< parent -> stdin[WRITE_END] -> child's stdin
  Pipe  stdout;   ///< child -> stdout[WRITE_END] -> parent reads stdout[READ_END]
  Pipe  stderr;   ///< child -> stderr[WRITE_END] -> parent reads stderr[READ_END]
  pid_t pid;      ///< child process' pid
} my_popen_t;

/** dup2( p[pe] ) then close and invalidate both ends of p */
static void dupFd( Pipe p,const PipeEnd pe,const int fd ) {
  dup2( p[pe],fd);
  close( p[READ_END] );
  close( p[WRITE_END] );
  p[READ_END] = INVALID_FD;
  p[WRITE_END] = INVALID_FD;
}

/**
 * Redirect a parent-accessible pipe to the child's stdin,and redirect the
 * child's stdout and stderr to parent-accesible pipes.
 */
my_popen_t my_popen( const char* cmd ) {
  my_popen_t r = { false,{ INVALID_FD,INVALID_FD },INVALID_PID };

  if ( -1 == pipe( r.stdin ) ) { goto end; }
  if ( -1 == pipe( r.stdout ) ) { goto end; }
  if ( -1 == pipe( r.stderr ) ) { goto end; }

  switch ( (r.pid = fork()) ) {
    case -1: // Error
      goto end;

    case 0: // Child process
      dupFd( r.stdin,READ_END,STDIN_FILENO );
      dupFd( r.stdout,WRITE_END,STDOUT_FILENO );
      dupFd( r.stderr,STDERR_FILENO );
      setpgid( getpid(),getpid() );

      {
        char* argv[] = { (char*)"sh",(char*)"-c",(char*)cmd,NULL };

        // @todo Research why - as has been pointed out - _exit() should be
        // used here,not exit().
        if ( -1 == execvp( argv[0],argv ) ) { exit(0); }
      }
  }

  // Parent process
  close( r.stdin[READ_END] );
  r.stdin[READ_END] = INVALID_FD;
  close( r.stdout[WRITE_END] );
  r.stdout[WRITE_END] = INVALID_FD;
  close( r.stderr[WRITE_END] );
  r.stderr[WRITE_END] = INVALID_FD;
  r.success = true;

end:
  if ( ! r.success ) {
    if ( INVALID_FD != r.stdin[READ_END] ) { close( r.stdin[READ_END] ); }
    if ( INVALID_FD != r.stdin[WRITE_END] ) { close( r.stdin[WRITE_END] ); }
    if ( INVALID_FD != r.stdout[READ_END] ) { close( r.stdout[READ_END] ); }
    if ( INVALID_FD != r.stdout[WRITE_END] ) { close( r.stdout[WRITE_END] ); }
    if ( INVALID_FD != r.stderr[READ_END] ) { close( r.stderr[READ_END] ); }
    if ( INVALID_FD != r.stderr[WRITE_END] ) { close( r.stderr[WRITE_END] ); }

    r.stdin[READ_END] = r.stdin[WRITE_END] =
      r.stdout[READ_END] = r.stdout[WRITE_END] =
      r.stderr[READ_END] = r.stderr[WRITE_END] = INVALID_FD;
  }

  return r;
}

int my_pclose( my_popen_t* p ) {
  if ( ! p )                    { return -1; }
  if ( ! p->success )           { return -1; }
  if ( INVALID_PID == p->pid )  { return -1; }

  {
    pid_t pid = INVALID_PID;
    int wstatus;

    do {
      pid = waitpid( -1 * (p->pid),&wstatus,0 );
    } while ( -1 == pid && EINTR == errno );

    return ( -1 == pid ? pid : wstatus );
  }
}

int main( int argc,char* argv[] ) {
  my_popen_t p = my_popen( "sleep 3" );
  //sleep( 1 ); // Uncomment this line for my_pclose() success.
  int res = my_pclose( &p );

  printf( "res: %d,errno: %d (%s)\n",res,errno,strerror( errno ) );

  return 0;
}

参考:123


更新
This link让我想知道$ gcc --version && gcc -g ./main.c && ./a.out gcc (Debian 6.3.0-18+deb9u1) 6.3.0 20170516 Copyright (C) 2016 Free Software Foundation,Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. res: -1,errno: 10 (No child processes) 之后在父进程中添加setpgid( pid,0 )是否相关。这样做似乎是可行的,因为添加后,在fork()之后立即调用my_pclose()似乎要等到该过程完成。

老实说,我不太明白为什么会有所不同。如果一个知识渊博的社区成员能够提供见解,我将不胜感激。

my_popen()

解决方法

my_pclose()的问题在于您试图执行进程组等待而不是等待特定的子进程。这个:

      pid = waitpid( -1 * (p->pid),&wstatus,0 );

试图等待属于进程组p->pid的孩子,但是如果没有您以后添加的setpgid()调用,这是极不可能的。分叉的子代最初将与其父代位于同一个进程组中,并且该组的进程组号几乎可以肯定与该子进程的进程号不同。

此外,目前尚不清楚为什么首先要等待流程组。您知道要等待的特定进程,而my_pclose()收集另一个进程是不正确的,无论它是否属于同一进程组。您应该等待该特定过程:

      pid = waitpid(p->pid,0 );

无论有没有setpgid()调用都可以使用,但是几乎可以肯定,您应该在这样的通用函数中忽略该调用。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)&gt; insert overwrite table dwd_trade_cart_add_inc &gt; select data.id, &gt; data.user_id, &gt; data.course_id, &gt; date_format(
错误1 hive (edu)&gt; insert into huanhuan values(1,&#39;haoge&#39;); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive&gt; show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 &lt;configuration&gt; &lt;property&gt; &lt;name&gt;yarn.nodemanager.res