最近学习自动驾驶系统时,碰到协程的概念。进程和线程已经迷了,又来个协程,看了很多资料后决定作总结,概括三者联系和区别,最后归结到协程在自动驾驶中的应用。初级程序员目标是搞清三者概念并应用到实际中,而资深工程师则需要在系统层面考虑三者的性能及实现代价,直到如今三者仍是Linux内核和各类编程语言持续更新完善的模块之一,所以理清三者的关系、编程应用和考量性能是进阶程序员的必修课。行文的目的,是对进程/线程/协程这一系列繁复的概念和知识点做一个全面的总结,同时尽量做到知识点讲精讲细讲全,甄别模糊概念,同时兼顾源码及编程实现,最后归结到Apollo的协程实现。
本系列文章分九篇讲解:
本章总结与进程有关的系统命令,包括:1. gnome图形查看命令;2. 静态查看命令ps;3. 动态查看命令top;向进程发送信号命令kill;5. 查看进程信息的伪文件系统/proc。下面逐一讲解。
gnome-system-monitor
展示界面如下,包括进程信息、系统资源和文件系统,其中进程信息的PID就是进程ID,它是独一无二的,其它字段都能看懂吧:
2. pstree
pstree(英文全称:display a tree of processes)) 命令将所有进程以树状图显示,树状图将会以 pid (如果有指定) 或是以 init 这个基本进程为根 (root),如果有指定使用者 id,则树状图会只显示该使用者所拥有的进程。命令参数如下:
-p 制定进程ID
-t 显示线程全名
-n 根据PID排序
-h 高亮显示当前进程和其父进程
-a 显示该进程的完整指令及参数, 如果是被记忆体置换出去的进程则会加上括号
-c 如果有重覆的进程名, 则分开列出(预设值是会在前面加上 *)
可以显示进程间关系,还可以显示制定ID的子进程和线程(比如浏览器),你还会看到你打开的网页线程:
//显示进程间关系
$ pstree -apnh
systemd,1 splash
├─systemd-journal,387
├─systemd-udevd,422
├─systemd-timesyn,922
│ └─{systemd-timesyn},925
//pgrep能查找当前正在运行的进程并列出符合条件的进程ID
pgrep firefox
2779
//显示制定ID的子进程和线程
$ pstree -p 2779 -t
firefox(2779)─┬─Privileged Cont(3078)─┬─{HTML5 Parser}(3119)
│ ├─{IPC I/O Child}(3084)
│ ├─{ImageBridgeChld}(3101)
│ ├─{ImageIO}(3090)
│ ├─{JS Watchdog}(3086)
ps即process status(进程状态),它的命令参数罗列如下:
内核使用一个简单些的数值范围,从0到139(包含)用来表示内部优先级,值越低,优先级越高。从0到99的范围专供实时进程使用。普通进程的nice值[20, +19]映射到范围100到139,因此实时进程的优先级总是比普通进程更高。其中nice可以作为命令制定优先级,比如:
//查看优先级
$ ps ax -o pid,nice,comm,user | head -5
PID NI COMMAND USER
1 0 systemd root
2 0 kthreadd root
3 -20 rcu_gp root
4 -20 rcu_par_gp root
//制定优先级:nice -n 优先级数字 进程名称
nice -n -4 vim & ##开启vim并指定进程优先级为-4,&表示后台运行
ps常用组合:
比如最常用的组合aux和axl,显示如下:
$ ps aux | head -10
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 226088 9676 ? Ss 2021 8:23 /sbin/init splash
root 2 0.0 0.0 0 0 ? S 2021 0:01 [kthreadd]
root 3 0.0 0.0 0 0 ? I< 2021 0:00 [rcu_gp]
root 4 0.0 0.0 0 0 ? I< 2021 0:00 [rcu_par_gp]
root 9 0.0 0.0 0 0 ? I< 2021 0:00 [mm_percpu_wq]
ps axl | head -5
F UID PID PPID PRI NI VSZ RSS WCHAN STAT TTY TIME COMMAND
4 0 1 0 20 0 226088 9676 - Ss ? 8:46 /sbin/init splash
1 0 2 0 20 0 0 0 - S ? 0:01 [kthreadd]
1 0 3 2 0 -20 0 0 - I< ? 0:00 [rcu_gp]
1 0 4 2 0 -20 0 0 - I< ? 0:00 [rcu_par_gp]
ps aux/axl中字段说明:USER—所属用户,PID/PPID/SPID—进程ID/父进程ID/系统进程ID,%CPU—CPU占用率,%MEM—内存占用率,VSZ—虚拟内存占用,RSS—实际内存占用,TTY—终端,STAT—状态,START—进程的启动时间,TIME—进程占用CPU的总时间,COMMAND—目录或文件名,F—进程旗标或权限 (process flags,4代表root,1表示仅能fork),PRI—优先级(-100…39),WCHAN:进程是否运行(-代表运行,其它代表进程正在睡眠的内核函数名称;该函数的名称是从/root/system.map文件中获得的)。
其中STAT(进程状态)中的值表示:
动态查看top,像windows的任务管理器,如下:
$ top
top - 14:34:05 up 28 days, 18:03, 1 user, load average: 0.41, 0.29, 0.20
Tasks: 380 total, 5 running, 271 sleeping, 0 stopped, 0 zombie
%Cpu(s): 1.5 us, 0.7 sy, 0.0 ni, 97.6 id, 0.0 wa, 0.0 hi, 0.1 si, 0.0 st
KiB Mem : 32485348 total, 14727192 free, 10751656 used, 7006500 buff/cache
KiB Swap: 15999996 total, 15999996 free, 0 used. 19731244 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1708 shaw 20 0 5212864 533048 156940 R 7.3 1.6 111:16.31 gnome-shell
1421 shaw 20 0 25.122g 200084 154944 R 5.0 0.6 388:14.29 Xorg
2779 shaw 20 0 61.363g 0.013t 0.010t S 4.7 43.9 1741:16 firefox
30446 shaw 20 0 4025532 693656 233384 S 4.3 2.1 520:34.93 Web Content
1777 shaw 20 0 3171592 21096 16636 S 2.0 0.1 605:03.78 pulseaudio
其中:VIRT:virtual memory usage 虚拟内存(申请内存),RES:resident memory usage 常驻内存(占用内存),SHR:shared memory 共享内存(共享库大小)。
top 运行中可以通过 top 的内部命令对进程的显示方式进行控制。内部命令如下:
使用kil给进程发送信号,使用kill -l查看kill所支持的信号:
$ kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
//杀死某个进程
kill -9 PID
由于信号非常多,前期学的时候记住一些常用的即可(kill -数字/信号):
proc文件系统是一个伪文件系统,它只存在内存当中,而不占用外存空间。它以文件系统的方式为访问系统内核数据的操作提供接口。用户和应用程序可以通过proc得到相关的信息,并可以改变内核的某些参数。由于系统的信息是动态改变的,所以用户或应用程序读取proc文件时,proc文件系统是动态从系统内核读出所需信息并提交的。根据/proc的用法,本小节分为系统状态、进程状态和如何评估进程内存三部分
/proc文件系统下的多种文件提供的系统信息不是针对某个特定进程的,而是能够在整个系统范围的上下文中使用。下面列出的这些文件或子文件夹,并不是都是在你的系统中存在,这取决于你的内核配置和装载的模块。另外,在 proc 下还有三个很重要的目录:net,scsi 和 sys。 sys 目录是可写的,可以通过它来访问或修改内核的参数,而 net 和 scsi 则依赖于内核配置。例如,如果系统不支持 scsi,则 scsi 目录不存在。下面举些例子。
$ cat /proc/cmdline
BOOT_IMAGE=/boot/vmlinuz-4.4.0-89-generic root=UUID=bef418fa-4202-4513-b39b-cde6a5d9753f ro quiet splash vt.handoff=7
$ ls /proc/sys
debug dev fs kernel net proc sunrpc vm
--------------------------------------------------------------------------------
ls /proc/sys/fs
aio-max-nr dentry-state file-nr lease-break-time overflowgid
aio-nr dir-notify-enable inode-nr leases-enable overflowuid
binfmt_misc file-max inode-state mqueue quota
--------------------------------------------------------------------------------
ls /proc/sys/net
core ethernet ipv4 ipv6 unix
除了以上介绍的这些,还有的是一些以数字命名的目录,它们是进程目录。系统中当前运行的每一个进程都有对应的一个目录在proc 下,以进程的PID为目录名,它们是读取进程信息的接口。而self目录则是读取进程本身的信息接口,是一个link。其它目录如下:
cat status
Name: ibus-engine-lib
Umask: 0002
State: S (sleeping)
Tgid: 3402
Ngid: 0
Pid: 3402
PPid: 1867
TracerPid: 0
Uid: 1000 1000 1000 1000
Gid: 1000 1000 1000 1000
FDSize: 64
Groups: 4 24 27 30 46 116 126 127 1000
NStgid: 3402
NSpid: 3402
NSpgid: 1867
NSsid: 1419
VmPeak: 295844 kB
VmSize: 295844 kB
VmLck: 0 kB
VmPin: 0 kB
VmHWM: 9984 kB
VmRSS: 9984 kB
RssAnon: 1100 kB
RssFile: 8884 kB
RssShmem: 0 kB
VmData: 25960 kB
VmStk: 132 kB
VmExe: 780 kB
VmLib: 14280 kB
VmPTE: 200 kB
VmSwap: 0 kB
HugetlbPages: 0 kB
CoreDumping: 0
THP_enabled: 1
Threads: 4
SigQ: 0/126699
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: 0000000001001000
SigCgt: 0000000180004002
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 0000003fffffffff
CapAmb: 0000000000000000
NoNewPrivs: 0
Seccomp: 0
Speculation_Store_Bypass: thread vulnerable
Cpus_allowed: ffff
Cpus_allowed_list: 0-15
Mems_allowed: 00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000001
Mems_allowed_list: 0
voluntary_ctxt_switches: 36
nonvoluntary_ctxt_switches: 0
$ sudo cat /proc/3402/smaps
55eed4630000-55eed46f3000 r-xp 00000000 103:0b 2366159 /usr/lib/ibus/ibus-engine-libpinyin
Size: 780 kB
KernelPageSize: 4 kB
MMUPageSize: 4 kB
Rss: 704 kB
Pss: 704 kB
Shared_Clean: 0 kB
Shared_Dirty: 0 kB
Private_Clean: 704 kB
Private_Dirty: 0 kB
Referenced: 704 kB
Anonymous: 0 kB
LazyFree: 0 kB
AnonHugePages: 0 kB
ShmemPmdMapped: 0 kB
FilePmdMapped: 0 kB
Shared_Hugetlb: 0 kB
Private_Hugetlb: 0 kB
Swap: 0 kB
SwapPss: 0 kB
Locked: 0 kB
THPeligible: 0
VmFlags: rd ex mr mw me dw sd
对于进程实际占用内存,/proc/[pid]/status中,VmSize(当前进程正在占用的内存总大小,包括申请未使用和共享库内存)因包含重复统计和未实际使用的内存,存在夸大的情况,VmHWM / VmRss(最大时/当前应用程序正在使用的物理内存的大小)是相对理想的内存评估依据。想要得到确定的内存使用量,将/proc/[pid]/smap中的所有Private_Clean(未改写的私有页面)和Private_Dirty(未改写的私有页面)累加起来,是很好的解决方案。
为了控制篇幅,这里不再解释其它字段,感兴趣的小伙伴可以参考文献4。
线程相关的系统命令较少,这里列出3种供大家参考:ps相关命令、top及htop。
$ ps -T -p 2779 | head -5
PID SPID TTY TIME CMD
2779 2779 tty1 03:12:51 firefox
2779 2785 tty1 00:00:00 gmain
2779 2786 tty1 00:00:43 gdbus
2779 2791 tty1 06:57:27 IPC I/O Parent
“SPID”栏表示线程ID,而“CMD”栏则显示了线程名称。
$ ps -Lf 2779 | head -5
UID PID PPID LWP C NLWP STIME TTY STAT TIME CMD
shaw 2779 1 2779 0 239 2021 tty1 Sl+ 263:29 /usr/lib/firefox/firefox -ne
shaw 2779 1 2785 0 239 2021 tty1 Sl+ 0:00 /usr/lib/firefox/firefox -ne
shaw 2779 1 2786 0 239 2021 tty1 Sl+ 1:11 /usr/lib/firefox/firefox -ne
shaw 2779 1 2791 1 239 2021 tty1 Sl+ 561:43 /usr/lib/firefox/firefox -ne
shaw 2779 1 2900 0 239 2021 tty1 Sl+ 11:56 /usr/lib/firefox/firefox -ne
启动top时加上-H选项,top一行显示一个线程。否则,它一行显示一个进程。当然,也可以指定pid显示线程:
top - 21:48:03 up 31 days, 1:17, 1 user, load average: 0.45, 0.56, 0.54
Threads: 253 total, 0 running, 253 sleeping, 0 stopped, 0 zombie
%Cpu(s): 2.4 us, 0.6 sy, 0.0 ni, 96.9 id, 0.0 wa, 0.0 hi, 0.2 si, 0.0 st
KiB Mem : 32485348 total, 13764432 free, 11553884 used, 7167032 buff/cache
KiB Swap: 15999996 total, 15999996 free, 0 used. 18968152 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
2791 shaw 20 0 59.682g 0.013t 0.010t S 1.7 43.3 417:41.50 IPC I/O Parent
2953 shaw 20 0 59.682g 0.013t 0.010t S 1.3 43.3 359:26.11 IPDL Background
2929 shaw 20 0 59.682g 0.013t 0.010t S 0.7 43.3 357:18.29 GLXVsyncThread
2779 shaw 20 0 59.682g 0.013t 0.010t S 0.3 43.3 193:28.88 firefox
19477 shaw 20 0 59.682g 0.013t 0.010t S 0.3 43.3 1:37.65 Indexed~ #14341
注意第二行的Threads,表示此时显示的是线程。当此处为Tasks时,表示进程界面。
说实话,我没看出升级版对查看线程有多大帮助(变色也算升级?)。这里列出使用方法仅供参考。安装htop后显示即可。命令如下:
$ apt install htop
$ htop
小结:本章总结了进程和线程相关的系统命令。下一章更进一步,讲解查看linux内核源码——vim+ctags/find+grep。
本打算行文尽量简洁,但达不到讲精讲细的目的,所以我对本系列文章的定位是复杂知识点详细总结,在此基础上做到尽量简练。由于查阅了大量资料,耗费了很多精力,虽然谈不上尽善尽美,但也希望各位支持作者一下,来个一键四联(点赞、收藏、评论、转发),希望帮助到不断探索的你。