GDB调试多进程和多线程(进阶版)

环境

系统:CentOs 7.4版本

编译器:gcc 4.8.5版本

debug:gdb 7.6.1版本

文本编辑器:VScode or VIM

准备工作

默认设置下,在调试多进程程序时GDB只会调试主进程。但是GDB(>V7.0)支持多进程的分别以及同时调试,换句话说,GDB可以同时调试多个程序。只需要设置follow-fork-mode(默认值:parent)和detach-on-fork(默认值:on)即可。

follow-fork-mode                     detach-on-fork                         说明
parent   on   只调试主进程(GDB默认)
child on只调试子进程
parent off同时调试两个进程,gdb跟主进程,子进程block在fork位置    
child off同时调试两个进程,gdb跟子进程,主进程block在fork位置

设置方法:set follow-fork-mode [parent|child]   set detach-on-fork [on|off]
 

调试多进程

测试代码:

#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<unistd.h>

void test()
{
    printf("this is a test\n");
}

int main()
{
    pid_t id;
    id = fork();
    if(id == 0)
    {
        test();
        exit(EXIT_SUCCESS);
    }
    else
    {
        printf("this is main\n");
    }
    return 0;
}

运行结果:

[wanghe@localhost ~]$ ./test_fork.exe 
this is main
this is a test

下面开始调试,常用的命令如下:

查询正在调试的进程:info inferiors

切换调试的进程: inferior <infer number>

添加新的调试进程: add-inferior [-copies n] [-exec executable] ,可以用file executable来分配给inferior 可执行文件。

其他:remove-inferiors infno, detach inferior

首先我们需要调试两个进程,先看程序的默认模式:

(gdb) show follow-fork-mode 
Debugger response to a program call of fork or vfork is "parent".
(gdb) show detach-on-fork 
Whether gdb will detach the child of a fork is on.

可以看出默认模式是parent和on,由于我们需要调试两个进程,所以只需要设置detach-on-fork 为off,gdb先跟着主进程。

(gdb) set detach-on-fork off
(gdb) show detach-on-fork 
Whether gdb will detach the child of a fork is off.

接着通过list或l指令来查看fork的位置:

