Linux进程控制和管理

1.1 什么是进程?

进程是 UNIX/Linux 用来表示正在运行的程序的一种抽象概念,所有系统上面运行的的数据都会以进程的形态存在。

1.2 进程的组成部分

一个进程由一个地址空间和内核内部的一组数据公同组成,地址空间是由内核标记出来供进程使用的一组内存页面(页面是管理内存的单位,页面大小通常是 1KB 或 8KB)。它包含进程正在执行的代码、库、进程变量、进程栈以及进程正在运行时内核所需要的各种其他信息。

内核的内部数据结构记录了有关每个进程的各种信息,其中非常重要的一些信息有:

  • 进程的属主;
  • 进程的信号掩码(一个记录,确定要封锁哪些信号);
  • 进程已打开的文件和网络端口的信息;
  • 进程执行的优先级;
  • 进程的当前状态(睡眠状态、停止状态、可运行状态等等);
  • 进程的地址空间映射。

1.3 子进程与父进程

每个进程都有一个唯一的 PID(Process ID),进程必须克隆自身去创建一个新进程。克隆出的进程能够把它正在运行的那个程序替换成另一个不同的程序。

当一个进程被克隆时,原来的进程就叫做父进程 PPID(Parent Process ID),而克隆出的副本则叫做子进程。进程的 PPID 属性就是克隆它的父进程的 PID

我们以一个实例加深对子进程与父进程的理解:在目前的 bash 环境下,再出发一次 bash ,并以 ps -l 命令观察进程 PIDPPID 的输出信息。

[root@web ~]# ps -l     //第一个 bash 的 PID 是 1363
F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
4 S     0  1363  1322  0  80   0 - 28864 do_wai pts/1    00:00:00 bash
0 R     0  1484  1363  0  80   0 - 38314 -      pts/1    00:00:00 ps
[root@web ~]# bash    //执行 bash 进入到子程序的环境中      
[root@web ~]# ps -l  //第二个 bash 的 PID 是 1487 , 它的 PPID 就是第一个 bash 的 PID 1363 
F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
4 S     0  1363  1322  0  80   0 - 28864 do_wai pts/1    00:00:00 bash
4 S     0  1487  1363  0  80   0 - 28864 do_wai pts/1    00:00:00 bash
0 R     0  1500  1487  0  80   0 - 38314 -      pts/1    00:00:00 ps

1.4 特殊进程

Linux 有三个特殊进程,idle 进程(PID=0),init 进程(PID=1),kthreadd(PID=2)。

idle 进程

idle 进程由系统自动创建的第一个进程, 运行在内核态,也是唯一一个没有通过 fork 或者 kernel_thread 产生的进程。完成加载系统后,演变为进程调度、交换。

init 进程

Linux 的所有进程都是有 init 进程创建并运行的。首先 Linux 内核启动,然后在用户空间中启动 init 进程,再启动其他系统进程。在系统启动完成完成后,init 将变为守护进程监视系统其他进程。

在我的 CentOS 7 系统里面,可以 ls 看到 init 进程是被软连接到 systemd 的。

[root@web ~]# ls -al /usr/sbin/init
lrwxrwxrwx 1 root root 22 Apr 26 11:07 /usr/sbin/init -> ../lib/systemd/system

系统启动之后,init 进程会启动很多 daemon 进程,为启动运行提供服务,比如 httpd、ssh 等等,然后就是 agetty,让用户登录,登录后运行 shell(bash),用户启动的进程都是通过shell 运行的。

执行 ps -ef 命令会发现 PID 1 的进程就是我们的 init 进程 systemd ,PID 2 的进程是内核现场 kthreadd ,这两个在内核启动的时候都见过,其中用户态的不带中括号,内核态的带中括号。 

