###进程分析

经常会遇到哪些名字很奇怪,不确定做什么的进程,次数就需要掌握一定的程序分析能力

#####线程

有时候需要查看某个进程的线程,在linux可以使用ps的top和htop等方式查看线程。

# ps命令查看线程
$ ps -T -p <pid>
实例
$ ps -ef | grep nginx
root     24827     1  0 3月05 ?       00:00:00 nginx: master process sbin/nginx
nobody   24828 24827  0 3月05 ?       00:00:08 nginx: worker process
opslab   30386 30360  0 23:16 pts/1    00:00:00 grep --color=auto nginx
[opslab@VM_0_14_centos ~]$ ps -T -p 24827
  PID  SPID TTY          TIME CMD
24827 24827 ?        00:00:00 nginx


# 使用Top
# 可以在top交互界面按H查看线程,但是那样不是很清晰
$ top -H -p <pid>
实例:
$ ps -ef | grep nginx
root     24827     1  0 3月05 ?       00:00:00 nginx: master process sbin/nginx
nobody   24828 24827  0 3月05 ?       00:00:08 nginx: worker process
opslab   30514 30360  0 23:19 pts/1    00:00:00 grep --color=auto nginx
$ top -H -p 24827
top - 23:19:46 up 77 days,  2:01,  2 users,  load average: 0.00, 0.01, 0.05
Threads:   1 total,   0 running,   1 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.2 us,  0.1 sy,  0.0 ni, 99.7 id,  0.1 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  3882032 total,   171240 free,   165604 used,  3545188 buff/cache
KiB Swap:        0 total,        0 free,        0 used.  3410716 avail Mem

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND
24827 root      20   0   75952   1824    236 S  0.0  0.0   0:00.00 nginx
lsof/pstack/strace/ltrace

lsof是一个查看当前文件系统的工具,在linux下一切皆以文件的形式存在。lsof可以打开:

​ 1.普通文件目录

​ 2.网络文件

​ 3.字符或设备文件

​ 4.函数共享库

​ 5.管道,命令管道

​ 6.符号链接

​ 7.NFSfile、网络socket、unix socket

关于lsof的输出字段的说明

  • command 进程的民粹

  • PID 进程的id

  • PPID 父进程的ID

  • USER 进程所有者

  • PGID 进程所属组

  • FD 文件描述符

    (1)cwd:表示current work dirctory,即:应用程序的当前工作目录,这是该应用程序启动的目录,除非它本身对这个目录进行更改 (2)txt :该类型的文件是程序代码,如应用程序二进制文件本身或共享库,如上列表中显示的 /sbin/init 程序 (3)lnn:library references (AIX); (4)er:FD information error (see NAME column); (5)jld:jail directory (FreeBSD); (6)ltx:shared library text (code and data); (7)mxx :hex memory-mapped type number xx. (8)m86:DOS Merge mapped file; (9)mem:memory-mapped file; (10)mmap:memory-mapped device; (11)pd:parent directory; (12)rtd:root directory; (13)tr:kernel trace file (OpenBSD); (14)v86 VP/ix mapped file; (15)0:表示标准输入 (16)1:表示标准输出 (17)2:表示标准错误 一般在标准输出、标准错误、标准输入后还跟着文件状态模式:r、w、u等 (1)u:表示该文件被打开并处于读取/写入模式 (2)r:表示该文件被打开并处于只读模式 (3)w:表示该文件被打开并处于 (4)空格:表示该文件的状态模式为unknow,且没有锁定 (5)-:表示该文件的状态模式为unknow,且被锁定 同时在文件状态模式后面,还跟着相关的锁 (1)N:for a Solaris NFS lock of unknown type; (2)r:for read lock on part of the file; (3)R:for a read lock on the entire file; (4)w:for a write lock on part of the file;(文件的部分写锁) (5)W:for a write lock on the entire file;(整个文件的写锁) (6)u:for a read and write lock of any length; (7)U:for a lock of unknown type; (8)x:for an SCO OpenServer Xenix lock on part of the file; (9)X:for an SCO OpenServer Xenix lock on the entire file; (10)space:if there is no lock.

  • TYPE 文件类型,常见的文件类型

    • DIR 表示目录
    • CHR表示字符类型
    • BLK 块设备类型
    • UNIX UNIX套机字
    • FIFO 先进先出队列
    • IPV4 网络协议IP套接字
  • DEVICE 指定磁盘的名称

  • SIZE 文件的大小

  • NODE 索引节点

  • NAME 打开文件的确切名称