(gdb) list
5	
6	void test()
7	{
8	    printf("this is a test\n");
9	}
10	
11	int main()
12	{
13	    pid_t id;
14	    id = fork();

 所以把断点设置在15行,通过b 15命令,然后通过info inferiors命令查看程序中的进程:

(gdb) run
Starting program: /home/wanghe/test_fork.exe 
[New process 13978]
Missing separate debuginfos, use: debuginfo-install glibc-2.17-260.el7_6.6.x86_64

Breakpoint 1, main () at test_fork.c:15
15	    if(id == 0)
Missing separate debuginfos, use: debuginfo-install glibc-2.17-260.el7_6.6.x86_64
(gdb) info inferiors 
  Num  Description       Executable        
  2    process 13978     /home/wanghe/test_fork.exe 
* 1    process 13974     /home/wanghe/test_fork.exe 

 可以看出有两个进程,主进程标号是1,子进程标号是2。

(gdb) n
22	        printf("this is main\n");

 通过n指令可以看到主进程的执行情况。

通过inferior + 编号可以切换进程调试。

(gdb) inferior 2
[Switching to inferior 2 [process 13978] (/home/wanghe/test_fork.exe)]
[Switching to thread 2 (process 13978)] 
#0  0x00007ffff7ad30c2 in fork () from /lib64/libc.so.6
(gdb) info inferiors 
  Num  Description       Executable        
* 2    process 13978     /home/wanghe/test_fork.exe 
  1    process 13974     /home/wanghe/test_fork.exe 

切换到子进程。

(gdb) n
Single stepping until exit from function fork,
which has no line number information.

Breakpoint 1, main () at test_fork.c:15
15	    if(id == 0)
(gdb) n
17	        test();
(gdb) n
this is a test
18	        exit(EXIT_SUCCESS);

 然后各个进程分别可以执行完毕。

调试多线程

set scheduler-locking off | on off是所有线程都运行,on是只有当前线程会执行

调试多线程和调试多进程的情况相似。

测试代码:

#include<stdio.h>
#include<pthread.h>

void *thread1(void *arg)
{
    printf("%s\n",(char*)arg);
}

void *thread2(void *arg)
{
    printf("%s\n",(char*)arg);
}

void *thread3(void *arg)
{
    printf("%s\n",(char*)arg);
}

int main()
{
    pthread_t t1,t2,t3;
    pthread_create(&t1,NULL,thread1,"thread1");
    pthread_create(&t2,NULL,thread2,"thread2");
    pthread_create(&t3,NULL,thread3,"thread3");

    pthread_join(t1,NULL);
    pthread_join(t2,NULL);
    pthread_join(t3,NULL);
    return 0;
}

首先通过list指令看:

(gdb) list
13	
14	void *thread3(void *arg)
15	{
16	    printf("%s\n",(char*)arg);
17	}
18	
19	int main()
20	{
21	    pthread_t t1,t2,t3;
22	    pthread_create(&t1,NULL,thread1,"thread1");

 第一次创建线程是在22行,所以断点设置在22行:

(gdb) b 22
Breakpoint 1 at 0x400643: file test_thread.c, line 22.
(gdb) run
Starting program: /home/wanghe/test_thread.exe 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".

Breakpoint 1, main () at test_thread.c:22
22	    pthread_create(&t1,NULL,thread1,"thread1");
Missing separate debuginfos, use: debuginfo-install glibc-2.17-260.el7_6.6.x86_64
(gdb) info threads 
  Id   Target Id         Frame 
* 1    Thread 0x7ffff7fe0740 (LWP 15251) "test_thread.exe" main ()
    at test_thread.c:22

 这时候可以看出只有一个主线程线程,继续同过n指令执行。

(gdb) n
[New Thread 0x7ffff77f1700 (LWP 15274)]
23	    pthread_create(&t2,NULL,thread2,"thread2");
(gdb) info threads 
  Id   Target Id         Frame 
  2    Thread 0x7ffff77f1700 (LWP 15274) "test_thread.exe" 0x00007ffff78efff1 in clone () from /lib64/libc.so.6
* 1    Thread 0x7ffff7fe0740 (LWP 15251) "test_thread.exe" main ()
    at test_thread.c:23

 发现多了一个子线程。

通过thread + 线程编号进行切换线程。

(gdb) thread 2
[Switching to thread 2 (Thread 0x7ffff77f1700 (LWP 15274))]
#0  0x00007ffff78efff1 in clone () from /lib64/libc.so.6
(gdb) info threads 
  Id   Target Id         Frame 
* 2    Thread 0x7ffff77f1700 (LWP 15274) "test_thread.exe" 0x00007ffff78efff1 in clone () from /lib64/libc.so.6
  1    Thread 0x7ffff7fe0740 (LWP 15251) "test_thread.exe" main ()
    at test_thread.c:23

通过n指令继续执行。 

(gdb) n
Single stepping until exit from function clone,
which has no line number information.
0x00007ffff7bc6d10 in start_thread () from /lib64/libpthread.so.0
(gdb) n
Single stepping until exit from function start_thread,
which has no line number information.
[New Thread 0x7ffff6ff0700 (LWP 15283)]
thread2
[New Thread 0x7ffff67ef700 (LWP 15284)]
[Thread 0x7ffff6ff0700 (LWP 15283) exited]
thread3
thread1
[Thread 0x7ffff67ef700 (LWP 15284) exited]
[Thread 0x7ffff77f1700 (LWP 15274) exited]
No unwaited-for children left.

发现三个子线程都结束了,说明是并发执行,同时程序就剩一个主线程。

(gdb) info threads 
  Id   Target Id         Frame 
  1    Thread 0x7ffff7fe0740 (LWP 15251) "test_thread.exe" 0x00007ffff7bc7f47 in pthread_join () from /lib64/libpthread.so.0

The current thread <Thread ID 2> has terminated.  See `help thread'.

测试代码

通过下面的测试代码,既包含进程,每个进程又有一个子线程,输出的顺序不是1 2 3 4,现在要求通过调试使得输出顺序是1 2 3 4。

#include<stdio.h>
#include<pthread.h>
#include<unistd.h>
#include<sys/types.h>
#include<stdlib.h>

void *compute()//子进程的子线程
{
	sleep(1);
	printf("4.子进程的子线程\n");
}

void *main_compute()//主进程的子线程
{
	sleep(1);
	printf("2.父进程的子线程\n");
}
int main( int argc , char *argv[] )  
{
	pid_t fork_id;
	fork_id = fork();
	if(fork_id == 0) //子进程
	{
		sleep(1);
		printf("3.这里是子进程\n");
		pthread_t id;
		pthread_create(&id,NULL,compute,NULL);
		pthread_join(id,NULL);
		exit(EXIT_SUCCESS);
	}
	else //父进程
	{
		sleep(1);
		printf("1.这里是父进程\n");
		pthread_t id;
		pthread_create(&id,NULL,main_compute,NULL);
		pthread_join(id,NULL);
	}
	return 0;
}

运行结果:

[wanghe@localhost ~]$ ./test.exe 
1.这里是父进程
3.这里是子进程
2.父进程的子线程
4.子进程的子线程

 如果使main_compute函数中的sleep函数的参数改成2s,就会调整并发执行结果的顺序,如下:

[wanghe@localhost ~]$ ./test.exe 
1.这里是父进程
3.这里是子进程
4.子进程的子线程
2.父进程的子线程

并发执行的结果不是1 2 3 4,所以接下来通过gdb来尝试。

首先通过list找到第一个fork位置。

(gdb) list
12	
13	void *main_compute()//主进程的子线程
14	{
15		sleep(1);
16		printf("2.父进程的子线程\n");
17	}
18	int main( int argc , char *argv[] )  
19	{
20		pid_t fork_id;
21		fork_id = fork();

设置断点为b 21,然后设置为同时调试两个进程,gdb默认跟主进程。

(gdb) b 21
Breakpoint 1 at 0x400700: file test.c, line 21.
(gdb) set detach-on-fork off
(gdb) run
Starting program: /home/wanghe/test.exe 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".

Breakpoint 1, main (argc=1, argv=0x7fffffffe008) at test.c:21
21		fork_id = fork();
Missing separate debuginfos, use: debuginfo-install glibc-2.17-260.el7_6.6.x86_64

这时候查看进程: 

(gdb) info inferiors 
  Num  Description       Executable        
* 1    process 15607     /home/wanghe/test.exe 
(gdb) n
[New process 15611]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
22		if(fork_id == 0) //子进程
Missing separate debuginfos, use: debuginfo-install glibc-2.17-260.el7_6.6.x86_64
(gdb) info inferiors 
  Num  Description       Executable        
  2    process 15611     /home/wanghe/test.exe 
* 1    process 15607     /home/wanghe/test.exe 

刚开始只有一个进程,执行fork()函数后变成了两个进程,接着查看线程,发现有两个线程,分别对应两个进程。

(gdb) info threads 
  Id   Target Id         Frame 
  2    Thread 0x7ffff7fe0740 (LWP 15611) "test.exe" 0x00007ffff78b70c2 in fork
    () from /lib64/libc.so.6
* 1    Thread 0x7ffff7fe0740 (LWP 15607) "test.exe" main (argc=1, 
    argv=0x7fffffffe008) at test.c:22

继续执行

(gdb) n
33			sleep(1);
(gdb) n
34			printf("1.这里是父进程\n");
(gdb) n
1.这里是父进程
36			pthread_create(&id,NULL,main_compute,NULL);

已经打印出了1,接着主进程的主线程会开辟一个子线程。

(gdb) info threads 
  Id   Target Id         Frame 
  3    Thread 0x7ffff77f1700 (LWP 15718) "test.exe" 0x00007ffff78efff1 in clone
    () from /lib64/libc.so.6
  2    Thread 0x7ffff7fe0740 (LWP 15611) "test.exe" 0x00007ffff78b70c2 in fork
    () from /lib64/libc.so.6
* 1    Thread 0x7ffff7fe0740 (LWP 15607) "test.exe" main (argc=1, 
    argv=0x7fffffffe008) at test.c:37

3号线程就是新的子线程,现在转到子线程执行,通过thread 3命令:

(gdb) thread 3
[Switching to thread 3 (Thread 0x7ffff77f1700 (LWP 15718))]
#0  0x00007ffff78efff1 in clone () from /lib64/libc.so.6
(gdb) n
Single stepping until exit from function clone,
which has no line number information.
0x00007ffff7bc6d10 in start_thread () from /lib64/libpthread.so.0
(gdb) n
Single stepping until exit from function start_thread,
which has no line number information.
2.父进程的子线程
[Thread 0x7ffff77f1700 (LWP 15718) exited]
No unwaited-for children left.

这时已经输出了2,接着转到2号进程,也就是子进程:

(gdb) inferior 2
[Switching to inferior 2 [process 15611] (/home/wanghe/test.exe)]
[Switching to thread 2 (Thread 0x7ffff7fe0740 (LWP 15611))] 
#0  0x00007ffff78b70c2 in fork () from /lib64/libc.so.6
(gdb) n
Single stepping until exit from function fork,
which has no line number information.
main (argc=1, argv=0x7fffffffe008) at test.c:22
22		if(fork_id == 0) //子进程
(gdb) n
24			sleep(1);
(gdb) n
25			printf("3.这里是子进程\n");
(gdb) n
3.这里是子进程
27			pthread_create(&id,NULL,compute,NULL);

连续执行后可以看出输出了3。

输入n继续执行,这时候查看线程:

(gdb) info threads 
  Id   Target Id         Frame 
  4    Thread 0x7ffff77f1700 (LWP 15727) "test.exe" 0x00007ffff78efff1 in clone
    () from /lib64/libc.so.6
* 2    Thread 0x7ffff7fe0740 (LWP 15611) "test.exe" main (argc=1, 
    argv=0x7fffffffe008) at test.c:28
  1    Thread 0x7ffff7fe0740 (LWP 15607) "test.exe" main (argc=1, 
    argv=0x7fffffffe008) at test.c:39

4号线程就是子进程的主线程开辟的子线程,通过thread 4继续执行:

(gdb) n
Single stepping until exit from function clone,
which has no line number information.
0x00007ffff7bc6d10 in start_thread () from /lib64/libpthread.so.0
(gdb) n
Single stepping until exit from function start_thread,
which has no line number information.
4.子进程的子线程
[Thread 0x7ffff77f1700 (LWP 15727) exited]
No unwaited-for children left.

至此,1 2 3 4按照顺序输出完毕,是不是很有意思呢。

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值