[root@web ~]# ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0  2019 ?        00:12:54 /usr/lib/systemd/systemd --system --deserialize 24
root         2     0  0  2019 ?        00:00:01 [kthreadd]
root         3     2  0  2019 ?        00:03:10 [ksoftirqd/0]
root         5     2  0  2019 ?        00:00:00 [kworker/0:0H]
root         7     2  0  2019 ?        00:00:00 [migration/0]
...
root     23086 23085  0 10:23 pts/3    00:00:00 su - root
root     23087 23086  0 10:23 pts/3    00:00:00 -bash
root     24529     2  0 16:28 ?        00:00:00 [kworker/0:2]
root     25448     2  0 16:33 ?        00:00:00 [kworker/0:0]
root     25861     2  0 16:36 ?        00:00:00 [kworker/u2:0]
root     25863     1  0 Mar20 ?        01:58:45 /usr/local/qcloud/YunJing/YDEyes/YDService
root     25916     1  0 Mar20 ?        00:06:03 /usr/local/qcloud/YunJing/YDLive/YDLive
root     26359     2  0 16:38 ?        00:00:00 [kworker/0:1]
root     26957     2  0 16:42 ?        00:00:00 [kworker/u2:1]
root     27254 23087  0 16:43 pts/3    00:00:00 ps -ef
root     27732     1  0 Apr28 ?        00:00:00 ./fork.o
root     29944     1  0 Apr06 ?        00:18:37 /usr/bin/dockerd-current --add-runtime docker-runc=/usr/libexec/docker/docker-runc-current --default-runtime=docker-runc --ex
root     29949 29944  0 Apr06 ?        00:12:40 /usr/bin/docker-containerd-current -l unix:///var/run/docker/libcontainerd/docker-containerd.sock --metrics-interval=0 --star
root     30648     1  0  2019 ?        00:00:00 /usr/lib/systemd/systemd-udevd
root     30936     1  0  2019 ?        00:01:06 /usr/sbin/crond -n
chrony   32061     1  0  2019 ?        00:00:07 /usr/sbin/chronyd

sshd 的父进程是 1,pts 的父进程是 sshd,bash 的父进程是 pts,ps -ef 这个命令的父进程是 bash,这样这个子父进程的关系就比较清晰了。

[root@web ~]# ps -ef|grep sshd
root      1299     1  0 12:06 ?        00:00:04 /usr/sbin/sshd -D
root      6008  1299  0 16:49 ?        00:00:00 sshd: root@pts/0
root      6415  1299  2 17:07 ?        00:00:00 sshd: root [priv]
sshd      6416  6415  0 17:07 ?        00:00:00 sshd: root [net]
root      6420  6012  0 17:07 pts/0    00:00:00 grep --color=auto sshd
[root@web ~]# ps -ef|grep bash
root      6012  6008  0 16:49 pts/0    00:00:00 -bash
root      6457  6012  0 17:10 pts/0    00:00:00 grep --color=auto bash
[root@web ~]# 

kthreadd 进程

kthreadd 进程由 idle 通过 kernel_thread 创建,并始终运行在内核空间,负责所有内核线程的调度和管理,所有的内核线程都是直接或者间接的以 kthreadd 为父进程。

1.5 进程的优先级

Linux 是多人多任务的环境,由 top 的输出结果我们也发现, 系统同时间有非常多的程序在运行中,叧是大部分的程序都在休眠 (sleeping) 状态而已。如果所有的程序同时被唤醒,那 CPU 应该要先处理那个程序呢?

具有优先级的程序队列图:

我们知道 CPU 一秒钟可以运作多达数 G 的微指令次数,透过核心的 CPU 排程可以让各程序被 CPU 所切换运作, 因此每个程序在一秒钟内或多或少都会被 CPU 执行部分的脚本。Linux 给予程序一个所谓的优先执行顺序 (priority, PRI), 这个PRI 值越低代表越优先的意思。不过这个 PRI 值是由内核动态调整的,用户无法直接调整 PRI 值的。

[root@web ~]# ps -l
F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
4 S     0  6012  6008  0  80   0 - 28864 do_wai pts/0    00:00:00 bash
0 R     0  7028  6012  0  80   0 - 38314 -      pts/0    00:00:00 ps

由于 PRI 是内核动态调整的,无法去干涉 PRI,可以通过调整 Nice 的值去调整程序的优先执行顺序,就是上面的 NI 值。PRI  与 NI的相关性如下:

PRI(new)= PRI(old)+ Nice

nice 值是有正负的,PRI 越小越早被执行, 所以当nice 值为负值时,就会降低 PRI 值,即是会较优先被处理。

  • nice 值范围为 -20 ~ 19 ;
  • root 可随意调整自己或他人程序的 Nice 值;
  • 一般用户仅可条整自己程序的 Nice 值,范围仅为 0 ~ 19 (避免一般用户抢占系统资源);
  • 一般使用者仅可将 nice 值调高,例如本来 nice 为 5 ,只能调整到大于 5;

使用 nice 或 renice 命令调整程序的 nice 值

nice:给予新执行指令新的 nice 值

以下只是一个示例,在实际环境并没有作用,一般在系统中不重要的程序才需要调大 nice 的值,比如备份工作,由于备份比较消耗资源,调大备份指令的 nice 值,可以使系统的资源分配更为公平。