usage: lsof [选项]
选项:
  -a:列出打开文件存在的进程;
  -c<进程名>:列出指定进程所打开的文件;
  -g:列出GID号进程详情;
  -d<文件号>:列出占用该文件号的进程;
  +d<目录>:列出目录下被打开的文件;
  +D<目录>:递归列出目录下被打开的文件;
  -n<目录>:列出使用NFS的文件;
  -i<条件>:列出符合条件的进程。(4、6、协议、:端口、 @ip )
  -p<进程号>:列出指定进程号所打开的文件;
  -u:列出UID号进程详情;
  -h:显示帮助信息;
  -v:显示版本信息。
实例:
$ lsof | more

  COMMAND    PID    USER   FD      TYPE             DEVICE   SIZE/OFF       NODE NAME
  loginwind  106 monsoon  cwd       DIR                1,4        992          2 /
  loginwind  106 monsoon  txt       REG                1,4    1241072 8590278304 /System/Library/CoreServices/loginwindow.app/Contents/MacOS/loginwindow

$ lsof /bin/bash //查找某个文件相关的进程
$ lsof -u username	//列出某个用户打开的文件信息
$ lsof -c mysql //列出某个进程打开的文件信息
$ lsof -u test -c mysql //列出某个用户以某个进程打开的文件信息
$ lsof -p 1314 //列出某个进程打开的文件
$ lsof -l //列出所有的网络连接
$ lsof -i tcp //列出搜优的tcp网络连接信息
$ lsof -i :3306 //列出谁在使用某个端口
$ lsof -a -u test -i //列出某个用户的所有活跃的网络端口
$ lsof -d descript // 根据文件描述符列出对应的文件信息

pstack命令可以显示每个进程的栈跟踪,pstack命令必须由相应进程的属主或root运行,此命令允许使用的唯一选项是要检查的进程的PID。

ps -ef | grep top
opslab   11230 11196  0 16:24 pts/0    00:00:00 top
opslab   11237 11196  0 16:24 pts/0    00:00:00 grep --color=auto top

[1]+  已停止               top
[opslab@VM_0_14_centos ~]$ pstack 11230
#0  0x00007fa21ca991d7 in raise () from /lib64/libc.so.6
#1  0x00000000004071e2 in sig_paused ()
#2  <signal handler called>
#3  0x00007fa21cb51bac in tcsetattr () from /lib64/libc.so.6
#4  0x00000000004033ec in main ()

strace能追踪到一个程序所指向的系统调用,当想知道某个程序和操作系统如何交互的时候,使用该命令极其方便的,比如想知道执行了那些系统调用,并且以何种顺序执行。strace后面可以任何命令,它将列出许多的系统调用。

strace的最简单的用法就是执行一个指定的命令,在指定的命令结束之后它也就退出了,在命令执行的过程中,strace会记录和解析命令进程的所有系统调用以及这个进程所接收到的所有的信号值。(strace在linux下用来跟踪某个进程的系统调用,在solaris下,对应的是dtrace,在mac下,对应的命令是:dtruss)

strace  [  -dffhiqrtttTvxx  ] [ -acolumn ] [ -eexpr ] ...
    [ -ofile ] [-ppid ] ...  [ -sstrsize ] [ -uusername ]
    [ -Evar=val ] ...  [ -Evar  ]...
    [ command [ arg ...  ] ]

strace  -c  [ -eexpr ] ...  [ -Ooverhead ] [ -Ssortby ]
    [ command [ arg...  ] ]
    
