justtreee / blog

31 stars 8 forks source link

【操作系统实验笔记】进程控制与GDB多线程调试 #2

Open justtreee opened 7 years ago

justtreee commented 7 years ago

本文基于最基础的Linux下创建子进程的程序,了解GDB在多线程下的调试。 参考链接: 100个gdb小技巧 操作系统:Ubuntu 16.04/bash on ubuntu on windows

实验内容

编写一段程序,使用系统调用 fork( )创建两个子进程,在系统中有一个父进程和两个子进程 活动。让每个进程在屏幕上显示一个字符;父进程显示字符“a”,子进程分别显示字符“b” 和“c”。 试观察记录屏幕上的显示结果,并分析原因。

实现代码

#include  
#include  
int main(){  
int p1,p2;  
while((p1=fork())==-1);
if(p1==0)
putchar('b');
else {
while((p2=fork())==-1);
if(p2==0){
putchar('c');
printf("\n");
}
else putchar('a');
}
return 0; 
}

其中,a、b、c 的显示顺序是随机的,取决与进程的调度顺序。fork()创建进程之后,各 个进程的时间片的获得不一定是顺序的,所以输出的顺序会变化。

GDB多进程调试

调试子进程

在调试多进程程序时,gdb 默认会追踪父进程。如果要调试子进程,要使用如下命 令:set follow-fork-mode child

(gdb) set follow-fork-mode child
(gdb) start
Temporary breakpoint 1 at 0x40056e: file 1.c, line 5.
Starting program: /root/tst

Temporary breakpoint 1, main () at 1.c:5
5               while((p1=fork())==-1);
(gdb) n
[New process 24]
ac
[Switching to process 24]
main () at 1.c:6
6               if(p1==0)
(gdb)
7                       putchar('b');
(gdb)
16              return 0;
(gdb)
17      }
(gdb)
__libc_start_main (main=0x400566 
, argc=1, argv=0x7ffffffde398, init=, fini=, rtld_fini=, stack_end=0x7ffffffde388) at ../csu/libc-start.c:325 325 ../csu/libc-start.c: No such file or directory. (gdb) b[Inferior 2 (process 24) exited normally] (gdb) The program is not being run.

可以看到执行到第5行,父进程打印“a” 并创建了子进程 24,子进程打印“c”。正常退出后打印“b”。 (但这里是不是只监视了一个子进程24 ?)

同时调试父进程和子进程

如果要同时调试父进程和子进程,可以使用set detach-on-fork off(默 认 detach-on-forkon)命令,这样 gdb 就能同时调试父子进程,并且在调试一 个进程时,另外一个进程处于挂起状态。在使用set detach-on-fork off命令 后,用i inferiors(i 是 info 命令缩写)查看进程状态,可以看到父子进程都 在被 gdb 调试的状态,前面显示“*”是正在调试的进程。当父进程退出后,用 inferior infno切换到子进程去调试。

(gdb) set detach-on-fork off
(gdb) start
Reading symbols from /usr/lib/debug/lib/x86_64-linux-gnu/libc-2.23.so...done.
Reading symbols from /usr/lib/debug/lib/x86_64-linux-gnu/ld-2.23.so...done.
Reading symbols from /usr/lib/debug/lib/x86_64-linux-gnu/libc-2.23.so...done.
Reading symbols from /usr/lib/debug/lib/x86_64-linux-gnu/ld-2.23.so...done.
Temporary breakpoint 2 at 0x40056e: main. (3 locations)
Starting program: /root/tst

Thread 1.1 "tst" hit Temporary breakpoint 2, main () at 1.c:5
5               while((p1=fork())==-1);
(gdb) n
[New process 26]
6               if(p1==0)
(gdb) i inferior
  Num  Description       Executable
* 1    process 25        /root/tst
  2    process 23        /root/tst
  3    process 24        /root/tst
  4    process 26        /root/tst