[root@web ~]# nice -n 19 vim /tmp/a.txt &
[1] 7720
[root@web ~]# ps -l
F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
4 S     0  6012  6008  0  80   0 - 28864 do_wai pts/0    00:00:00 bash
0 T     0  7720  6012  1  99  19 - 36725 do_sig pts/0    00:00:00 vim
0 R     0  7725  6012  0  80   0 - 38314 -      pts/0    00:00:00 ps

renice:已运行程序的 nice 重新调整

[root@web ~]# ps -l
F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
4 S     0  6012  6008  0  80   0 - 28864 do_wai pts/0    00:00:00 bash
0 T     0  7720  6012  0  99  19 - 36725 do_sig pts/0    00:00:00 vim
0 R     0  7911  6012  0  80   0 - 38314 -      pts/0    00:00:00 ps
[root@web ~]# renice 19 6012
6012 (process ID) old priority 0, new priority 19
[root@web ~]# ps -l
F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
4 S     0  6012  6008  0  99  19 - 28864 do_wai pts/0    00:00:00 bash
0 T     0  7720  6012  0  99  19 - 36725 do_sig pts/0    00:00:00 vim
0 R     0  7921  6012  0  99  19 - 38314 -      pts/0    00:00:00 ps

1.6 /proc 文件系统

 Linux 的 ps、top 命令都是从 /proc 目录读取进程的状态信息,但是它里面的信息却不局限于进程的信息--内核产生的所有状态信息和统计信息也都在 /proc

进程的信息都分别放到 /proc/[PID] 按 PID 命名的子目录里面

[root@web ~]# ls /proc
1    114  119  1252  1296  139  17  21     30499  31763  32066  32174  346  375  380  538  550  609  667  9          bus       crypto     execdomains  iomem     keys         kpageflags  misc     pagetypeinfo  scsi      swaps          timer_list   vmstat
10   115  120  1254  1299  14   18  22     30680  318    32078  32175  355  376  381  546  551  610  705  973        cgroups   devices    fb           ioports   key-users    loadavg     modules  partitions    self      sys            tty          zoneinfo
11   116  123  126   13    140  19  29178  31314  319    321    32176  356  377  4    547  552  615  708  979        cmdline   diskstats  filesystems  irq       kmsg         locks       mounts   pressure      slabinfo  sysrq-trigger  uptime
112  117  124  127   1310  15   2   29184  31610  31952  32113  32177  373  378  460  548  583  618  8    acpi       consoles  dma        fs           kallsyms  kpagecgroup  mdstat      mtrr     sched_debug   softirqs  sysvipc        version
113  118  125  128   1314  16   20  3      317    320    32116  32178  374  379  487  549  6    627  878  buddyinfo  cpuinfo   driver     interrupts   kcore     kpagecount   meminfo     net      schedstat     stat      thread-self    vmallocinfo

/proc 目录包含的几个最有用的文件/目录内容信息:

文件/目录 内容
/proc/cmdline 加载 kernel 时下达的相关参数,查看此文件可了解系统启动的一些信息
/proc/meminfo 内存信息
/proc/cpuinfo cpu 相关信息,包含频率、类型、运算功能等等
/proc/mounts 系统已经挂载的数据,执行 mount 命令直接读取此文件
/proc/modules 已经加载的模块列表(驱动程序)
 /proc/[pid]/attr  此目录中的文件提供了用于安全相关模块的 API
  /proc/[pid]/cwd  链到进程当前目录的符号链接(软链接)
  /proc/[pid]/cmdline  进程的完整命令行
 /proc/[pid]/environ  进程的环境变量
 /proc/[pid]/exe  链到正在被执行的文件的符号链接
/proc/[pid]/fd  子目录,包含链到每个打开文件的描述符的链接
 /proc/[pid]/maps  内存映射信息(共享段、库等)
 /proc/[pid]/root  链到进程的根目录(由 chroot 设置)的符号链接
/proc/[pid]/status 进程大量的信息,包含进程的 name、state、ppid 等等
 /proc/[pid]/stat  进程的总体状态信息(ps使用这个)

注:man proce 查看更多更详细的 /proc信息

1.7 信号

 进程是可以通过信号(signal)控制的,比如重启、关闭、停止进程。

主要的信号代号名称以及内容:

代号 名称 内容
1 SIGHUB 启动被终止的进序,可让该 PID 重新读取自己的配置文件,类似重新启动
2 SIGINT 相当于用键盘输入【ctrl +c】来中断一个程序的进行
9 SIGKILL 强制中断进程,如果该进程运行到一半,那么尚未完成的部分可能会有【半产品】产生,
类似 vim 会有 .filename.swp 保留下来
15 SIGTERM 以正常的结束进程来终止该进程,由于是正常的终止,所以后续的动作会将它完成,
如果该进程已经发生问题,无法使用正常的终止方法,使用这个也没用
17 SIGSTOP 相当于用键盘输入【ctrl +z】来暂停一个进程

 注:更多详细信息可执行 kill -l 或 man 7 signal 命令查看

