编写 UNIX® 系统程序充满乐趣,并且具有教育意义。使用 UNIX strace 工具和 GDB(GNU 项目调试工具),您可以真正地深入研究系统的功能,并了解组成这些功能的各种各样的程序。同时使用这两种工具,能够在查看 UNIX 计算机底层信息 的时候,给您带来更好的体验。

UNIX 家族总是为用户提供了丰富的工具。UNIX 是一个工具财宝箱,有了这些工具,您不仅可以完成具有创造性的工作,还可以在深入研究该操作系统的同时得到教育和娱乐。strace(用来跟踪任何程序的系统调用)和 GDB 调试工具(用来在受控的环境中运行程序的功能齐全的调试工具)是实现这个目标的两个有价值的工具。

UNIX 的设计由大量的函数调用(称为系统调用)组成,其中包括一些简单的任务,如在屏幕上显示字符串来设置任务优先级。所有的 UNIX 程序都是通过调用操作系统提供的这些底层服务来完成它们的任务,使用 strace 工具,您可以清楚地看到这些调用过程及其使用的参数。通过这种方式,您可以操作这些程序,以了解它们与操作系统之间的底层交互。

开始游戏

让我们以一个简单的 UNIX 命令 pwd 作为开始,然后更深入地研究该命令在完成其任务的过程中进行了哪些工作。启动 xterm 以创建一个进行实验的受控环境,然后输入下面的命令:

$ pwd

这个 pwd 命令显示了当前的工作目录。在我的计算机上,当时的输出是:

/home/bill/

一个如此简单的函数掩饰了该命令底层的复杂性(顺便说一下,所有的计算机程序都是这样的)。要真正地了解其复杂性,请使用 strace 工具再次运行 pwd 命令:

$ strace pwd

通过该命令,您可以看到,在显示和列举当前工作目录的过程中,UNIX 计算机执行了相当多的操作(请参见清单 1)。

