用strace了解程序的工作原理
问题:在进程内打开一个文件,都有唯一一个文件描述符(fd: file descriptor)与这个文件对应。如果已知一个fd,如何获取这个fd所对应文件的完整路径?不管是Linux、FreeBSD或其他Unix系统都没有提供这样的API,那怎么办呢?
我们换个角度思考:Unix下有没有什么命令可以获取进程打开了哪些文件?使用 lsof 命令即可以知道程序打开了哪些文件,也可以了解一个文件被哪个进程打开。(平时工作中很常用,例如,使用 lsof -p PID来查找某个进程存放的位置)
1.#include<stdio.h>
2.#include<unistd.h>
3.#include<sys/types.h>
4.#include<sys/stat.h>
5.#include<fcntl.h>
6.
7.int main()
8.{
9. open("wcdj", O_CREAT|O_RDONLY);// open file foo
10. sleep(1200);// sleep 20 mins 方便调试
11.
12. return 0;
13.}
14./*
15.gcc -Wall -g -o testlsof testlsof.c
16../testlsof &
17.*/
18.
19.$ gcc -Wall -g -o testlsof testlsof.c
20.$ ./testlsof &
21.[1] 12371
22.$ strace -o lsof.strace lsof -p 12371
23.COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
24.testlsof 12371 gerryyang cwd DIR 8,4 4096 2359314 /data/home/gerryyang/test/HACK
25.testlsof 12371 gerryyang rtd DIR 8,1 4096 2 /
26.testlsof 12371 gerryyang txt REG 8,4 7739 2359364 /data/home/gerryyang/test/HACK/testlsof
27.testlsof 12371 gerryyang mem REG 8,1 1548470 1117263 /lib/libc-2.4.so
28.testlsof 12371 gerryyang mem REG 8,1 129040 1117255 /lib/ld-2.4.so
29.testlsof 12371 gerryyang mem REG 0,0 0 [stack] (stat: No such file or directory)
30.testlsof 12371 gerryyang 0u CHR 136,0 2 /dev/pts/0
31.testlsof 12371 gerryyang 1u CHR 136,0 2 /dev/pts/0
32.testlsof 12371 gerryyang 2u CHR 136,0 2 /dev/pts/0
33.testlsof 12371 gerryyang 3r REG 8,4 0 2359367 /data/home/gerryyang/test/HACK/wcdj
34.$ grep "wcdj" lsof.strace
35.readlink("/proc/12371/fd/3", "/data/home/gerryyang/test/HACK/wcdj", 4096) = 35
36.$ cd /proc/12371/fd
37.$ ls -l
38.总计 4
39.lrwx------ 1 gerryyang users 64 2012-03-23 14:14 0 -> /dev/pts/0
40.lrwx------ 1 gerryyang users 64 2012-03-23 14:14 1 -> /dev/pts/0
41.lrwx------ 1 gerryyang users 64 2012-03-23 14:14 2 -> /dev/pts/0
42.lr-x------ 1 gerryyang users 64 2012-03-23 14:14 3 -> /data/home/gerryyang/test/HACK/wcdj
用strace跟踪lsof的运行,输出结果保存在lsof.strace中。然后通过对lsof.strace内容的分析
从而了解到其实现原理是:
lsof利用了/proc/pid/fd目录。Linux内核会为每一个进程在/proc建立一个以其pid为名的目录用来保存进程的相关信息,而其子目录fd保存的是该进程打开的所有文件的fd。进入/proc/pid/fd目录下,发现每一个fd文件都是符号链接,而此链接就指向被该进程打开的一个文件。我们只要用readlink()系统调用就可以获取某个fd对应的文件了。
1.#include<stdio.h>
2.#include<string.h>
3.#include<sys/types.h>
4.#include<unistd.h>// readlink
5.#include<fcntl.h>
6.#include<sys/stat.h>
7.
8.int get_pathname_from_fd(int fd, char pathname[], int n)
9.{
10. char buf[1024];
11. pid_t pid;
12. bzero(buf, 1024);
13. pid = getpid();
14. snprintf(buf, 1024, "/proc/%i/fd/%i", pid, fd);// %i == %d
15.
16. return readlink(buf, pathname, n);
17.}
18.
19.int main()
20.{
21. int fd;
22. char pathname[4096] = {0};
23. bzero(pathname, 4096);
24. fd = open("wcdj", O_RDONLY);
25.
26. get_pathname_from_fd(fd, pathname, 4096);
27.
28. printf("fd=%d; pathname=%s\n", fd, pathname);
29.
30. return 0;
31.}
32./*
33.gcc -Wall -g -o GetPathByFd GetPathByFd.c
34.*/
ltrace的基本使用方法(针对前面的demo)
ltrace - A library call tracer
1.$ ltrace ./st1
2.__libc_start_main(0x8048494, 1, 0xbfe4a204, 0x8048500, 0x80484f0 <unfinished ...>
3.fopen("r", "r") = 0
4.puts("r"Error!
5.) = 7
6.sleep(3) = 0
7.fopen("r", "r") = 0
8.puts("r"Error!
9.) = 7
10.sleep(3) = 0
11.fopen("r", "r") = 0
12.puts("r"Error!
13.) = 7
14.sleep(3 <unfinished ...>
15.--- SIGINT (Interrupt) ---
16.+++ killed by SIGINT +++