1. 进程号
- 在计算机中,每一个进程都有一个进程号,进程号类似于一个索引,操作系统就是通过这个进程号快速地找到进程。在 linux 使用
ps -aux
查看进程,可以看到进程号pid
:
root@swd-Lenovo-G40-80:/proc/4234# ps -aux | more
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.1 225368 9136 ? Ss 10月29 0:02 /sbin/init splash
root 2 0.0 0.0 0 0 ? S 10月29 0:00 [kthreadd]
root 3 0.0 0.0 0 0 ? I< 10月29 0:00 [rcu_gp]
root 4 0.0 0.0 0 0 ? I< 10月29 0:00 [rcu_par_gp]
root 6 0.0 0.0 0 0 ? I< 10月29 0:00 [kworker/0:0H-kb]
root 8 0.0 0.0 0 0 ? I< 10月29 0:00 [mm_percpu_wq]
root 9 0.0 0.0 0 0 ? S 10月29 0:00 [ksoftirqd/0]
root 10 0.0 0.0 0 0 ? I 10月29 0:06 [rcu_sched]
root 11 0.0 0.0 0 0 ? S 10月29 0:00 [migration/0]
root 12 0.0 0.0 0 0 ? S 10月29 0:00 [idle_inject/0]
root 14 0.0 0.0 0 0 ? S 10月29 0:00 [cpuhp/0]
root 15 0.0 0.0 0 0 ? S 10月29 0:00 [cpuhp/1]
root 16 0.0 0.0 0 0 ? S 10月29 0:00 [idle_inject/1]
....
root 138 0.0 0.0 0 0 ? S 10月29 0:00 [oom_reaper]
- 在上面
1
中获取到的进程号,我们就可以去/proc
中查看进程的具体信息,在/proc
中每一个数字,就是对应的一个进程的pid
,进入到进程对应的pid
的进程的文件夹,里面就可以看到进程的具体信息:
root@swd-Lenovo-G40-80:/proc# ls
1 1176 1285 1354 1456 1736 201 28 3479 3708 397 47 8 889 93 cgroups irq mtrr thread-self
10 119 1299 1359 1471 18 21 29 3495 3739 4 543 800 89 933 cmdline kallsyms net timer_list
101 12 1300 1360 1474 187 22 295 35 3750 40 575 802 892 94 consoles kcore pagetypeinfo tty
102 1202 1304 1369 1496 188 223 296 351 38 4000 591 805 9 95 cpuinfo keys partitions uptime
104 1219 1311 137 15 189 226 299 3510 3802 4002 592 806 90 96 crypto key-users pressure version
2. 建立demo
来分析进程
- 创建一个 demo:
// filename: pmaps1.c
#include <stdio.h>
#include <stdlib.h>int main()
{char *pStr;pStr = (char *)malloc(2);while (1); // 死循环return 0;
}
- 编译:
gcc -o pmaps1 pmaps.c
- 运行:
./pmaps1
- 查看进程的 pid(pid 为 4345):``
root@swd-Lenovo-G40-80:/proc# ps -aux | grep pmaps1
swd 4345 100 0.0 4516 808 pts/3 R+ 22:34 4:43 ./pmaps1
- 进入
/proc/4345
:cd /proc/4345
- 查看该进程的信息:
root@swd-Lenovo-G40-80:/proc/4345# ls
arch_status clear_refs cwd gid_map maps net oom_score_adj root smaps status uid_map
attr cmdline environ io mem ns pagemap sched smaps_rollup syscall wchan
autogroup comm exe limits mountinfo numa_maps patch_state schedstat stack task
auxv coredump_filter fd loginuid mounts oom_adj personality sessionid stat timers
cgroup cpuset fdinfo map_files mountstats oom_score projid_map setgroups statm timerslack_ns
可以看到,能够获取到的信息挺多的!具体需要什么信息,就去查找,下面说几个常用的。
3. 几个常用的进程信息
- 打开的文件描述符,可以看到,进程打开的文件描述符,其中
99
是因为这个进程我是在vscode
中远程执行的,所以它有一个指向vscode
的文件描述符,0
,1
,2
是进程的标准输入输出,指向设备文件/dev/pts/3
:
root@swd-Lenovo-G40-80:/proc/4345# cd fd
root@swd-Lenovo-G40-80:/proc/4345/fd# ls
0 1 19 2 20 21 22 23 99root@swd-Lenovo-G40-80:/proc/4345/fd# ll
total 0
dr-x------ 2 swd swd 0 10月 31 22:40 ./
dr-xr-xr-x 9 swd swd 0 10月 31 22:34 ../
lrwx------ 1 swd swd 64 10月 31 22:47 0 -> /dev/pts/3
lrwx------ 1 swd swd 64 10月 31 22:47 1 -> /dev/pts/3
l-wx------ 1 swd swd 64 10月 31 22:47 19 -> /home/swd/.vscode-server/data/logs/20231031T213437/remoteagent.log
lrwx------ 1 swd swd 64 10月 31 22:47 2 -> /dev/pts/3
l-wx------ 1 swd swd 64 10月 31 22:47 20 -> /home/swd/.vscode-server/data/logs/20231031T213437/ptyhost.log
lrwx------ 1 swd swd 64 10月 31 22:47 21 -> /dev/ptmx
lrwx------ 1 swd swd 64 10月 31 22:47 22 -> /dev/ptmx
lrwx------ 1 swd swd 64 10月 31 22:47 23 -> /dev/ptmx
l-wx------ 1 swd swd 64 10月 31 22:47 99 -> /home/swd/.vscode-server/bin/da76f93349a72022ca4670c1b84860304616aaa2/vscode-remote-lock.swd.da76f93349a72022ca4670c1b84860304616aaa2
这个查看文件描述符有什么用呢?可以用来检查进程的文件描述符有没有被释放,这个很重要,我之前在公司做开发,使用了一个开源代码,就通过查看该进程的 /proc/pid/fd
文件夹发现文件描述符泄露。
- 虚拟内存分布(通过
/proc/pid/maps
查看)
这个也很常见,在面试C/C++
岗位的时候,经常会被问到,进程的内存布局。首先这个内存指的是虚拟内存。然后一般会回答主要有几个,当然除了这几个还有一些提的比较少的。而这些在/proc/pid/maps
均能够看到:
a. 堆区
b. 栈区
c. 数据段(静态、全局)
d. 代码段(存放程序编译后的二进制代码)
在/proc/pid/maps
可以看到(在下面有解释):
root@swd-Lenovo-G40-80:/proc/4345# cat maps
55d7d356d000-55d7d356e000 r-xp 00000000 08:02 17040544 /home/swd/pros/c--learn/0.test_codes/demos/pmaps1
55d7d376d000-55d7d376e000 r--p 00000000 08:02 17040544 /home/swd/pros/c--learn/0.test_codes/demos/pmaps1
55d7d376e000-55d7d376f000 rw-p 00001000 08:02 17040544 /home/swd/pros/c--learn/0.test_codes/demos/pmaps1
55d7d404c000-55d7d406d000 rw-p 00000000 00:00 0 [heap]
7f3f9f80f000-7f3f9f9f6000 r-xp 00000000 08:02 5505107 /lib/x86_64-linux-gnu/libc-2.27.so
7f3f9f9f6000-7f3f9fbf6000 ---p 001e7000 08:02 5505107 /lib/x86_64-linux-gnu/libc-2.27.so
7f3f9fbf6000-7f3f9fbfa000 r--p 001e7000 08:02 5505107 /lib/x86_64-linux-gnu/libc-2.27.so
7f3f9fbfa000-7f3f9fbfc000 rw-p 001eb000 08:02 5505107 /lib/x86_64-linux-gnu/libc-2.27.so
7f3f9fbfc000-7f3f9fc00000 rw-p 00000000 00:00 0
7f3f9fc00000-7f3f9fc29000 r-xp 00000000 08:02 5505040 /lib/x86_64-linux-gnu/ld-2.27.so
7f3f9fe12000-7f3f9fe14000 rw-p 00000000 00:00 0
7f3f9fe29000-7f3f9fe2a000 r--p 00029000 08:02 5505040 /lib/x86_64-linux-gnu/ld-2.27.so
7f3f9fe2a000-7f3f9fe2b000 rw-p 0002a000 08:02 5505040 /lib/x86_64-linux-gnu/ld-2.27.so
7f3f9fe2b000-7f3f9fe2c000 rw-p 00000000 00:00 0
7ffe5b0db000-7ffe5b0fc000 rw-p 00000000 00:00 0 [stack]
7ffe5b125000-7ffe5b128000 r--p 00000000 00:00 0 [vvar]
7ffe5b128000-7ffe5b12a000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall]
- 这个是 64 位机器,所以地址会显得比较大
- 每一列的含义:
第一列:本段在虚拟内存中的地址范围。
第二列:本段的权限。
第三列:偏移地址,即指本段映射地址在文件中的偏移。
第四列:主设备号与次设备号。
第五列:文件索引节点号。
第六列:映射的文件名。
a. 第一行: 权限是只读,并且可执行,是进程的代码段
55d7d356d000-55d7d356e000 r-xp 00000000 08:02 17040544 /home/swd/pros/c--learn/0.test_codes/demos/pmaps1
b. 第三行:权限是可读可写,但是没有执行权限,是进程的数据段
55d7d376e000-55d7d376f000 rw-p 00001000 08:02 17040544 /home/swd/pros/c--learn/0.test_codes/demos/pmaps1
c. 第四行:带有 [heap]
字样,说明是堆
55d7d404c000-55d7d406d000 rw-p 00000000 00:00 0 [heap]
d. 倒数第四行:带有 [stack]
字样,说明是栈
7ffe5b0db000-7ffe5b0fc000 rw-p 00000000 00:00 0 [stack]
- 上面就说完了面试的内存布局了。那还剩下的是什么呢?
i. ld-2.27共享库在 maps 的记录,每个共享库对应着3~4行,对应着数据段和代码段,主要是新的编译器对应的出来更加细分的段,有一些含义我也不清楚。
7f3f9f80f000-7f3f9f9f6000 r-xp 00000000 08:02 5505107 /lib/x86_64-linux-gnu/libc-2.27.so
7f3f9f9f6000-7f3f9fbf6000 ---p 001e7000 08:02 5505107 /lib/x86_64-linux-gnu/libc-2.27.so
7f3f9fbf6000-7f3f9fbfa000 r--p 001e7000 08:02 5505107 /lib/x86_64-linux-gnu/libc-2.27.so
7f3f9fbfa000-7f3f9fbfc000 rw-p 001eb000 08:02 5505107 /lib/x86_64-linux-gnu/libc-2.27.so
7f3f9fbfc000-7f3f9fc00000 rw-p 00000000 00:00 0
7f3f9fc00000-7f3f9fc29000 r-xp 00000000 08:02 5505040 /lib/x86_64-linux-gnu/ld-2.27.so
7f3f9fe12000-7f3f9fe14000 rw-p 00000000 00:00 0
7f3f9fe29000-7f3f9fe2a000 r--p 00029000 08:02 5505040 /lib/x86_64-linux-gnu/ld-2.27.so
7f3f9fe2a000-7f3f9fe2b000 rw-p 0002a000 08:02 5505040 /lib/x86_64-linux-gnu/ld-2.27.so
ii. 下面的段我现在也不认识
7ffe5b125000-7ffe5b128000 r--p 00000000 00:00 0 [vvar]
7ffe5b128000-7ffe5b12a000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall]
好了,介绍完了这个 maps 的文件后,那这个文件有什么用呢?
作用可多了,对一些进程进行内存优化,先看这个文件:
- 先看堆区是否太大,在
malloc/new
的时候有没有开辟的太多内存 - 看栈区的大小,如果太大,看能不能合理调整结构体,栈变量有没有冗余。
看内存是否泄露:
3. 看进程的业务已经稳定了,观察堆区的大小是否在变大。变大就应该怀疑内存有泄露。
上面看地址的范围,很难计算出各个段占用内存的大小。那么还有一个文件可以看到占用的大小:
cat /proc/pid/smaps