kill:发送信号

顾名思义,kill 命令最常见的用法是终止一个进程,默认情况下发送 TERM 信号,语法是:

kill [signal] pid

signal 就是要发送信号的编号,没有带信号的编号的kill命令不保证进程会被杀死,因为 TERM 信号可能会被捕获、封锁或忽略。下面的命令:

kill -9 pid

将“保证”进程的消亡,因为是信号 9,即 KILL 不能被捕获到。给“保证”加引号是因为进程的生命力有时候能够变得相当“旺盛”,以致连 kill 9 也不能影响他们(通常是由于有些退化的 I/O 虚假锁定,例如等等已经停止旋转的磁盘)。

重新启动系统通常是解决这些“不听话”进程的唯一方法。

killall 命令可以按照进程的名字杀死所有进程,语法:

killall process_name

1.8 ps:监视进程

ps 用于报告当前系统的进程状态,是最基本同时也是非常强大的进程查看命令,ps 通过读取 /proc 中的虚拟文件来工作。

ps 的选项参数众多,只需记住几个常用的选项就可以满足平时查看系统进程 99% 的需要,遇到特殊情况的 %1,不懂的时候使用 man ps 。

ps 常用选项:

-A:显示所有进程,等于 -e 选项
-C:通过名字搜索进程 -f:显示当前用户运行进程的 UID、PID、PPID 等等 f:以 ASCII 字符显示树状结构,表达进程间的相互关系,类似 -H 选项 L:列出栏位相关信息 -l 或 l:采用详细的格式显示当前用户的进程信息 -o:自定义显示进程信息,比如可以根据ps L显示的栏目信息进行自定义输出的进程信息:ps -eo 'tty,comm,pid,ppid,%cpu,%mem',或者根据AIX格式: ps -eo "%p %y %x %c" -p:列出指定 pid的进程信息,类似 n 选项
-t/t:指定 tty 终端机编号,并列出属于该终端机的程序的状况。比如 ps -t 4 或 ps -t pts/4
-u/-U:列出指定用户的进程信息
x:显示所有程序,不以终端机来区分。

 ps 常用命令组合:

查看所有进程信息:
ps -ef
ps aux
查看进程树:
ps -ejH
ps -axjf
查看线程有关的信息:
ps -eLf
ps axms

 ps常用实例

ps axo pid,comm,pcpu 	# 查看进程的 PID、名称以及 CPU 占用率
ps aux | sort -rnk 4 	# 按内存资源的使用量对进程进行排序
ps aux | sort -nk 3  	# 按 CPU 资源的使用量对进程进行排序
ps -u root 	# 显示指定用户信息
ps -e -o "%C : %p :%z : %a" | sort -k5 -nr 	# 查看进程并按内存使用大小排列
ps -ef	 # 显示所有进程信息,连同命令行
ps -ef | grep ssh 	# ps 与 grep 常用组合用法,查找特定进程
ps -C nginx 	# 通过名字搜索进程
ps aux --sort=-pcpu,+pmem 	# CPU或者内存进行排序,-降序,+升序
ps -f --forest -C nginx 	# 用树的风格显示进程的层次关系
ps -o pid,ppid,uname,comm -C nginx 	# 显示一个父进程的子进程
ps -e -o pid,uname=USERNAME,pcpu=CPU_USAGE,pmem,comm 	# 重定义栏目名字
ps -e -o pid,comm,etime	 # 显示进程运行的时间
ps -aux | grep named 	# 查看named进程详细信息
ps -o command -p 91730 | sed -n 2p	 # 通过进程id获取服务名称

 ps 的字段说明

[root@web ~]# ps -l
F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
4 S     0  3672  3649  0  80   0 - 28864 do_wai pts/2    00:00:00 bash
0 R     0  4596  3672  0  80   0 - 38314 -      pts/2    00:00:00 ps
key long description
F flags 4代表进程的权限位 root;1代表此子进程仅进行复制(fork)而没有实际执行(exec)
S Stat

进程的状态主要有:
R(Running/Runnable):进程正在运行中或可运行(在队列上);
S(Interruptible Sleep):可中断睡眠状态,进程等待某个事件而被系统挂起。当进程等待的事件发生时,它会被唤醒并进入 R 状态;

D(Disk Sleep):不可中断状态说明(Uninterruptible Sleep),一般表示进程正在跟硬件交互不允许被其他进程中断;

T(Stopped/Traced):停止状态,背景暂停或调试状态;

