簡體   English   中英

有沒有辦法確定用“sh -c”生成的進程的“孫子”pid_t?

[英]Is there a way to determine the "grandchild" pid_t of a process spawned with "sh -c"?

這個問題來自thisthis

為了加深我對生成進程和重定向管道的理解,我在下面編寫了popen2() popen的函數popen2() ,它返回生成的子進程的pid_t

注意: popen2()的實現通過exec sh -c cmd而不是cmd產生子進程,因為在第二個鏈接問題中有支持這種方法的解釋。

底部的代碼不是很長,但切入正child.outa.out產生child.out以及ps aux | grep child ps aux | grep child在打印出它認為是child.out的 pid 之前對子進程的統計信息進行視覺確認。

第二個鏈接問題的評論者指出,通過sh -c產生的進程可能最終成為進程或進程,具體取決於sh是什么。
我通過觀察無意中驗證了這一點,我的主機上-在sh解析為/bin/bash -運行a.out顯示, child.out運行作為一個子進程:

$ 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]

...而在同一主機上的 docker 容器中 - 其中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 通過觀察,在我的popen2()產生子進程和孫進程的環境中,向進程發送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_FD, INVALID_FD },
    { 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, WRITE_END, 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, &status, 0 );

    return wpid == p.pid && WIFEXITED( status ) ? WEXITSTATUS( status ) : -1;
  }
}
// child.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main( int argc, char* argv[] ) {
  char buf[128] = { '\0' };

  snprintf( buf, sizeof buf, "%s:%d\n", __FILE__, __LINE__ );
  write( STDOUT_FILENO, buf, strlen( buf ) );
  sleep( 1 );

  snprintf( buf, sizeof buf, "%s:%d\n", __FILE__, __LINE__ );
  write( STDOUT_FILENO, buf, strlen( buf ) );
  sleep( 1 );

  snprintf( buf, sizeof buf, "%s:%d\n", __FILE__, __LINE__ );
  write( STDOUT_FILENO, buf, strlen( buf ) );
  sleep( 1 );

  snprintf( buf, sizeof buf, "%s:%d\n", __FILE__, __LINE__ );
  write( STDOUT_FILENO, buf, strlen( buf ) );
  sleep( 1 );
  return 0;
}

這略高於我的工資等級,但由於沒有任何其他答案,我將發布我最終所做的事情,這是基於 user414777 的評論,並且似乎有效。

我的方法不是獲取孫子進程的pid_t ,而是將子進程設置為進程組長。 通過這樣做,如果我向進程組( killpg() )發送一個信號,就會產生信號到達孫子進程的效果。 這反映在下面添加的setpgid()中。

popen2_t popen2( const char* cmd ) {
  popen2_t r = { false,
    { INVALID_FD, INVALID_FD },
    { INVALID_FD, INVALID_FD },
    { 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, WRITE_END, 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;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM