从 pstack 实现来看 /proc 目录
2015-10-11 23:43
507 查看
从 pstack 实现来看 /proc 目录
实现
pstack 是 Linux 下查看进程运行堆栈的程序,如分析一个进程 ID 为 1518 的程序:[root@AlexWoo-CentOS ~]# pstack 1518 #0 0x0076c424 in __kernel_vsyscall () #1 0x009b2508 in __epoll_wait_nocancel () from /lib/libc.so.6 #2 0x080688c8 in ngx_epoll_process_events () #3 0x080613ac in ngx_process_events_and_timers () #4 0x0806715f in ngx_worker_process_cycle () #5 0x080659b4 in ngx_spawn_process () #6 0x08066604 in ngx_start_worker_processes () #7 0x080676c7 in ngx_master_process_cycle () #8 0x0804b6d3 in main () [root@AlexWoo-CentOS ~]#
pstack 其实是一个 Shell 脚本,位于 /usr/bin 目录下
[root@AlexWoo-CentOS ~]# cat /usr/bin/pstack #!/bin/sh if test $# -ne 1; then echo "Usage: `basename $0 .sh` <process-id>" 1>&2 exit 1 fi # 用于查看指定的进程是否存在 if test ! -r /proc/$1; then echo "Process $1 not found." 1>&2 exit 1 fi # 用于查看指定进程是否为多线程运行: # 对于新的内核,直接查看 /proc/pid/task 下文件数是否大于 1, # 该目录存放了当前运行进程的所有线程 ID # 对于旧的内核,查看 /proc/pid/maps 文件中进程是否链接了 pthread 库, # 这种方法并不一定准确,因为链接了 pthread 库不代表该进程有多线程 # 这里主要原因还是上面说的,gdb 中的 bt 命令只能查看主线程的堆栈, # 而查看所有线程的堆栈需要使用 thread apply all bt # GDB doesn't allow "thread apply all bt" when the process isn't # threaded; need to peek at the process to determine if that or the # simpler "bt" should be used. backtrace="bt" if test -d /proc/$1/task ; then # Newer kernel; has a task/ directory. if test `/bin/ls /proc/$1/task | /usr/bin/wc -l` -gt 1 2>/dev/null ; then backtrace="thread apply all bt" fi elif test -f /proc/$1/maps ; then # Older kernel; go by it loading libpthread. if /bin/grep -e libpthread /proc/$1/maps > /dev/null 2>&1 ; then backtrace="thread apply all bt" fi fi GDB=${GDB:-/usr/bin/gdb} if $GDB -nx --quiet --batch --readnever > /dev/null 2>&1; then readnever=--readnever else readnever= fi # 这里调用 gdb 接入内存,并使用 bt 或 thread apply all bt 查看程序堆栈 # --quiet 用于关闭 gdb 接入的版本信息显示 # -nx 便是不读取 .gdbinit 文件 ^-^ 具体这个文件读与不读有什么影响不太清楚 # --readnever 是关闭堆栈中函数所在文件位置的信息,如果需要实际可以把这个参数删掉 # 这里 /proc/pid/exe 是进程实际执行文件的软链接 # Run GDB, strip out unwanted noise. $GDB --quiet $readnever -nx /proc/$1/exe $1 <<EOF 2>&1 | set width 0 set height 0 set pagination no $backtrace EOF /bin/sed -n \ -e 's/^\((gdb) \)*//' \ -e '/^#/p' \ -e '/^Thread/p'
总结
pstack 使用 gdb 实现了函数堆栈的查看运行中的进程,在 /proc 目录下会有一个以进程 pid 命名的目录。实际内核在处理过程中,会把进程运行的内存挂载到 /proc 目录下的文件上,不要轻易对这个目录下的文件进行操作,也不要把自己的文件放到该目录下
/proc/$pid/maps 文件下有执行程序当前链接的库的信息
/proc/$pid/task 目录下为执行进程的所有线程的线程 ID (每个线程一个目录),以此,我们可以写一个脚本,用于查看系统中哪些程序是多线程运行的
#!/bin/bash procspid=`/bin/ls /proc/ | grep [0-9][0-9]*` for proc in $procspid do if test `/bin/ls /proc/$proc/task | /usr/bin/wc -l` -gt 1 then echo $proc fi done
进一步探讨,我们发现 /proc/$pid 目录下面还有 fd 和 fdinfo 目录
[root@AlexWoo-CentOS 1518]# ls -l fd fdinfo fd: 总用量 0 lrwx------. 1 nobody nobody 64 9月 26 18:47 0 -> /dev/pts/0 lrwx------. 1 nobody nobody 64 9月 26 18:47 1 -> /dev/pts/0 l-wx------. 1 nobody nobody 64 9月 26 18:08 2 -> /usr/local/nginx/logs/error.log l-wx------. 1 nobody nobody 64 9月 26 18:47 4 -> /usr/local/nginx/logs/error.log l-wx------. 1 nobody nobody 64 9月 26 18:47 5 -> /usr/local/nginx/logs/access.log lrwx------. 1 nobody nobody 64 9月 26 18:47 6 -> socket:[12574] lrwx------. 1 nobody nobody 64 9月 26 18:47 7 -> socket:[12578] lrwx------. 1 nobody nobody 64 9月 26 18:47 8 -> [eventpoll] lrwx------. 1 nobody nobody 64 9月 26 18:47 9 -> [eventfd] fdinfo: 总用量 0 -r--------. 1 nobody nobody 0 9月 26 18:47 0 -r--------. 1 nobody nobody 0 9月 26 18:47 1 -r--------. 1 nobody nobody 0 9月 26 18:47 2 -r--------. 1 nobody nobody 0 9月 26 18:47 4 -r--------. 1 nobody nobody 0 9月 26 18:47 5 -r--------. 1 nobody nobody 0 9月 26 18:47 6 -r--------. 1 nobody nobody 0 9月 26 18:47 7 -r--------. 1 nobody nobody 0 9月 26 18:47 8 -r--------. 1 nobody nobody 0 9月 26 18:47 9 [root@AlexWoo-CentOS 1518]# cat fdinfo/9 pos: 0 flags: 02 [root@AlexWoo-CentOS 1518]# cat fdinfo/0 pos: 0 flags: 0100002 [root@AlexWoo-CentOS 1518]# cat fdinfo/4 pos: 132296 flags: 0102001 [root@AlexWoo-CentOS 1518]# cat fdinfo/2 pos: 132296 flags: 0102001
可以看见:
fd 目录下的数字是当前进程打开文件的文件描述符,0 是标准输入,1 是标准输出,2 是标准错误,他们分别被指向了 /dev/pts/0,/dev/pts/0 和 /usr/local/nginx/logs/error.log 下,8 是一个 epollfd
fdinfo 中的文件描述符保存了当前文件的偏移量 pos 和文件设置的标识位 flags
实际上在 /proc/pid/task/tid 下也有 fd 和 fdinfo 目录,只不过这两个目录存放的是对应线程连接的文件描述符信息
还有一些其他信息,可以查找相关资料进一步学习
相关文章推荐
- Linux socket 初步
- linux lsof详解
- linux 文件权限
- Linux 执行数学运算
- 10 篇对初学者和专家都有用的 Linux 命令教程
- Linux 与 Windows 对UNICODE 的处理方式
- Ubuntu12.04下QQ完美走起啊!走起啊!有木有啊!
- 解決Linux下Android开发真机调试设备不被识别问题
- 运维入门
- 运维提升
- Linux 自检和 SystemTap
- Ubuntu Linux使用体验
- c语言实现hashmap(转载)
- Linux 信号signal处理机制
- linux下mysql添加用户
- Scientific Linux 5.5 图形安装教程
- 基于 Linux 集群环境上 GPFS 的问题诊断
- 谁是桌面王者?Win PK Linux三大镇山之宝
- vivi下重新调整分区