您的位置:首页 > 运维架构 > Linux

从 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