选项:
  -c 统计每一系统调用的所执行的时间,次数和出错的次数等.
  -d 输出strace关于标准错误的调试信息.
  -f 跟踪由fork调用所产生的子进程.
  -ff 如果提供-o filename,则所有进程的跟踪结果输出到相应的filename.pid中,pid是各进程的进程号.
  -F 尝试跟踪vfork调用.在-f时,vfork不被跟踪.
  -h 输出简要的帮助信息.
  -i 输出系统调用的入口指针.
  -q 禁止输出关于脱离的消息.
  -r 打印出相对时间关于,,每一个系统调用.
  -t 在输出中的每一行前加上时间信息.
  -tt 在输出中的每一行前加上时间信息,微秒级.
  -ttt 微秒级输出,以秒了表示时间.
  -T 显示每一调用所耗的时间.
  -v 输出所有的系统调用.一些调用关于环境变量,状态,输入输出等调用由于使用频繁,默认不输出.
  -V 输出strace的版本信息.
  -x 以十六进制形式输出非标准字符串
  -xx 所有字符串以十六进制形式输出.
  -a column 设置返回值的输出位置.默认 为40.
  -e expr 指定一个表达式,用来控制如何跟踪.格式:[qualifier=][!]value1[,value2]...
  qualifier只能是 trace,abbrev,verbose,raw,signal,read,write其中之一.value是用来限定的符号或数字.默认的 qualifier是 trace.感叹号是否定符号.例如:-eopen等价于 -e trace=open,表示只跟踪open调用.而-etrace!=open 表示跟踪除了open以外的其他调用.有两个特殊的符号 all 和 none. 注意有些shell使用!来执行历史记录里的命令,所以要使用\\.
  -e trace=set 只跟踪指定的系统 调用.例如:-e trace=open,close,rean,write表示只跟踪这四个系统调用.默认的为set=all.
  -e trace=file 只跟踪有关文件操作的系统调用.
  -e trace=process 只跟踪有关进程控制的系统调用.
  -e trace=network 跟踪与网络有关的所有系统调用.
  -e strace=signal 跟踪所有与系统信号有关的 系统调用
  -e trace=ipc 跟踪所有与进程通讯有关的系统调用
  -e abbrev=set 设定strace输出的系统调用的结果集.-v 等与 abbrev=none.默认为abbrev=all.
  -e raw=set 将指定的系统调用的参数以十六进制显示.
  -e signal=set 指定跟踪的系统信号.默认为all.如 signal=!SIGIO(或者signal=!io),表示不跟踪SIGIO信号.
  -e read=set 输出从指定文件中读出 的数据.例如: -e read=3,5
  -e write=set 输出写入到指定文件中的数据.
  -o filename 将strace的输出写入文件filename
  -p pid 跟踪指定的进程pid.
  -s strsize 指定输出的字符串的最大长度.默认为32.文件名一直全部输出.
  -u username 以username的UID和GID执行被跟踪的命令

