1.1 什么是进程?
进程是 UNIX/Linux 用来表示正在运行的程序的一种抽象概念,所有系统上面运行的的数据都会以进程的形态存在。
1.2 进程的组成部分
一个进程由一个地址空间和内核内部的一组数据公同组成,地址空间是由内核标记出来供进程使用的一组内存页面(页面是管理内存的单位,页面大小通常是 1KB 或 8KB)。它包含进程正在执行的代码、库、进程变量、进程栈以及进程正在运行时内核所需要的各种其他信息。
内核的内部数据结构记录了有关每个进程的各种信息,其中非常重要的一些信息有:
- 进程的属主;
- 进程的信号掩码(一个记录,确定要封锁哪些信号);
- 进程已打开的文件和网络端口的信息;
- 进程执行的优先级;
- 进程的当前状态(睡眠状态、停止状态、可运行状态等等);
- 进程的地址空间映射。
1.3 子进程与父进程
每个进程都有一个唯一的 PID(Process ID),进程必须克隆自身去创建一个新进程。克隆出的进程能够把它正在运行的那个程序替换成另一个不同的程序。
当一个进程被克隆时,原来的进程就叫做父进程 PPID(Parent Process ID),而克隆出的副本则叫做子进程。进程的 PPID 属性就是克隆它的父进程的 PID。
我们以一个实例加深对子进程与父进程的理解:在目前的 bash 环境下,再出发一次 bash ,并以 ps -l 命令观察进程 PID、PPID 的输出信息。
[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 所切