Skip to content

Latest commit

 

History

History
161 lines (78 loc) · 6.94 KB

对进程的理解.md

File metadata and controls

161 lines (78 loc) · 6.94 KB

对进程的理解

进程是一个独立的程序正在处于运行的状态,它是一个占用CPU、内存、IO资源的实体。

Linux 系统启动后,不会直接管理进程,而是会交给 init 进程管理进程,在一些 Linux 发行版中,比如 CentOS,这个 init 进程就是 systemd。

进程是通过 fork 函数生成的,父进程 fork 出子进程。

如果一个进程的父进程退出了,那么它的父进程就会变成 systemd。

systemd 会管理进程的生命周期,包括处理进程退出。

如果在进程退出时,它的父进程没有管它,它就一直处于僵尸状态,这就是僵尸进程的出现原因。

进程的种类

Linux 系统里有几种不同类型的进程:用户进程(User processes)、守护进程(Deamon processes)和内核进程(Kernel processes)。

进程的元信息

操作系统的角度来说,进程描述为一个结构体---->PCB(在Linux下PCB位task_struct)

task_struct是Linux内核的一种数据结构,他会被装载到RAM(内存)里并且包含着进程的信息:

  • 标示符 pid:描述本进程的唯一标示符,用来区别其他进程
  • 状态:任务状态,退出代码,退出信号等
  • 优先级:相对于其他进程的优先级
  • 程序计数器:程序中即将被执行的下一条指令的地址
  • 内存指针:包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
  • 上下文数据:进程执行时处理器的寄存器中的数据
  • I/O状态信息:包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表
  • 记账信息:可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等
  • 其他信息

如何查看进程

$ ls /proc/
$ ps aux
$ ps ef

另外在 top 命令中可以看到各种状态的进程的数量。

进程状态及转换

进程的七大状态:(linux内核里有时候也叫任务)

  • R--->运行状态
  • S--->睡眠状态(可中断睡眠状态)
  • D--->磁盘休眠状态(不可中断睡眠状态)
  • T--->停止状态
  • X--->死亡状态
  • t --->追踪状态
  • Z--->僵尸状态(很重要,必须理解)

其中运行状态包括可运行状态

可运行状态

进程获取了所有所需资源,正等待 CPU 时,就会进入可运行状态。处于可运行状态的进程在 ps 的输出中,也已 R 标识。

举例来说,一个正在 I/O 的进程并不立即需要 CPU。当进程完成 I/O 操作后,就会触发一个信号,通知 CPU 和调度器将该进程置于运行队列(由内核维护的可运行进程的列表)。当 CPU 可用时,该进程就会进入正在运行状态。

和正在运行状态一样,进程的状态被设置为 TASK_RUNNING

睡眠状态

当进程所需的资源暂不可用时,就会进入睡眠状态。此时,进程要么主动进入睡眠状态,要么被内核置于睡眠状态(不管你想不想睡,反正内核会让你睡;因此,后者又称为「进程被内核睡了」)。进入睡眠状态的进程,会立即交出 CPU 的使用权。

当进程所需的资源可用时,CPU 会收到一个信号。于是,当调度器下次调度该进程时,会将它置为正在运行或可以运行状态。

以 login shell 进程为例,它

  • 在你键入命令时进入睡眠状态,同时等待一个特定的事件(取决与你键入执行的命令);
  • Shell 进程睡眠时,会进入一个特定的等待通道(WCHAN, wait channel,同样取决于你键入执行的命令);
  • 当 Shell 进程等待的事件发生时(例如,收到一个来自键盘的中断 ^C),在该等待通道的所有进程都会苏醒。

执行 ps -l 可看到与当前 shell 关联的进程,执行 ps -el 则可看到系统上所有进程。如果进程处于睡眠状态,ps 输出中的 WCHAN 字段会显示进程在等待什么系统调用。

$ ps -l | more
F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
0 R   867 12085 27779  0  80   0 - 27029 -      pts/480  00:00:00 ps
0 S   867 12086 27779  0  80   0 - 25779 pipe_w pts/480  00:00:00 more
0 S   867 27779 35146  0  80   0 - 27721 do_wai pts/480  00:00:01 bash

例如,在这里,我们执行了 ps -l | more 这个命令。输出中,morebash 都处于睡眠状态。前者是在等待管道输入,即 pipe_wait,因为 ps 输出时,more 还没有接到内容。后者是在 等待 ps -l | more 执行完毕,即等待 do_wait 系统调用。

除了等待资源之外,进程也可以主动进入睡眠状态并持续一段时间。例如,sleep() 函数接收一个时间长度(以秒为单位,比如 10 秒)的参数,然后调用该函数的进程就会进入睡眠状态,并持续 10 秒。当睡眠时间结束后,调度器再次调度到该进程时,会将其设置为可运行状态。之后,当 CPU 空闲时,进程会重新进入正在运行状态。

由此可见,sleep(10) 并不能保证「恰好」睡眠 10 秒,它只保证睡眠时间不少于 10 秒。

部分进程永远不会终止,而是不断地在睡眠、唤醒干活的状态中循环。每次循环开始时,进程进入睡眠状态,然后等待某个特定的事件。当事件发生时,进程被唤醒(进入正在运行或者可以运行状态),然后处理任务。

睡眠状态也分可中断之睡眠状态和不可中断之睡眠状态。

可中断睡眠状态

可中断之睡眠状态表示进程在等待时间片段或者某个特定的事件。一旦事件发生,进程会从可中断之睡眠状态中退出。ps 命令的输出中,可中断之睡眠状态标识为 S

系统会为可中断之睡眠状态的进程设置进程运行状态为:

p->state = TASK_INTERRUPTABLE;

不可中断睡眠状态

不可中断之睡眠状态的进程不会处理任何信号,而仅在其等待的资源可用或超时时退出(前提是设置了超时时间)。

不可中断之睡眠状态通常和设备驱动等待磁盘或网络 I/O 有关。在内核源码 fs/proc/array.c 中,其文字定义为 "D (disk sleep)", /* 2 */。当进程进入不可中断之睡眠状态时,进程不会处理信号,而是将信号都积累起来,等进程唤醒之后再处理。在 Linux 中,ps 命令使用 D 来标识处于不可中断之睡眠状态的进程。

系统会为不可中断之睡眠状态的进程设置进程运行状态为:

p->state = TASK_UNINTERRUPTABLE;

由于处于不可中断之睡眠状态的进程不会处理任何信号,所以 kill -9 也杀不掉它。解决此类进程的办法只有两个:

  • 对于怨妇,你还能怎么办,只能满足它啊:搞定不可中断之睡眠状态进程所等待的资源,使资源可用。
  • 如果满足不了它,那就只能 kill the world——重启系统。