Linux ptrace(ptrace_ME,…)和后续等待出现问题
我正在将调试器“pi”(“进程检查器”)移植到Linux和am 处理子对象的fork/exec代码以检查它。我是 按照标准程序(我相信),但等待是悬而未决的。 “hang”是执行工作的过程,“cmd”参数为 要跟踪的二进制文件(a.out)的名称:Linux ptrace(ptrace_ME,…)和后续等待出现问题,linux,debugging,wait,ptrace,Linux,Debugging,Wait,Ptrace,我正在将调试器“pi”(“进程检查器”)移植到Linux和am 处理子对象的fork/exec代码以检查它。我是 按照标准程序(我相信),但等待是悬而未决的。 “hang”是执行工作的过程,“cmd”参数为 要跟踪的二进制文件(a.out)的名称: int Hostfunc::hang(char *cmd){ char *argv[10], *cp; int i; Localproc *p; struct exec exec; struct rlimit
int Hostfunc::hang(char *cmd){
char *argv[10], *cp;
int i;
Localproc *p;
struct exec exec;
struct rlimit rlim;
i = strlen(cmd);
if (++i > sizeof(procbuffer)) {
i = sizeof(procbuffer) - 1;
procbuffer[i] = 0;
}
bcopy(cmd, procbuffer, i);
argv[0] = cp = procbuffer;
for(i = 1;;) {
while(*cp && *cp != ' ')
cp++;
if (!*cp) {
argv[i] = 0;
break;
} else {
*cp++ = 0;
while (*cp == ' ')
cp++;
if (*cp)
argv[i++] = cp;
}
}
hangpid = fork();
if (!hangpid){
int fd, nfiles = 20;
if(getrlimit(RLIMIT_NOFILE, &rlim))
nfiles = rlim.rlim_cur;
for( fd = 0; fd < nfiles; ++fd )
close(fd);
open("/dev/null", 2);
dup2(0, 1);
dup2(0, 2);
setpgid(0, 0);
ptrace(PTRACE_TRACEME, 0, 0, 0);
execvp(argv[0], argv);
exit(0);
}
if (hangpid < 0)
return 0;
p = new Localproc;
if (!p) {
kill(9, hangpid);
return 0;
}
p->sigmsk = sigmaskinit();
p->pid = hangpid;
if (!procwait(p, 0)) {
delete p;
return 0;
}
if (p->state.state == UNIX_BREAKED)
p->state.state = UNIX_HALTED;
p->opencnt = 0;
p->next = phead;
phead = p;
return hangpid;
}
“procwait”中的“waitpid”只是挂起。如果我用
上面的代码,并运行一个'ps',我可以看到'pi'已经分叉
但是还没有调用exec,因为命令行仍然是
“pi”,而不是我正在分叉的二进制文件的名称。我发现
如果我删除“raise”,pi仍然挂起,但是现在是“ps”
显示分叉程序具有正在执行的二进制文件的名称
检查,这表明它已经执行了exec
因此,据我所知,我正在遵循文件化的程序
控制一个分叉的进程,但它不工作
Noel Hunt我发现了这个问题(正如Nate所指出的,我自己的代码),但在我运行“strace pi”之前,原因还不清楚。很明显,有一个SIGCHLD处理程序,它正在执行等待。父级进入wait,SIGCHLD被传递,处理程序等待并获取子级的状态,然后在父级中重新启动wait并挂起,因为状态不再发生任何更改。SIGCHLD处理程序是有意义的,因为pi希望被告知子进程中的状态更改。我使用的“pi”的第一个版本是Solaris版本,它使用/proc进行过程控制,因此没有使用“wait”来获取子状态,因此我在Solaris版本中没有看到这个问题。我发现了这个问题(正如Nate指出的,我自己的代码),但在运行“strace pi”之前,原因还不清楚。很明显,有一个SIGCHLD处理程序,它正在执行等待。父级进入wait,SIGCHLD被传递,处理程序等待并获取子级的状态,然后在父级中重新启动wait并挂起,因为状态不再发生任何更改。SIGCHLD处理程序是有意义的,因为pi希望被告知子进程中的状态更改。我使用的第一个“pi”版本是Solaris版本,它使用/proc进行过程控制,因此没有使用“wait”来获取子状态,因此我在Solaris版本中没有看到这个问题。你能把它转换成一个,有人可以编译、运行和测试吗?我没有看到
raise()
在此版本的代码中。注意,waitpid
只会在子进程停止时返回,而就代码而言,我看不到任何会使它停止的东西。你希望它什么时候停止?要回答这个问题,把它变成一个最小的可复制的例子,问题是,非常严格的例子,例如可以在网上找到的,工作。也就是说,父对象分叉,在子对象中它调用'ptrace(ptrace_TRACEME,0,0),然后exec的东西,比如'ls-l'。另一方面,父级执行等待。当等待返回时,父级现在处于控制中,可以设置寄存器等。我必须编写没有“raise()”的版本,但正如我所说,它不会改变结果,除了“raise”之外,子级的argv[]是“pi”本身的名称,这意味着子级没有执行;如果我删除'raise',等待仍然挂起,但是ps显示子对象中的argv现在具有子对象中的程序exec'd的名称。这表明SIGSTOP阻止了forked子级访问exec,但是我已经看到在这个上下文中的网络示例中使用了“raise”。然而,问题在于等待;为什么它没有返回?至于你关于没有看到任何会使它停止的东西的评论,我的理解是PTRACE_TRACEME实际上会导致孩子在第一次执行时停止。你能把它变成一个,有人可以编译、运行和测试吗?我在这个版本的代码中没有看到一个raise()
。注意,waitpid
只会在子进程停止时返回,而就代码而言,我看不到任何会使它停止的东西。你希望它什么时候停止?要回答这个问题,把它变成一个最小的可复制的例子,问题是,非常严格的例子,例如可以在网上找到的,工作。也就是说,父对象分叉,在子对象中它调用'ptrace(ptrace_TRACEME,0,0),然后exec的东西,比如'ls-l'。另一方面,父级执行等待。当等待返回时,父级现在处于控制中,可以设置寄存器等。我必须编写没有“raise()”的版本,但正如我所说,它不会改变结果,除了“raise”之外,子级的argv[]是“pi”本身的名称,这意味着子级没有执行;如果我删除'raise',等待仍然挂起,但是ps显示子对象中的argv现在具有子对象中的程序exec'd的名称。这表明SIGSTOP阻止了forked子级访问exec,但是我已经看到在这个上下文中的网络示例中使用了“raise”。然而,问题在于等待;为什么它没有返回?至于你关于没有看到任何东西会使它停止的评论,我的理解是,PTRACE_TRACEME实际上会导致孩子在第一次执行时停止。
int Hostfunc::procwait(Localproc *p, int flag){
int tstat;
int cursig;
again:
if (p->pid != waitpid(p->pid, &tstat, (flag&WAIT_POLL)? WNOHANG: 0))
return 0;
if (flag & WAIT_DISCARD)
return 1;
if (WIFSTOPPED(tstat)) {
cursig = WSTOPSIG(tstat);
if (cursig == SIGSTOP)
p->state.state = UNIX_HALTED;
else if (cursig == SIGTRAP)
p->state.state = UNIX_BREAKED;
else {
if (p->state.state == UNIX_ACTIVE &&
!(p->sigmsk&bit(cursig))) {
ptrace(PTRACE_CONT, p->pid, 1, cursig, 0);
goto again;
}
else {
p->state.state = UNIX_PENDING;
p->state.code = cursig;
}
}
} else {
p->state.state = UNIX_ERRORED;
p->state.code = WEXITSTATUS(tstat) & 0xFFFF;
}
return 1;
}