UNIX系列一直为用户提供丰富的服务。 UNIX是工具的宝库,您不仅可以进行生产性工作,还可以在探索操作系统的深度时进行自我教育和娱乐。 为此目的,两个有用的工具是strace和GDB Debugger,它可用来跟踪任何程序的系统调用,GDB Debugger是一个功能齐全的调试器,可让您在受控环境中运行程序。
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都是最好的准备,可以帮助您立即了解其中的内容。
发现nweb
对于下一步,您需要比简单的UNIX命令(如pwd
更大,更复杂的东西。 一个简单的超文本传输协议(HTTP)服务器,例如nweb,是完美的。 当您浏览 Internet时,HTTP服务器会侦听您的浏览器请求,然后通过发送您请求的对象(例如网页和图形文件)来响应浏览器请求。
下载并安装由IBM developerWorks特约作者Nigel Griffiths编写的nweb。 (请参阅相关信息中的链接部分Nigel的文章“NWEB:一个微小的,安全的Web服务器(只有静态页面)。”(developerWorks,2004年6月))
将es-nweb.zip下载到$ HOME / downloads目录后,键入清单2中所示的简单命令以提取,编译和启动该程序:
注意:我假设您要将该程序编译到Linux®工作站。 如果不是这种情况,请阅读nweb文章,以获取有关在其他UNIX操作系统上编译程序的详细信息。
清单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 Debugger进行调试,稍后将使用它。
接下来,要验证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中的第2行)。
清单4.开始跟踪nweb
$ strace -e trace=network -p 4009
accept(0,
请注意,跟踪已在调用网络accept()
函数的过程中停止。 几次刷新浏览器中的http://localhost:9090
页面,并注意每次刷新页面时显示的strace。 那不是很好吗? 您正在查看的是HTTP服务器nweb
进行的低级网络调用,而您的Web浏览器已对其进行了调用。 简而言之, nweb
接受您浏览器的呼叫。
当运行strace的xterm具有窗口焦点时,可以通过按Ctrl + C停止跟踪正在运行的nweb进程上的网络调用。
转到GDB调试器
如您所见,strace可以是学习用户程序如何通过某些系统调用与操作系统交互的好程序。 GDB调试器还可以将自身附加到当前正在运行的进程,并帮助您进行更深入的研究。
GDB调试器是一个非常有用的工具,Internet上可以找到许多有关它的信息。 调试器通常是有价值的工具,负责计算机系统开发和维护的任何人都应该知道如何使用它们。 因此,当nweb仍在另一个xterm会话中运行时,请按Ctrl + C停止strace,然后通过键入清单5中所示的命令启动GDB Debugger。
清单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所示。
列出和反汇编命令
两个重要的GDB Debugger命令是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.反汇编命令
(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 PowerPC®的计算机)上运行,则外观将大不相同。
现场观看
因为你看一个实际运行的程序,你可以设置断点,然后看看该程序,因为它回复到浏览器的请求和传输.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 Debugger xterm中滚动。 与strace一样,您可以通过按Ctrl + C停止调试nweb服务器。 停止跟踪之后,可以通过键入quit
命令退出GDB调试器。
清单11. GDB Debugger 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
命令来暂停程序,否则该页面将在您离开会话时消失。
与往常一样,您可以从其手册和信息页面中学到很多关于strace和GDB Debugger之类的工具。 确保使用UNIX提供的工具!
尽可能多地学习
尽可能多地了解正在使用的计算机,这并不是一件坏事,在过程中获得乐趣也不是一件坏事。 UNIX实际上通过提供诸如strace和GDB Debugger之类的工具以及手册页和信息页中的大量信息来鼓励您进行探索和学习。 计算机是我们思维的延伸,我们对计算机的了解越多,它们就越有用。
翻译自: https://www.ibm.com/developerworks/aix/library/au-unix-strace.html