(gdb) n
9                       while((p2=fork())==-1);
(gdb)
[New process 27]
10                      if(p2==0){
(gdb)
14                      else putchar('a');
(gdb)
16              return 0;
(gdb)
17      }
(gdb)
__libc_start_main (main=0x400566 
, argc=1, argv=0x7ffffffde398, init=, fini=, rtld_fini=, stack_end=0x7ffffffde388) at ../csu/libc-start.c:325 325 ../csu/libc-start.c: No such file or directory. (gdb) a[Inferior 1 (process 25) exited normally] (gdb) The program is not being run. (gdb) inferior 2 [Switching to inferior 2 [process 23] (/root/tst)] [Switching to thread 2.1 (process 23)] #0 0x00007fffff0fc41a in __libc_fork () at ../sysdeps/nptl/fork.c:145 145 ../sysdeps/nptl/fork.c: No such file or directory. (gdb) [Switching to inferior 2 [process 23] (/root/tst)] [Switching to thread 2.1 (process 23)] #0 0x00007fffff0fc41a in __libc_fork () at ../sysdeps/nptl/fork.c:145 145 in ../sysdeps/nptl/fork.c . . . 168 in ../sysdeps/nptl/fork.c (gdb) bt #0 __libc_fork () at ../sysdeps/nptl/fork.c:168 #1 0x0000000000400573 in main () at 1.c:5 (gdb) n 169 in ../sysdeps/nptl/fork.c . . . (gdb) 264 in ../sysdeps/nptl/fork.c (gdb) main () at 1.c:6 6 if(p1==0) (gdb) i inferior Num Description Executable 1 /root/tst * 2 process 23 /root/tst 3 process 24 /root/tst 4 process 26 /root/tst 5 process 27 /root/tst (gdb) inferior 3 [Switching to inferior 3 [process 24] (/root/tst)] [Switching to thread 3.1 (process 24)] #0 0x00007fffff0fc41a in __libc_fork () at ../sysdeps/nptl/fork.c:145 145 ../sysdeps/nptl/fork.c: No such file or directory. (gdb) i inferior Num Description Executable 1 /root/tst 2 process 23 /root/tst * 3 process 24 /root/tst 4 process 26 /root/tst 5 process 27 /root/tst (gdb) n 152 in ../sysdeps/nptl/fork.c . . . (gdb) 264 in ../sysdeps/nptl/fork.c (gdb) main () at 1.c:10 10 if(p2==0){ (gdb) 11 putchar('c'); (gdb) 12 printf("\n"); (gdb) c 16 return 0; (gdb) 17 } (gdb) __libc_start_main (main=0x400566
, argc=1, argv=0x7ffffffde398, init=, fini=, rtld_fini=, stack_end=0x7ffffffde388) at ../csu/libc-start.c:325 325 ../csu/libc-start.c: No such file or directory. (gdb) [Inferior 3 (process 24) exited normally] (gdb) 7 putchar('b'); (gdb) 16 return 0; (gdb) 17 } (gdb) __libc_start_main (main=0x400566
, argc=1, argv=0x7ffffffde398, init=, fini=, rtld_fini=, stack_end=0x7ffffffde388) at ../csu/libc-start.c:325 325 ../csu/libc-start.c: No such file or directory. (gdb) b[Inferior 2 (process 23) exited normally] (gdb) The program is not being run.

在使用“set detach-on-fork off”命令后,用“i inferiors”(i是info命令缩写)查看进程状态,可以看到父子进程都在被gdb调试的状态,前面显示“*”是正在调试的进程。当父进程退出后,用“inferior infno”切换到子进程去调试。 此外,如果想让父子进程都同时运行,可以使用“set schedule-multiple on”(默认schedule-multiple是off)命令,仍以上述代码为例:

(gdb) set detach-on-fork off
(gdb) set schedule-multiple on
(gdb) start
Temporary breakpoint 1 at 0x40056e: file 1.c, line 5.
Starting program: /root/tst

Temporary breakpoint 1, main () at 1.c:5
5               while((p1=fork())==-1);
(gdb) n
[New process 32]
b6              if(p1==0)
(gdb)
[Inferior 2 (process 32) exited normally]
(gdb)
9                       while((p2=fork())==-1);
(gdb)
[New process 33]
c
10                      if(p2==0){
(gdb)
[Inferior 3 (process 33) exited normally]
(gdb)
14                      else putchar('a');
(gdb)
16              return 0;
(gdb)
17      }
(gdb)
__libc_start_main (main=0x400566 
, argc=1, argv=0x7ffffffde398, init=, fini=, rtld_fini=, stack_end=0x7ffffffde388) at ../csu/libc-start.c:325 325 ../csu/libc-start.c: No such file or directory. (gdb) a[Inferior 1 (process 28) exited normally] (gdb) The program is not being run.

父进程Inferior 1(process 28)创建Inferior 2(process 32)打印 b ,并正常退出;再创建Inferior 3(process 33)打印c,并正常退出,之后父进程打印a,退出。

总结

第一次上手GDB调试,有助于对进程控制的理解