【发布时间】:2021-07-19 13:21:29
【问题描述】:
我正在编写一些必须与 gdb mi 通信的代码。所以我分叉了我的程序,放置了两个管道并在孩子中启动了 gdb mi,这样我就可以与父母的 gdb mi 进行通信。 gdb 完成后总是返回 "(gdb) \n",所以我寻找它并编写我的下一个命令。这是我的代码的最小示例:
int main(){
printf("Starting Test\n");
int fromGDB[2], toGDB[2], nbytes;
pid_t childpid;
char readbuffer[80] = "";
pipe(fromGDB);
pipe(toGDB);
if((childpid = fork())==-1)
{
perror("fork");
exit(1);
}
if(childpid == 0){
close(toGDB[1]);
close(fromGDB[0]);
int backup = dup(1); //store stdout
if (dup2(fromGDB[1],1) < 0){puts("hat nicht geklappt");}
int backupStdin = dup(0); //store stdin
if (dup2(toGDB[0],0) < 0){puts("hat nicht geklappt");}
system("gdb -q --interpreter=mi2"); // start gdb mi
dup2(backup,1); // restore stdout
puts("child fertig");
exit(0);
}else{
close(toGDB[0]);
close(fromGDB[1]);
char* writeCommand = "";
int commandCounter = 0;
while (commandCounter <3){
nbytes = read(fromGDB[0],readbuffer,sizeof(readbuffer));
printf("parent recived: %s", readbuffer);
if (strncmp(readbuffer+strlen(readbuffer)-strlen("(gdb) \n"),"(gdb) \n", strlen("(gdb) \n")) == 0){
switch (commandCounter){
case 0: writeCommand = "-file-exec-and-symbols /home/dev/spielwiese/build/main\n"; break;
case 1: writeCommand = "-gdb-exit\n"; break;
default: writeCommand = "you should never reach here\n"; break;
}
write(toGDB[1],writeCommand,strlen(writeCommand)+1);
printf("wrote: %s", writeCommand);
commandCounter++;
}else if(strncmp(readbuffer,"^exit", sizeof("^exit")-1) == 0){
break;
}
memset(readbuffer,'\0',strlen(readbuffer)); //clear the readbuffer
}
puts("parent fertig");
sleep(5);
}
return 0;
}
如果我手动调用相同的命令,这就是我得到的输出(-> 表示来自我的输入)
-> gdb -q --interpreter=mi2
=thread-group-added,id="i1"
(gdb)
-> -file-exec-and-symbols /home/dev/spielwiese/build/main
^done
(gdb)
-> -gdb-exit
^exit
但是如果我运行我的代码,这应该是基本相同的,我会得到这个输出:
Starting Test
parent recived: =thread-group-added,id="i1"
parent recived: (gdb)
wrote: -file-exec-and-symbols /home/dev/spielwiese/build/main
parent recived: ^done
(gdb)
wrote: -gdb-exit
parent recived: &"\n"
parent recived: ^done
(gdb)
wrote: you should never reach here
parent fertig
根据 gdb mi 手册,& 前面有一个日志条目,但这个日志条目是空的,除了换行符。另外,我不知道为什么应该有一个退出的日志条目,或者为什么它无法退出,但没有产生错误。 另外,如果您知道比这更好的 gdb mi 来源:https://sourceware.org/gdb/current/onlinedocs/gdb/GDB_002fMI.html#GDB_002fMI,请告诉我。
【问题讨论】:
-
read()不会用'\0'终止缓冲区,因此之后使用printf( "%s" )或strcmp()之类的东西会导致UB。由于协议是面向行的,你可以考虑使用fgets() -
我并不清楚为什么 gdb-mi 可能会在一种情况下输出日志记录,而在另一种情况下不会。但是,由于您无法完全预测何时会发出日志消息,因此我倾向于说您的程序应该简单地准备好处理它们。
-
还要注意,除了
read()s 不会自动以空值结尾之外,它们可能很短。此外,它们可能很容易超过您的 80 字节缓冲区可以容纳的长度(或任何更长的缓冲区可以容纳的长度)。这两个因素都可能导致子级输出的消息跨越多个父级的read()。这同样适用于另一个方向,尽管您可以更好地控制缓冲区大小是否足够。 -
另外,在分叉的孩子中使用
system()有点奇怪。只要您为自己费心fork(),使用其中一个 exec 函数会更传统。唯一花费您的是输出“child fertig”的能力(因为 exec 函数不返回,除非出现错误)。但是父母还有其他几种方法可以知道孩子何时终止以及如何终止。