介绍
在本实验中,我们将介绍 GNU 调试器(gdb)并探索其在调试 C 程序中的使用。gdb 是一个强大的工具,允许你检查运行中的程序状态、设置断点,并逐步执行代码以识别和修复问题。我们将首先确保在 Ubuntu 22.04 Docker 容器中安装了 gdb,然后创建一个带有运行时错误的简单 C 程序,并使用 gdb 进行调试。最后,我们将演示如何使用 gdb 调试多线程 C 程序。
在本实验中,我们将介绍 GNU 调试器(gdb)并探索其在调试 C 程序中的使用。gdb 是一个强大的工具,允许你检查运行中的程序状态、设置断点,并逐步执行代码以识别和修复问题。我们将首先确保在 Ubuntu 22.04 Docker 容器中安装了 gdb,然后创建一个带有运行时错误的简单 C 程序,并使用 gdb 进行调试。最后,我们将演示如何使用 gdb 调试多线程 C 程序。
在本步骤中,我们将介绍 GNU 调试器(gdb)并探索其在调试 C 程序中的使用。gdb 是一个强大的工具,允许你检查运行中的程序状态、设置断点,并逐步执行代码以识别和修复问题。
首先,确保在我们的 Ubuntu 22.04 Docker 容器中安装了 gdb:
sudo apt-get update
sudo apt-get install -y gdb
示例输出:
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following additional packages will be installed:
libasan6 libubsan1 python3-gdb
Suggested packages:
gdb-multiarch
The following NEW packages will be installed:
gdb libasan6 libubsan1 python3-gdb
0 upgraded, 4 newly installed, 0 to remove and 0 not upgraded.
Need to get 3,470 kB of archives.
After this operation, 13.5 MB of additional disk space will be used.
Do you want to continue? [Y/n] Y
接下来,我们创建一个简单的 C 程序用于调试。在 ~/project
目录下创建一个名为 example.c
的文件,内容如下:
#include <stdio.h>
int main() {
int a = 5;
int b = 0;
int c = a / b;
printf("The result is: %d\n", c);
return 0;
}
这个程序会故意除以零,从而引发运行时错误。
要使用 gdb 调试此程序,请按照以下步骤操作:
-g
标志编译程序以包含调试符号:gcc -g -o example example.c
gdb ./example
main
函数处设置断点:(gdb) break main
Breakpoint 1 at 0x11a6: file example.c, line 4.
(gdb) run
Starting program: /home/labex/project/example
Breakpoint 1, main () at example.c:4
4 int a = 5;
(gdb) print a
$1 = 5
(gdb) next
5 int b = 0;
(gdb) print b
$2 = 0
(gdb) next
6 int c = a / b;
如你所见,当程序尝试除以零时,会触发一个运行时错误,可以通过 gdb 捕获并调试该错误。
在本步骤中,我们将使用 gdb 调试一个包含运行时错误的简单 C 程序。
首先,在 ~/project
目录下创建一个名为 simple.c
的新 C 文件,内容如下:
#include <stdio.h>
int main() {
int x = 10;
int y = 0;
int z = x / y;
printf("The result is: %d\n", z);
return 0;
}
这个程序会故意除以零,从而引发运行时错误。
接下来,使用 -g
标志编译程序以包含调试符号:
gcc -g -o simple simple.c
然后,启动 gdb 调试器并加载编译后的程序:
gdb ./simple
在 gdb 提示符中,在 main
函数处设置断点:
(gdb) break main
Breakpoint 1 at 0x11a6: file simple.c, line 4.
现在,运行程序:
(gdb) run
Starting program: /home/labex/project/simple
当程序命中断点时,你可以检查变量、逐步执行代码并调试问题:
Breakpoint 1, main () at simple.c:4
4 int x = 10;
(gdb) print x
$1 = 10
(gdb) next
5 int y = 0;
(gdb) print y
$2 = 0
(gdb) next
6 int z = x / y;
如你所见,当程序尝试除以零时,会触发一个运行时错误,可以通过 gdb 捕获并调试该错误。
在本步骤中,我们将使用 gdb 调试一个存在竞态条件(race condition)的多线程 C 程序。
首先,在 ~/project
目录下创建一个名为 multithreaded.c
的新 C 文件,内容如下:
#include <stdio.h>
#include <pthread.h>
int shared_variable = 0;
void* thread_function(void* arg) {
for (int i = 0; i < 1000000; i++) {
shared_variable++;
}
return NULL;
}
int main() {
pthread_t thread1, thread2;
pthread_create(&thread1, NULL, thread_function, NULL);
pthread_create(&thread2, NULL, thread_function, NULL);
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
printf("Final value of shared_variable: %d\n", shared_variable);
return 0;
}
该程序创建了两个线程,每个线程都会对共享变量 shared_variable
递增 1,000,000 次。由于竞态条件的存在,共享变量的最终值可能不会达到预期的 2,000,000。
接下来,使用 -g
标志编译程序以包含调试符号:
gcc -g -o multithreaded multithreaded.c -lpthread
然后,启动 gdb 调试器并加载编译后的程序:
gdb ./multithreaded
在 gdb 提示符中,在 thread_function
函数的开头设置断点:
(gdb) break thread_function
Breakpoint 1 at 0x11b6: file multithreaded.c, line 7.
现在,运行程序:
(gdb) run
Starting program: /home/labex/project/multithreaded
当程序命中断点时,你可以检查变量、逐步执行代码并调试竞态条件:
Breakpoint 1, thread_function (arg=0x0) at multithreaded.c:7
7 for (int i = 0; i < 1000000; i++) {
(gdb) print shared_variable
$1 = 0
(gdb) step
8 shared_variable++;
(gdb) print shared_variable
$2 = 1
通过逐步执行代码并检查共享变量,你可以观察到竞态条件,并识别多线程程序中的问题。
在本实验中,我们介绍了 GNU 调试器(gdb)并探索了其在调试 C 程序中的使用。我们首先确保在 Ubuntu 22.04 Docker 容器中安装了 gdb,然后创建了一个带有运行时错误的简单 C 程序进行调试。我们使用 -g
标志编译程序以包含调试符号,启动 gdb 调试器,在 main
函数处设置断点,并运行程序。当程序命中断点时,我们能够检查变量、逐步执行代码并调试问题。
本实验涵盖了使用 gdb 调试简单 C 程序的基础知识,以及调试多线程 C 程序的内容。通过遵循逐步说明,学习者获得了使用这一强大调试工具的实践经验。