清单 1:strace pwd 命令的输出
execve("/bin/pwd", ["pwd"], [/* 39 vars */]) = 0
uname({sys="Linux", node="sammy", ...}) = 0
brk(0) = 0x804c000
old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x4001...
.
.
.
fstat64(3, {st_mode=S_IFREG|0644, st_size=115031, ...}) = 0
old_mmap(NULL, 115031, PROT_READ, MAP_PRIVATE, 3, 0) = 0x40017000
close(3) = 0
open("/lib/tls/libc.so.6", O_RDONLY) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\360U\1"..., 1024) = 1024
fstat64(3, {st_mode=S_IFREG|0755, st_size=1547996, ...}) = 0
old_mmap(0x42000000, 1257224, PROT_READ|PROT_EXEC, MAP_PRIVATE, 3, 0) = 0x42000000
mprotect(0x4212e000, 20232, PROT_NONE) = 0
old_mmap(0x4212e000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 3, 0x12e000)...
old_mmap(0x42131000, 7944, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS,...
close(3) = 0
set_thread_area({entry_number:-1 -> 6, base_addr:0x40016ac0, limit:1048575, seg_32bit...
munmap(0x40017000, 115031) = 0
brk(0) = 0x804c000
brk(0x804d000) = 0x804d000
brk(0) = 0x804d000
open("/usr/lib/locale/locale-archive", O_RDONLY|O_LARGEFILE) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=30301680, ...}) = 0
mmap2(NULL, 2097152, PROT_READ, MAP_PRIVATE, 3, 0) = 0x40017000
close(3) = 0
brk(0) = 0x804d000
brk(0x804e000) = 0x804e000
getcwd("/home/bill", 4096) = 11
fstat64(1, {st_mode=S_IFCHR|0600, st_rdev=makedev(136, 6), ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x4021700...
write(1, "/home/bill\n", 11/home/bill
) = 11
munmap(0x40217000, 4096) = 0
exit_group(0) = ?
 

回页首

UNIX 系统调用的具体细节

关于检索和显示当前工作目录所需的所有系统调用的详细细节,已经超出了本文的讨论范围,但我会介绍如何获取这些信息。清单 1 中的每一行都以类似 C 的格式,清楚地说明了一项系统调用及其参数,这也是 C 程序员希望看到的。Strace 同样以这种方式显示这些调用,不管在实际创建该程序时使用的是何种编程语言。

如果要了解清单 1 中的所有细节信息,对于所有这些系统调用,UNIX 为您提供了大量的文档。清单 1 中“最重要”的函数是 getcwd() 函数,它表示获取当前工作目录。当前的 xterm 显示了 strace pwd 的输出,同时再启动另一个 xterm 并输入下面的命令以查看 UNIX 对该函数的显示:

$ man getcwd

您所看到的应该是 getcwd() 函数完整的清单以及这个重要的 C 函数需要的和返回的参数清单。同样地,您可以输入 man brk 或 man fstat64等等。通常,UNIX 系统通过文档对这些系统函数进行了详细的说明,如果花些时间仔细地研究它们,您将逐渐地了解到 UNIX 的功能是多么的强大,以及学习这些底层系统细节是多么的容易。在所有的操作系统中,UNIX 最善于帮助您理解其底层的处理过程。

 

回页首

观察 nweb

对于下面几个步骤,您需要使用更庞大且更复杂的程序,而不是像 pwd 这样简单的 UNIX 命令。简单的超文本传输协议 (HTTP) 服务器,如 nweb,是非常适合的。当您在 Internet 上冲浪 的时候,HTTP 服务器侦听浏览器请求,然后通过发送所请求的对象,如 Web 页面和图形文件,以此响应浏览器的请求。

下载并安装 nweb,该软件由 IBM developerWorks 投稿作家 Nigel Griffiths 编写。 (请参阅参考资料部分提供的 Nigel 的文章“nweb: ?"a tiny, safe Web server (static pages only)”(developerWorks,2004 年 6 月)的链接。)

下载 es-nweb.zip 到 $HOME/downloads 目录,然后输入清单 2 中所示的简单命令,以提取、编译并启动该程序:

注意:我假设您需要为 Linux® 工作站编译这个程序。如果实际情况并非如此,那么有关在其他的 UNIX 操作系统上对该程序进行编译的详细信息,请阅读这篇 nweb 文章。

清单 2. 用于提取、编译和启动 nweb 的命令
$ cd src
$ mkdir nweb
$ cd nweb
$ unzip $HOME/downloads/es-nweb.zip
$ gcc -ggdb -O -DLINUX nweb.c -o nweb
$ ./nweb 9090 $HOME/src/nweb &

注意:清单 2 中的 -ggdb 选项和 Nigel 的文章中的内容有些不同,该选项用于告诉 GCC 编译器对该程序进行优化,以便使用 GDB 调试工具对其进行调试,您将在以后用到该调试工具。

接下来,要确认 nweb 服务器已经运行,可以使用清单 3 中所示的 ps 命令来对它进行检查。

清单 3. ps 命令
$ ps
PID TTY TIME CMD
2913 pts/5 00:00:00 bash
4009 pts/5 00:00:00 nweb
4011 pts/5 00:00:00 ps

最后,要确认 nweb 确实正在运行并且状态正常,可以在您的计算机上启动一个 Web 浏览器并在地址栏中输入 http://localhost:9090

针对 nweb 使用 strace

现在,让我们来进行一些有趣的工作。启动另一个 xterm,然后使用 strace 来跟踪正在运行的 nweb 服务器。要完成该任务,您必须清楚该程序的进程 ID,并且必须具有适当的权限。您仅仅可以看到一组特定的系统调用,即那些与网络相关的系统调用。输入清单 4 第一行所示的命令作为开始,其中使用了前面显示的 nweb 的进程 ID。您应该看到如下的输出(清单 4 中的第二行)。

清单 4. 开始对 nweb 进行跟踪
$ strace -e trace=network -p 4009
accept(0,

请注意,在调用网络 accept() 函数的过程中停止了跟踪操作。在浏览器中刷新几次 http://localhost:9090 页面,请注意每次刷新该页面时 strace 的显示。这是不是很棒呢?您所看到的是,当 Web 浏览器调用 HTTP 服务器 (nweb) 时,服务器所进行的底层网络调用。简单地说,nweb 正在接受 来自您的浏览器的调用。

您可以在运行 strace 的具有窗口焦点的 xterm 中按下 Ctrl+C 以停止对网络调用的跟踪。

 

回页首

再来研究 GDB 调试工具

正如您所看到的,strace 可以作为了解用户程序如何通过某些系统调用与操作系统进行交互的一个很好的程序。GDB 调试工具本身也可以附加于一个正在运行的进程,并帮助您进行更深入的研究。

GDB 调试工具非常有用,Internet 上提供了大量的有关该工具的可用信息。通常,调试工具是很有价值的工具,并且任何负责开发和维护计算机系统的人员应该了解如何使用它们。因此,在 nweb 运行于另一个 xterm 会话的同时,按下 Ctrl+C 停止 strace,然后输入清单 5 中所示的命令启动 GDB 调试工具。

清单 5. 启动 GDB 调试工具
$ gdb --quiet
(gdb) attach 4009
Attaching to process 4009
Reading symbols from /home/bill/src/nweb/nweb...done.
Reading symbols from /lib/tls/libc.so.6...done.
Loaded symbols for /lib/tls/libc.so.6
Reading symbols from /lib/ld-linux.so.2...done.
Loaded symbols for /lib/ld-linux.so.2
0xffffe410 in ?? ()
(gdb)

-quiet 选项告诉 GDB 调试工具仅显示其提示符,而不要显示所有其他的启动信息。如果需要显示额外的文本信息,可以去掉 -quiet 选项。

attach 4009 命令启动对当前正在运行的 nweb 服务器的调试工作,并且 GDB 调试工具通过读取有关该进程的所有的符号信息来做出同样方式的响应。下一步,使用 info 命令来列举您所研究的程序的相关信息(请参见清单 6)。

清单 6. info 命令列出程序信息
(gdb) info proc
process 4009
cmdline = './nweb'
cwd = '/home/bill/src/nweb'
exe = '/home/bill/src/nweb/nweb'
(gdb)

info 命令(请参见清单 7)的另一个有用的变种是 info functions,然而,函数的列表可能很长。

清单 7. info functions 命令得到的函数列表
(gdb) info functions
All defined functions: File nweb.c:
void log(int, char *, char *, int);
int main(int, char **);
void web(int, int); File __finite:
int __finite();
.
.
.
(gdb)

因为使用 -ggdb 选项对 nweb 程序进行了编译,所以可执行文件中包含了大量的调试信息,允许该调试工具查看文件中列举的已定义的函数,如清单 7 所示。

 

回页首

list 和 disassemble 命令

有两个重要的 GDB 调试工具命令,它们分别是 list 和 disassemble。通过使用清单 8 中所示的代码尝试使用这些命令。

清单 8. list 命令
(gdb) list main
121 exit(1);
122 }
123
124
125 main(int argc, char **argv)
126 {
127 int i, port, pid, listenfd, socketfd, hit;
128 size_t length;
129 char *str;
130 static struct sockaddr_in cli_addr; /* static = initialised to zeros */
(gdb)
131 static struct sockaddr_in serv_addr; /* static = initialised to zeros */
132
133 if( argc < 3 || argc > 3 || !strcmp(argv[1], "-?") ) {
134 (void)printf("hint: nweb Port-Number Top-Directory\n\n"
135 "\tnweb is a small and very safe mini web server\n"
136 "\tnweb only servers out file/web pages with extensions named below\n"
137 "\t and only from the named directory or its sub-directories.\n"
138 "\tThere is no fancy features = safe and secure.\n\n"
139 "\tExample: nweb 8181 /home/nwebdir &\n\n"
140 "\tOnly Supports:");

正如您所看到的,list 命令以源文件的形式列出了正在运行的程序,并标注了相应的行号。按下 Return 键(如第 130 行和第 131 行之间所示)以接着上次的列表继续进行列举。现在,尝试使用 disassemble 命令,可以缩写为 disass(请参见清单 9)。

清单 9. disassemble 命令
(gdb) disass main
Dump of assembler code for function main:
0x08048ba2 <main+0>: push ebp
0x08048ba3 <main+1>: mov ebp,esp
0x08048ba5 <main+3>: push edi
0x08048ba6 <main+4>: push esi
0x08048ba7 <main+5>: push ebx
0x08048ba8 <main+6>: sub esp,0xc
0x08048bab <main+9>: mov ebx,DWORD PTR [ebp+12]
0x08048bae <main+12>: and esp,0xfffffff0
.
.
.
0x08048c01 <main+95>: call 0x8048664 <printf>
0x08048c06 <main+100>: add esp,0x10
0x08048c09 <main+103>: inc esi
0x08048c0a <main+104>: cmp DWORD PTR [ebx+esi],0x0
---Type <return> to continue, or q <return> to quit---

反汇编清单显示了该 main 函数的汇编语言清单。在本示例中,汇编代码指示出运行该代码的计算机使用的是 Intel® Pentium® 处理器。如果该程序运行于不同类型的处理器上,如基于 IBM Power PC® 的计算机,那么您的代码看上去将有很大的区别。

 

回页首

在其运行的过程中进行监视

因为您所监视的是一个正在运行的程序,所以可以设置相应的断点,然后在它响应浏览器请求并向提出请求的浏览器传输 .html 和 .jpg 文件的同时,对该程序进行监视清单 10 介绍了如何完成该任务。

清单 10. 设置断点
(gdb) break 188
Breakpoint 1 at 0x8048e70: file nweb.c, line 188.
(gdb) commands 1
Type commands for when breakpoint 1 is hit, one per line.
End with a line saying just "end".
>continue
>end
(gdb) c
Continuing.

此时,GDB 调试工具已设置为在 nweb 服务器接受 浏览器请求处进行中断,该调试工具将仅仅显示相应的请求并继续处理其他的请求,而不会中断正在运行的程序。刷新几次浏览器中的 http://localhost:9090/ 页面,可以观察到,GDB 调试工具显示了断点并继续运行。

在刷新浏览器页面的同时,您应该看到如清单 11 所示的断点信息,在 GDB 调试工具 xterm 中滚动输出。与 strace 相同,您可以按下 Ctrl+C 来停止对 nweb 服务器的调试。在停止了跟踪操作之后,您可以输入 quit 命令以退出 GDB 调试工具。

清单 11. GDB 调试工具 xterm 中的断点信息
Breakpoint 1, main (argc=3, argv=0x1) at nweb.c:188
188 if((socketfd = accept(listenfd, (struct sockaddr *)&cli_addr, &length)) < 0)
Breakpoint 1, main (argc=3, argv=0x1) at nweb.c:188
188 if((socketfd = accept(listenfd, (struct sockaddr *)&cli_addr, &length)) < 0)
Breakpoint 1, main (argc=3, argv=0x1) at nweb.c:188
188 if((socketfd = accept(listenfd, (struct sockaddr *)&cli_addr, &length)) < 0)
Breakpoint 1, main (argc=3, argv=0x1) at nweb.c:188
188 if((socketfd = accept(listenfd, (struct sockaddr *)&cli_addr, &length)) < 0)
Program received signal SIGINT, Interrupt.
0xffffe410 in ?? ()
(gdb) quit
The program is running. Quit anyway (and detach it)? (y or n) y
Detaching from program: /home/bill/src/nweb/nweb, process 4009
$

请注意,您正告诉 GDB 调试工具停止对一个仍在内存中活动的程序的调试。即使是在退出了调试工具之后,您还可以刷新浏览器页面,并将看到 nweb 仍在运行。可以输入 kill 4009 命令来停止该程序,或者在您退出会话时,该页面将会消失。

和平常一样,您可以通过其 man 和 info 页面来了解各种各样的工具,如 strace 和 GDB 调试工具。请确保使用 UNIX 为您提供的这些工具!

 

回页首

了解尽可能多的信息

了解关于您所使用的计算机的尽可能多的信息,绝不是件坏事,并且还可以从这个过程中获得乐趣。实际上,UNIX 通过提供各种工具,如 strace 和 GDB 调试工具以及包含在相应的 man 和 info 页面中的大量的信息,鼓励您对系统进行研究和学习。计算机是人类智慧的延伸,并且我们对其了解得越多,它们将变得越有用。

使用 Strace 和 GDB 调试工具的乐趣的更多相关文章

  1. 在php中使用strace、gdb、tcpdump调试工具

    [转] http://www.syyong.com/php/Using-strace-GDB-and-tcpdump-debugging-tools-in-PHP.html 在php中我们最常使用调试 ...

  2. Linux调试工具strace和gdb常用命令小结

    strace和gdb是Linux环境下的两个常用调试工具,这里是个人在使用过程中对这两个工具常用参数的总结,留作日后查看使用. strace调试工具 strace工具用于跟踪进程执行时的系统调用和所接 ...

  3. 在 php 中使用 strace、gdb、tcpdump 调试工具

    转自:http://www.syyong.com/php/Using-strace-GDB-and-tcpdump-debugging-tools-in-PHP.html 在 php 中我们最常使用调 ...

  4. 使用单进程、strace、gdb调试PHP错误

    使用单进程.strace.gdb调试PHP错误 PHP一般是在FPM的呵护下运行的,但是某些情况下进程异常崩溃会导致502.下面是解决思想: 1. 单进程运行: php -d display_erro ...

  5. gdb调试工具使用方法分享

    刚才看了一个CSDN上分享gdb调试工具使用的教程,讲得非常好,推荐到这里: http://blog.csdn.net/liigo/article/details/582231

  6. Linux环境下的GCC编译器与GDB调试工具介绍

    假如现在我们有如下代码需要编译运行和调试.文件名为:test.c #include <stdio.h> int main() { int day, month, year, sum, le ...

  7. GDB调试工具总结

    程序调试的基本思想是“分析现象->假设错误原因->产生新的现象去验证假设”这样一个循环过程,根据现象如何假设错误原因,以及如何设计新的现象去验证假设,需要非常严密的分析和思考.程序中除了一 ...

  8. Gdb调试工具/ Makfile项目管理

    gdb调试工具 gcc -g main.c -o main 常用命令 命令            简写         作用 help                h            按模块列 ...

  9. 【Linux】GDB调试工具

    GDB调试工具 Linux中包含一个很强大的调试工具GDB(GNU Debuger),可以用它来调试C和C++程序. 一. GDB的主要功能有: 设置断点,当程序运行到断点处暂停 显示变量的值,可以打 ...

随机推荐

  1. using 1.7 requires using android build tools version 19 or later

    这意思大概是adt用了1.7,abt(android build tools)就要用19或更高,可是abt在哪设置呢,原来是在sdk manager中 之前我已安装的最高的abt是17,然后~~~,F ...

  2. java开发经验分享(二)

    二. 数据库 1. SQL语句中保留字.函数名要大写,表明.字段名全部小写 如:SELECT vc_name,vc_sex,i_age FROM user WHERE i_id = 100 AND i ...

  3. hdu5072(鞍山regional problem C):容斥,同色三角形模型

    现场过的第四多的题..当时没什么想法,回来学了下容斥,又听学长讲了一讲,终于把它过了 题目大意:给定n个数,求全部互质或者全部不互质的三元组的个数 先说一下同色三角形模型 n个点 每两个点连一条边(可 ...

  4. 【POJ2777】Count Color(线段树)

    以下是题目大意: 有水平方向上很多块板子拼成的墙,一开始每一块都被涂成了颜色1,有C和P两个操作,代表的意思是:C X Y Z —— 从X到Y将板子涂成颜色ZP X Y    —— 查询X到Y的板子共 ...

  5. 解决selenium 启动ie浏览器报错:Unexpected error launching Internet Explorer. Protected Mode settings are not the same for all zones

    启动ie代码: System.setProperty("webdriver.ie.driver", "bin/IEDriverServer.exe"); Web ...

  6. ThinkPHP3.2.3中三大自动中的缺陷问题

    我们在使用Thinkphp3.2.3框架时在对数据表进行模型化后就可以使用自动完成功能. 自动完成可以帮助我们更简便的完成对表单内容对数据表(集合)的填充,自动完成是基于: 当实例化数据库user后, ...

  7. Java反射举例

    本文參考:http://www.cnblogs.com/yydcdut/p/3845430.html 1.Java反射的基本介绍 Java的反射很强大,传递class. 能够动态的生成该类.取得这个类 ...

  8. Ubuntu 10.04下安装Opengl glx

    1.安装OpenGL Library sudo apt-get install build-essential 2. 安装OpenGL Utilities sudo apt-get install l ...

  9. Chapter 4: Spring and AOP:Spring's AOP Framework -- draft

    Spring's AOP Framework Let's begin by looking at Spring's own AOP framework - a proxy-based framewor ...

  10. javascript 打开新窗口(window.open)

    打开新窗口(window.open) open() 方法可以查找一个已经存在或者新建的浏览器窗口. 语法: window.open([URL], [窗口名称], [参数字符串]) 参数说明: URL: ...