Z(Zombie):僵尸状态,进程已经被终止但却无法被移除至内存外。

UID User ID 用户ID
PID Process ID 进程ID
C Cpu cpu 使用率,单位是百分比
PRI Priority Priority值
NI Nice nice值
ADDR 内存有关 ADDR是kernel function,指出进程在内存的哪个部分,如果是running,一般显示-
SZ Size 进程使用的内存
WCHAN WCHAN 进程所在的内核函数的名称,如果进程是多线程则显示*
TTY controlling tty (terminal) 登录者的终端机位置,若为远程则使用动态端口接口(pts/n)
TIME TIME 消费CPU运作时间,这里不是系统时间,"[DD-]HH:MM:SS"格式。(别名cputime)。
CMD COMMAND 触发进程的指令名称
VSZ VSIZE 进程的虚拟内存大小,单位为KB
RSS RSS 进程占用的固定内存,单位KB

 

1.9 top:动态监视进程

 ps 命令只能提供系统进程的一次性快照,因此,需要使用 top 命令来获得系统正在发生事情的“全景”。

常用 top 选项:

c/-c:显示完整的 Command-line/Program-name
-d:屏幕刷新间隔时间,默认是1秒
 u/-u/U/-U:指定用户名
-p/p:指定进程 pid
-n:循环显示的次数

top 显示 cpu 相关参数说明:

us, user : 用户进程占用cpu百分比
sy, system :内核进程占用cpu百分比
ni, nice :经过改变优先级的用户进程占用cpu百分比
id, idle : 空闲cpu百分比
wa, IO-wait : 等待I/O进程的百分比
hi : time spent servicing hardware interrupts
si : time spent servicing software interrupts
st : time stolen from this vm by the hypervisor

 常用 top 交互指令:

f/F: 从当前显示中添加或者删除项目。
l:切换显示平均负载和启动时间信息。
m/M:切换显示内存信息/根据驻留内存大小进行排序。
t:切换显示进程和CPU状态信息。
c:切换显示命令名称和完整命令行。
P:根据CPU使用百分比大小进行排序。
q 退出

1.10 strace:追踪信号和系统调用

有时候通过 ps、top 这样的命令来判断一个进程实际正在做什么相当困难,但通过 strace 命令直接观察一个进程,进程每调用一次系统调用,以及接受到一个信息,这个命令都能显示出来。

man strace 手册里有其中大多数功能的说明,例如,-f 标准后面跟 fork 出来的进程,这个可以帮助跟踪像 httpd 这个生出好多子进程的守护进程,-e 这个文件选项只显示文件操作,对应找到难以定位的配置文件的位置特别方便。

strace 附在正在执行的进程上,监视一会该进程,再从这个进程脱离。从而获取进程的活动信息情况。

 比如下面是 strace 附在一个活动的 top 进程上获得的信息:

