[英]Is there a way to determine the "grandchild" pid_t of a process spawned with "sh -c"?
為了加深我對生成進程和重定向管道的理解,我在下面編寫了popen2()
popen
的函數popen2()
,它返回生成的子進程的pid_t
。
注意: popen2()
的實現通過exec
sh -c cmd
而不是cmd
產生子進程,因為在第二個鏈接問題中有支持這種方法的解釋。
底部的代碼不是很長,但切入正child.out
: a.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.