实例:
  $ strace -p 23488
  Process 23488 attached
  futex(0x817d70, FUTEX_WAIT, 0, NULL)    = 0
  epoll_wait(4, {{EPOLLOUT, {u32=2544143856, u64=140585413676528}}}, 128, 0) = 1
  pselect6(0, NULL, NULL, NULL, {0, 3000}, 0) = 0 (Timeout)
  futex(0x817d70, FUTEX_WAIT, 0, NULL)    = 0
  epoll_wait(4, {{EPOLLOUT, {u32=2544143856, u64=140585413676528}}}, 128, 0) = 1
  pselect6(0, NULL, NULL, NULL, {0, 3000}, 0) = 0 (Timeout)
  futex(0x817d70, FUTEX_WAIT, 0, NULL)    = 0
  epoll_wait(4, {{EPOLLOUT, {u32=2544143856, u64=140585413676528}}}, 128, 0) = 1
  epoll_wait(4, {{EPOLLIN, {u32=2544144240, u64=140585413676912}}}, 128, -1) = 1
  clock_gettime(CLOCK_MONOTONIC, {6636609, 10696304}) = 0
  futex(0x817598, FUTEX_WAKE, 1)          = 1
  accept4(3, {sa_family=AF_INET6, sin6_port=htons(51438), inet_pton(AF_INET6, "::ffff:127.0.0.1", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [28], SOCK_CLOEXEC|SOCK_NONBLOCK) = 5
  
  // 跟踪系统调用
  $ strace ./xxx
  
  // 系统调用统计
  $ strace -c ./xxx
  
  // 统计现有的进程
  $ strace -p pid
  
  // 显示系统调用之间的相对时间戳
  $ strace -r ls
  
  // 使用保存结果
  $ strace -o process_strace.log -p pid
  
  // 跟踪指定的系统调用 如open、write
  $ strace -e open ls
  
  //查找程序读取的配置文件
  $ strace nginx 2>&1 | grep nginx.conf

ltrace命令是用来跟踪进程调用库函数的情况

usage: ltrace [选项] [参数]
选项:
  -a 对齐具体某个列的返回值。
  -c 计算时间和调用,并在程序退出时打印摘要。
  -C 解码低级别名称(内核级)为用户级名称。
  -d 打印调试信息。
  -e 改变跟踪的事件。
  -f 跟踪子进程。
  -h 打印帮助信息。
  -i 打印指令指针,当库调用时。
  -l 只打印某个库中的调用。
  -L 不打印库调用。
  -n, --indent=NR 对每个调用级别嵌套以NR个空格进行缩进输出。
  -o, --output=file 把输出定向到文件。
  -p PID 附着在值为PID的进程号上进行ltrace。
  -r 打印相对时间戳。
  -s STRLEN 设置打印的字符串最大长度。
  -S 显示系统调用。
  -t, -tt, -ttt 打印绝对时间戳。
  -T 输出每个调用过程的时间开销。
  -u USERNAME 使用某个用户id或组ID来运行命令。
  -V, --version 打印版本信息,然后退出。

实例:
	$ ltrace ./weixin
	
	//输出调用时间开销
	$ ltrace -T ./weixin
	
	// 显示系统调用
	$ ltrace -S ./weixin
	

#####ipcs/pmap/ldd/nm/size

ipcs该命令用于报告Linux中进程间通信设施的状态,显示的信息包括消息列表、共享内存和信号量的信息。

usage: ipcs [选项]
选项:
  -a:显示全部可显示的信息;
  -q:显示活动的消息队列信息;
  -m:显示活动的共享内存信息;
  -s:显示活动的信号量信息。
实例:
ipcs -a

--------- 消息队列 -----------
键        msqid      拥有者  权限     已用字节数 消息

------------ 共享内存段 --------------
键        shmid      拥有者  权限     字节     nattch     状态

--------- 信号量数组 -----------
键        semid      拥有者  权限     nsems

pmap命令用于报告进程的内存映射关系,其实它原本是sun机器上的,在linux只有有限的功能。

usage: pmap [选项] [参数]
选项:
  -x:显示扩展格式;
  -d:显示设备格式;
  -q:不显示头尾行;
  -V:显示指定版本。
实例:
  pmap -x 5371
  5371:   nginx: worker process                
  Address           Kbytes     RSS   Dirty Mode   Mapping
  0000000000400000     564     344       0 r-x--  nginx
  000000000068c000      68      68      60 rw---  nginx
  000000000069d000      56      12      12 rw---    [ anon ]
  000000000a0c8000    1812    1684    1684 rw---    [ anon ]
  0000003ac0a00000     112      40       0 r-x--  ld-2.5.so

ldd命令用于查看进程动态加载库的信息,运维的时候有时候需要定位依赖关系,可以使用该命令查看。

usage: ldd [选项] [参数]
选项:
  --version:打印指令版本号;
  -v:详细信息模式,打印所有相关信息;
  -u:打印未使用的直接依赖;
  -d:执行重定位和报告任何丢失的对象;
  -r:执行数据对象和函数的重定位,并且报告任何丢失的对象和函数;
  --help:显示帮助信息。
参数:
	指定可执行的程序或文件
实例:
$ ldd /bin/ls
linux-vdso.so.1 (0x00007ffeeffc3000)
libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007f8e631c7000)

nm命令被用于显示二进制目标文件的符号表

usage: nm [选项] [参数]
选项:
  -A:每个符号前显示文件名;
  -D:显示动态符号;
  -g:仅显示外部符号;
  -r:反序显示符号表。

size 可以查看查询被映射到内存映像所占用的大小消息,程序映射到内存中,从低地址到高地址会分为不同的段。这些段依次为(并不一定固定):

  • 代码段

    该段自读,可共享.代码段code segment通常是指用来存放程序执行代码的一块内存区域。该部分的大小在程序运行前已经确定。(很重要的一个段)

  • 数据段

    存储已被初始化了的静态数据,数据段data segment通常是指用来存放程序中已被初始化的全局变量的一块内存区域。

  • BSS段

    BSS是Block STARTED BY SYMBOL的简称,该段属于静态内存分配,通常用是用来存放程序中未被初始化的全局变量的一块内存区域

  • 堆Heap

    堆是用于存放进程运行中被动态分配的内存段,它的大小并不哭的,可动态伸缩。

  • 栈stack

    栈又被成为堆栈,用来存放程序临时创建的变量,函数调用时参数等。

$ size /usr/bin/ls
   text	   data	    bss	    dec	    hex	filename
 102801	   4792	   3360	 110953	  1b169	/usr/bin/ls
objdump/readelf

objdump是用来显示二进制文件的信息,就是以一种可阅读的格式展现二进制文件可能附加信息.

usage: objdump [选项] [参数]
常用选项:
  -f 显示文件头信息
  -D 反汇编所有section (-d反汇编特定section)
  -h 显示目标文件各个section的头部摘要信息
  -x 显示所有可用的头信息,包括符号表、重定位入口。-x 等价于 -a -f -h -r -t 同时指定。
  -i 显示对于 -b 或者 -m 选项可用的架构和目标格式列表。
  -r 显示文件的重定位入口。如果和-d或者-D一起使用,重定位部分以反汇编后的格式显示出来。
  -R 显示文件的动态重定位入口,仅仅对于动态目标文件有意义,比如某些共享库。
  -S 尽可能反汇编出源代码,尤其当编译的时候指定了-g这种调试参数时,效果比较明显。隐含了-d参数。
  -t 显示文件的符号表入口。类似于nm -s提供的信息
  
实例:
	$ objdump -i //查看文本是大端还是小端
	$ objdump -d main.o //反汇编
	

readelf 该用具和objdump命令提供的功能类似,只是更具体些,并且它不依赖于BFD库。

usage: readelf [选项] [参数]
常用选项:
	a –all 全部 等价于: -h -l -S -s -r -d -V -A -I

	-h –file-header 文件头

	-l –program-headers 程序 

	–segments An alias for –program-headers

	-S –section-headers 段头 Display the sections’ header

	--sections	
	An alias for –section-headers

	-e –headers 全部头 等价于: -h -l -S
	
实例:
	$ readelf -h /usr/bin/ls
    ELF 头:
      Magic:  7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
      Class:                             ELF64
      Data:                              2's complement, little endian
      Version:                           1 (current)
      OS/ABI:                            UNIX - System V
      ABI Version:                       0
      Type:                              EXEC (可执行文件)
      Machine:                           Advanced Micro Devices X86-64
      Version:                           0x1
      入口点地址:              0x404b48
      程序头起点:              64 (bytes into file)
      Start of section headers:          115696 (bytes into file)
      标志:             0x0
      本头的大小:       64 (字节)
      程序头大小:       56 (字节)
      Number of program headers:         9
      节头大小:         64 (字节)
      节头数量:         30
      字符串表索引节头: 29