[root@web ~]# strace -p 29669
strace: Process 29669 attached
pselect6(1, [0], NULL, NULL, {1, 921941517}, {[WINCH], 8}) = 0 (Timeout)
lseek(6, 0, SEEK_SET)                   = 0
read(6, "MemTotal:        1882216 kB\nMemF"..., 8191) = 1282
lseek(5, 0, SEEK_SET)                   = 0
read(5, "12352734.94 12094491.68\n", 8191) = 24
openat(AT_FDCWD, "/proc", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 8
getdents(8, /* 168 entries */, 32768)   = 4776
stat("/proc/1", {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0
open("/proc/1/stat", O_RDONLY)          = 9
read(9, "1 (systemd) S 0 1 1 0 -1 4202752"..., 1024) = 390
close(9)                                = 0
open("/proc/1/statm", O_RDONLY)         = 9
read(9, "12925 919 600 355 0 2354 0\n", 1024) = 27
close(9)                                = 0
stat("/proc/2", {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0
open("/proc/2/stat", O_RDONLY)          = 9
read(9, "2 (kthreadd) S 0 0 0 0 -1 213817"..., 1024) = 170
close(9)                                = 0
open("/proc/2/statm", O_RDONLY)         = 9
read(9, "0 0 0 0 0 0 0\n", 1024)        = 14
......

 在本例中,top 先打开 /proc 目录,用stat获得其信息,然后读取该目录的内容,由此获得当前正在运行的进程清单,top 接着用 stat 获得代表 init 进程的那个目录的信息,然后打开 /proc/1/stat 读取 init 的状态信息。

strace 实践

//编写一个简单的c语言代码test.c
#include <stdio.h>
int main()
{
    int a;
    scanf("%d", &a);
    printf("%09d\n", a);
    return 0;
}

//编译后得到可执行文件test,然后使用strace调用执行
[root@web demo]# gcc -o test test.c
[root@web demo]# ./test    // 直接执行test的结果
68
000000068
[root@web demo]# strace ./test   //通过strace执行的结果
execve("./test", ["./test"], [/* 20 vars */]) = 0
brk(NULL)                               = 0x16c9000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f4d7e181000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=38904, ...}) = 0
mmap(NULL, 38904, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f4d7e177000
close(3)                                = 0
open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\20&\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2156160, ...}) = 0
mmap(NULL, 3985888, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f4d7db93000
mprotect(0x7f4d7dd56000, 2097152, PROT_NONE) = 0
mmap(0x7f4d7df56000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c3000) = 0x7f4d7df56000
mmap(0x7f4d7df5c000, 16864, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f4d7df5c000
close(3)                                = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f4d7e176000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f4d7e174000
arch_prctl(ARCH_SET_FS, 0x7f4d7e174740) = 0
mprotect(0x7f4d7df56000, 16384, PROT_READ) = 0
mprotect(0x600000, 4096, PROT_READ)     = 0
mprotect(0x7f4d7e182000, 4096, PROT_READ) = 0
munmap(0x7f4d7e177000, 38904)           = 0
fstat(0, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f4d7e180000
read(0, 68
"68\n", 1024)                   = 3
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f4d7e17f000
write(1, "000000068\n", 10000000068
)             = 10
exit_group(0)                           = ?
+++ exited with 0 +++


[root@web demo]# strace -c ./test    //获取进程所有的系统调用的统计分析
^Cstrace: Process 2009 detached
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
  0.00    0.000000           0         1           read
  0.00    0.000000           0         2           open
  0.00    0.000000           0         2           close
  0.00    0.000000           0         3           fstat
  0.00    0.000000           0         8           mmap
  0.00    0.000000           0         4           mprotect
  0.00    0.000000           0         1           munmap
  0.00    0.000000           0         1           brk
  0.00    0.000000           0         1         1 access
  0.00    0.000000           0         1           execve
  0.00    0.000000           0         1           arch_prctl
------ ----------- ----------- --------- --------- ----------------
100.00    0.000000                    25         1 total

1.11 lsof:列出被进程打开的文件

常用选项:

-a:列出打开某文件的进程;
-c<进程名>:列出指定进程所打开的文件;
+D<目录>:递归列出目录下被打开的文件;
-i<条件>:列出符合条件的进程。(4、6、协议、:端口、 @ip )
-p<进程号>:列出指定进程号所打开的文件;
-u:列出UID号进程详情;

lsof 实例

[root@web ~]# lsof -i    // 显示所有连接
COMMAND     PID   USER   FD   TYPE   DEVICE SIZE/OFF NODE NAME
sshd       2808   root    3u  IPv4 89562037      0t0  TCP web:22999->225.73.55.115:policyserver (ESTABLISHED)
sshd       2816  david    3u  IPv4 89562037      0t0  TCP web:22999->225.73.55.115:policyserver (ESTABLISHED)
......
[root@web ~]# lsof -i 6    // 显示 ipv6连接
COMMAND   PID   USER   FD   TYPE   DEVICE SIZE/OFF NODE NAME
master   5880   root   14u  IPv6 52188842      0t0  TCP VM_0_13_centos:smtp (LISTEN)
chronyd 32061 chrony    6u  IPv6  3289514      0t0  UDP VM_0_13_centos:323 
.......
[root@web ~]# lsof -iTCP    //显示tcp连接
COMMAND     PID  USER   FD   TYPE   DEVICE SIZE/OFF NODE NAME
sshd       2808  root    3u  IPv4 89562037      0t0  TCP web:22999->225.73.55.115:policyserver (ESTABLISHED)
sshd       2816 david    3u  IPv4 89562037      0t0  TCP web:22999->225.73.55.115:policyserver (ESTABLISHED)
taskmgr    3317  root    0u  IPv4 89581455      0t0  TCP web:53644->103.117.121.93:6677 (SYN_SENT)
master     5880  root   13u  IPv4 52188841      0t0  TCP VM_0_13_centos:smtp (LISTEN)
master     5880  root   14u  IPv6 52188842      0t0  TCP VM_0_13_centos:smtp (LISTEN)
sshd       8673  root    3u  IPv4 89580550      0t0  TCP web:22999->225.73.55.115:3comfaxrpc (ESTABLISHED)......
[root@web ~]# lsof -i:80    // 显示监听80端口相关的进程信息
COMMAND   PID USER   FD   TYPE   DEVICE SIZE/OFF NODE NAME
nginx   23008 root    8u  IPv4 56706878      0t0  TCP *:http (LISTEN)
nginx   23009  www    8u  IPv4 56706878      0t0  TCP *:http (LISTEN)
[root@web ~]# lsof -i@225.73.55.115    // 显示指定主机连接的进程信息
COMMAND   PID  USER   FD   TYPE   DEVICE SIZE/OFF NODE NAME
sshd     2808  root    3u  IPv4 89562037      0t0  TCP web:22999->225.73.55.115:policyserver (ESTABLISHED)
sshd     2816 david    3u  IPv4 89562037      0t0  TCP web:22999->225.73.55.115:policyserver (ESTABLISHED)
sshd     8673  root    3u  IPv4 89580550      0t0  TCP web:22999->225.73.55.115:3comfaxrpc (ESTABLISHED)
sshd     8676 david    3u  IPv4 89580550      0t0  TCP web:22999->225.73.55.115:3comfaxrpc (ESTABLISHED)
nginx   23009   www    3u  IPv4 89582028      0t0  TCP web:http->225.73.55.115:fjhpjp (ESTABLISHED)
[root@web ~]# lsof  -i -sTCP:ESTABLISHED    // 显示tcp连接的进程信息
COMMAND     PID  USER   FD   TYPE   DEVICE SIZE/OFF NODE NAME
sshd       2808  root    3u  IPv4 89562037      0t0  TCP web:22999->225.73.55.115:policyserver (ESTABLISHED)
sshd       2816 david    3u  IPv4 89562037      0t0  TCP web:22999->225.73.55.115:policyserver (ESTABLISHED)
sshd       8673  root    3u  IPv4 89580550      0t0  TCP web:22999->225.73.55.115:3comfaxrpc (ESTABLISHED)
sshd       8676 david    3u  IPv4 89580550      0t0  TCP web:22999->225.73.55.115:3comfaxrpc (ESTABLISHED)
nginx     23009   www    3u  IPv4 89582028      0t0  TCP web:http->225.73.55.115:fjhpjp (ESTABLISHED)
YDService 25863  root    6u  IPv4 64551180      0t0  TCP web:51422->169.254.0.55:lsi-bobcat (ESTABLISHED)
[root@web ~]# lsof -u david    //显示指定用户打开的文件
COMMAND  PID  USER   FD   TYPE             DEVICE  SIZE/OFF     NODE NAME
sshd    2816 david  cwd    DIR              253,1      4096        2 /
sshd    2816 david  rtd    DIR              253,1      4096        2 /
sshd    2816 david  txt    REG              253,1    852856    16203 /usr/sbin/sshd
sshd    2816 david  mem    REG              253,1     37168    16740 /usr/lib64/libnss_sss.so.2
sshd    2816 david  mem    REG              253,1     15480     8530 /usr/lib64/security/pam_lastlog.so
......
[root@web ~]# kill -9 `lsof -t -u david`    // 杀死指定用户运行的进程
[root@web ~]# lsof -c nginx    // 列出指定进程打开的文件
COMMAND   PID USER   FD      TYPE             DEVICE SIZE/OFF     NODE NAME
nginx   23008 root  cwd       DIR              253,1     4096   919161 /data/application/nginx-1.16.0/conf/vhosts
nginx   23008 root  rtd       DIR              253,1     4096        2 /
nginx   23008 root  txt       REG              253,1  7299448   919146 /data/application/nginx-1.16.0/sbin/nginx
nginx   23008 root  mem       REG              253,1    61624    21247 /usr/lib64/libnss_files-2.17.so
nginx   23008 root  mem       REG              253,1   155784     4428 /usr/lib64/libselinux.so.1
nginx   23008 root  mem       REG              253,1   105824    21252 /usr/lib64/libresolv-2.17.so
......
[root@web ~]# lsof -p 23009    //列出指定进程号打开的文件
COMMAND   PID USER   FD      TYPE             DEVICE SIZE/OFF     NODE NAME
nginx   23009  www  cwd       DIR              253,1     4096   919161 /data/application/nginx-1.16.0/conf/vhosts
nginx   23009  www  rtd       DIR              253,1     4096        2 /
nginx   23009  www  txt       REG              253,1  7299448   919146 /data/application/nginx-1.16.0/sbin/nginx
nginx   23009  www  mem       REG              253,1    37168    16740 /usr/lib64/libnss_sss.so.2
nginx   23009  www  mem       REG              253,1    61624    21247 /usr/lib64/libnss_files-2.17.so
nginx   23009  www  mem       REG              253,1   155784     4428 /usr/lib64/libselinux.so.1
nginx   23009  www  mem       REG              253,1   105824    21252 /usr/lib64/libresolv-2.17.so
nginx   23009  www  mem       REG              253,1    15688     4556 /usr/lib64/libkeyutils.so.1.5
nginx   23009  www  mem       REG              253,1    67104    16148 /usr/lib64/libkrb5support.so.0.1
......
[root@web ~]# 

注:lsof 真的很强大!

1.12 线程

在 Linux 中,一些进程被细分为更小的部分,我们称为线程(thread)。线程和进程很类似,它有一个标识符(即TID)。内核运行线程的方式和运行进程基本相同。但有一点不同,即进程之间不共享内存和 I/O 这样的系统资源,而同一个进程中的所有线程则共享该进程占用的系统资源和一些内存。

单线程进程和多线程进程

很多进程只有一个线程,叫单线程进程。有超过一个线程的叫多线程进程。所有进程最开始都是单线程,起始线程通常称为主线程。主线程随后可能会启动新的线程,这样进程就变为多线程。这个过程和进程使用 fork() 创建新进程类似。

多线程的主要优势在于,当进程要做的事情很多时,多个线程可以同时在多个处理器上运行,这样可以加快进程的运行速度。虽然你也可以同时在多个处理器上运行多个进程,但线程相对进程来说启动更快,并且线程间通过共享的进程内存来相互通信,比进程间通过网络和管道相互通信更加便捷高效。

一些应用程序使用线程来解决在管理多个I/O资源时遇到的问题。传统上来说,进程有时候会使用 fork() 创建新的子进程来处理新的输入输出流。线程提供相似的机制,但却省去了启动新进程的麻烦。

[root@racknerd-d3e10f ~]# ps m    // 查看线程
1276 tty4     -      0:00 /sbin/mingetty /dev/tty4
    - -        Ss+    0:00 -
 1278 tty5     -      0:00 /sbin/mingetty /dev/tty5
    - -        Ss+    0:00 -
 1281 tty6     -      0:00 /sbin/mingetty /dev/tty6
    - -        Ss+    0:00 -
11618 pts/0    -      0:00 -bash
    - -        Ss     0:00 -
11646 pts/0    -      0:00 ps m
    - -        R+     0:00 -
12287 - /usr/bin/python /usr/bin/gm-notify
- 12287 -
- 12288 -
- 12289 -
- 12295 -
[root@racknerd-d3e10f ~]# ps m -o pid,tid,comm        // 自定义查看线程
 1276     - mingetty
    -  1276 -
 1278     - mingetty
    -  1278 -
 1281     - mingetty
    -  1281 -
11618     - bash
    - 11618 -
11694     - ps
    - 11694 -
12287 - /usr/bin/python /usr/bin/gm-notify
- 12287 -
- 12288 -
- 12289 -
- 12295 -

注意:单线程进程中的TID和PID相同,即主线程。对于多线程进程12287,线程12287是主线程。

1.13 其它

pidof:查找正在运行进程的 pid

[root@web ~]# pidof nginx
23009 23008
[root@web ~]# pidof systemd
1
[root@web ~]# pidof sshd
11079 8676 8673 2816 2808

程序 program、进程 process、线程 thread 之间的关系和区别

简单来说,程序是一个可执行的文件,是一组指令的集合,比如一个 windows 的谷歌浏览器安装文件 chrom.exe 或 shell 脚本 auto-install.sh

进程就是一个正在运行的程序,执行者的权限属性、程序的程序代码所需数据等都会被加载内存中, 操作系统给予这个内存内的单元一个标识符 (PID)。

线程是一个进程执行一次 fork 的结果,线程继承了包含它的进程的很多属性(例如,进程的地址空间),多个线程在同一个进程内按照一种称为多线程的机制并发执行。

 

线程与进程之间的区别

进程不共享其地址空间,而在同一进程下执行的线程共享地址空间。

进程是相互独立执行的,进程之间的同步仅由内核负责,而另一方面,线程同步必须由线程在其下执行的进程负责。

与进程之间的上下文切换相比,线程之间的上下文切换速度更快。

两个进程之间的交互只能通过标准的进程间通信来实现,而在同一进程下执行的线程可以轻松进行通信,因为它们共享大多数资源,例如内存,文本段等。

小结

一个程序至少有一个进程,一个进程至少有一个线程。

进程是程序的一部分,线程是进程的一部分。

参考文献

《鸟哥的Linux私房菜》

《Linux系统管理技术手册》

《精通Linux》

极客时间《趣谈Linux操作系统》

posted @ 2020-05-04 09:18  z寒江雪  阅读(1304)  评论(0编辑  收藏  举报