有没有办法确定由“ sh -c”生成的进程的“孙子” pid_t?

如何解决有没有办法确定由“ sh -c”生成的进程的“孙子” pid_t?

这个问题来自thisthis

为了增强我对生成过程和重定向管道的理解,我在下面编写了类似popen的函数popen2(),该函数返回了生成的子进程的pid_t

注意:popen2()的实现通过exec ing sh -c cmd而不是cmd产生子进程,原因是在第二个链接的问题。

底部的代码不是很长,但是要切合实际:a.out产生child.outps aux | grep child以便在打印出来之前获得子进程状态的视觉确认它认为是child.out的pid。

the second linked question的评论者指出,通过sh -c生成的进程最终可能是 child grandchild 进程,具体取决于{ {1}}是。
我无意间通过观察主机上的情况(在sh解析为sh的情况下进行了验证,运行/bin/bash显示a.out是作为子进程运行的:

child.out

...而在同一主机上的docker容器中-$ g++ --version && gcc -Wall -Wextra -pedantic -Werror ./main.c && ./a.out g++ (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. p2 stdout: user 3004534 0.0 0.0 4028 732 pts/14 S+ 17:51 0:00 ./child.out user 3004535 0.0 0.0 11176 2932 pts/14 S+ 17:51 0:00 sh -c ps aux | grep child user 3004537 0.0 0.0 12780 968 pts/14 S+ 17:51 0:00 grep child p.pid[3004534] 解析为sh-运行/bin/dash表明a.out作为孙子运行过程:

child.out

我的问题是:在Step 63/63 : RUN ./a.out ---> Running in 7a355740577b p2 stdout: root 7 0.0 0.0 2384 760 ? S 00:55 0:00 sh -c ./child.out root 8 0.0 0.0 2384 760 ? S 00:55 0:00 sh -c ps aux | grep child root 9 0.0 0.0 2132 680 ? S 00:55 0:00 ./child.out root 11 0.0 0.0 3080 880 ? S 00:55 0:00 grep child p.pid[7] 的代码中,是否有一种方法可以抽象实际的命令是否为来获取已执行命令的a.out。子过程”还是孙子过程?

提供一些背景信息:我希望能够杀死pid_t。通过观察,在我的child.out产生子进程和孙进程的环境中,向 child 进程发送popen2()仅杀死了 child 进程,即SIGTERM,但不是我真正想要杀死的孙子流程,即sh -c child.out


代码:

child.out
// main.c
#include <pthread.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

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

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

typedef int Pipe[2];

/** Encapsulates information about a created child process. */
typedef struct popen2_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
} popen2_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.
 */
popen2_t popen2( const char* cmd ) {
  popen2_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 );

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

        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 main( int argc,char* argv[] ) {
  (void)argc;
  (void)argv;
  popen2_t p = popen2( "./child.out" );
  int status = 0;

  {
    char buf[4096] = { '\0' };
    popen2_t p2 = popen2( "ps aux | grep child" );
    waitpid( p2.pid,&status,0 );

    read( p2.stdout[READ_END],buf,sizeof buf );
    printf( "p2 stdout:\n%s\n",buf );
  }

  printf( "p.pid[%d]\n",p.pid );

  {
    pid_t wpid = waitpid( p.pid,0 );

    return wpid == p.pid && WIFEXITED( status ) ? WEXITSTATUS( status ) : -1;
  }
}

解决方法

这比我的薪水高一点,但是由于没有其他答案,我将根据user414777的评论发布最终的工作,并且似乎可以正常工作。

我的方法不是获取子流程的pid_t,而是将子流程设置为流程组负责人。这样,如果我将信号发送到进程组(killpg()),则会影响信号到达孙进程。反映在下面的setpgid()中。

popen2_t popen2( const char* cmd ) {
  popen2_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() ); // This is the relevant change

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

        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;
}

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

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地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-