怎样做网站发帖,各大网站推广软件,php 茶叶网站建设,高端网站建设的图片目录 进程概念 描述进程-PCB
task_struct-PCB的一种
task_struct内容分类 查看进程
通过系统目录查看
通过ps命令查看
通过系统调用获取进程的PID和PPID
通过系统调用创建进程
fork的认识
使用if进行分流
最后的总结 Linux进程状态 运行状态-R 浅度睡眠状态-S
深度睡…目录 进程概念 描述进程-PCB
task_struct-PCB的一种
task_struct内容分类 查看进程
通过系统目录查看
通过ps命令查看
通过系统调用获取进程的PID和PPID
通过系统调用创建进程
fork的认识
使用if进行分流
最后的总结 Linux进程状态 运行状态-R 浅度睡眠状态-S
深度睡眠状态-D
暂停状态-T
暂停状态-t
死亡状态-X
僵尸状态-Z 子退出
僵尸进程
僵尸进程的危害
孤儿进程
重要知识补充阻塞和挂起
阻塞
挂起 二者区别 进程概念
课本定义进程 是程序的一个执行实例是正在执行的程序。
但是实际上这种说法不全面。
举个例子 如果有一个社会人士想要成为你们学校的学生那么他需要满足什么样的条件呢只需要他本人进入到你们的学校在学校内活动就可以了吗这显然是不行的判断一个人是不是这个学校的学生依据的是这个人有没有被学校所管理学校会不会给他排课给他计学分、发毕业证。 所以同样的把一个可执行程序变为进程不仅仅要把该可执行程序加载到内存中还要让这个可执行程序被操作系统所管理。 所以在内核观点担当分配系统资源CPU时间内存的实体。 这样描叙比较合适点。
在我们日常写代码的时候其中有一部分内容为编译原理。其对于一个c/c代码要想运行起来要经过预处理编译汇编链接。四步走才会生成一个.exe的可执行程序对于这个生成的.exe的可执行程序其实就存放在磁盘上现在的笔记本电脑仍然有磁盘作为存储设备不过磁盘的类型和配置有所变化。根据搜索结果笔记本电脑的磁盘主要分为固态硬盘SSD和机械硬盘HDD两种。当我们双击这个可执行程序将其运行起来时本质上是将这个程序加载到内存当中了因为冯诺依曼体系的原因只有加载道内存的数据才可以被CPU进行接下来的操作。而一旦将这个.exe程序加载到内存后我们就不应该将这个.exe程序再叫做程序了严格意义上将应该将其称之为进程。 在我们Windows下我们也可以通过打开任务管理器查看当前运行的所有进程。
不难理解的是原本存放在磁盘上的任务管理器经过我们打开运行后他也会变为一个进程这也侧面证明了我们上面说的是对的。 描述进程-PCB
系统当中可以同时存在大量进程刚才我们在Windows下查看了当前运行的所有进程光后台运行都有142个可见之多。当然在Linux下也可以查看。使用命令ps aux便可以显示系统当中存在的进程。
ps aux
这里就截取一部分其实还是蛮多的。 而当你开机的时候启动的第一个程序就是我们的操作系统即操作系统是第一个加载到内存的我们都知道操作系统是做管理工作的而其中就包括了进程管理。而系统内是存在大量进程的那么操作系统是如何对进程进行管理的呢
遵循我们之前文章中提到的六个字原则先描述再组织。
操作系统管理进程也是一样的操作系统作为管理者是不需要直接和被管理者进程直接进行沟通的当一个进程出现时操作系统就立马对其进行描述之后对该进程的管理实际上就是对其描述信息的管理。 在Windows下这些操作其实都是在c盘下进行的这也就是为什么如果c盘空间满的话电脑会特别卡。 进程信息被放在一个叫做进程控制块的数据结构中可以理解为进程属性的集合课本上称之为PCBprocess control block。 这样一来操作系统只要拿到这个双链表的头指针便可以访问到所有的PCB。此后操作系统对各个进程的管理就变成了对这条双链表的一系列操作。
例如创建一个进程实际上就是先将该进程的代码和数据加载到内存紧接着操作系统对该进程进行描述形成对应的PCB并将这个PCB插入到该双链表当中。而退出一个进程实际上就是先将该进程的PCB从该双链表当中删除然后操作系统再将内存当中属于该进程的代码和数据进行释放或是置为无效。 总的来说操作系统对进程的管理实际上就变成了对该双链表的增、删、查、改等操作。 所以总的来说根据上面描叙对于一个进程不仅仅是由我们想象的代码与数据组成的还要由内核PCB数据结构对象描述你所有的属性值同样还存在指针指向下一个PCB。 task_struct-PCB的一种
进程控制块PCB在描述进程的过程中从c这类高级编程语言来看就是面向对象但是Linux是用c语言来写的所以对于PCB他只能使用结构体。 对于task_structLinux中的PCB就是task_struct(在其他操作系统中的PCB就不一定叫task_struct)。task_struct是Linux内核的一种数据结构它会被装载到RAM内存里并且包含进程的信息。 task_struct内容分类
task_struct就是Linux当中的进程控制块task_struct当中主要包含以下信息
标示符 描述本进程的唯一标示符用来区别其他进程。状态 任务状态退出代码退出信号等。优先级 相对于其他进程的优先级。程序计数器(pc) 程序中即将被执行的下一条指令的地址。内存指针 包括程序代码和进程相关数据的指针还有和其他进程共享的内存块的指针。\上下文数据 进程执行时处理器的寄存器中的数据。I/O状态信息 包括显示的I/O请求分配给进程的I/O设备和被进程使用的文件列表。记账信息 可能包括处理器时间总和使用的时钟总和时间限制记账号等。其他信息。
此后所有对于进程的管理都被转换成了对数据结构PCB的增删查改这是一个对进程的管理建模的过程。 所以进程的正确定义进程是内核关于进程的相关数据结构与当前进程的代码和数据的结合。 查看进程
通过系统目录查看
在根目录下有一个名为proc的系统文件夹。 文件夹当中包含大量进程信息其中有些子目录的目录名为数字。
ls /proc 文件夹当中包含大量进程信息其中有些子目录的目录名为数字。
这些数字其实是某一进程的PID对应文件夹当中记录着对应进程的各种信息。我们若想查看PID为1的进程的进程信息则查看名字为1的文件夹即可。 通过ps命令查看
在一开始我们就是用ps aux 指令查看所有进程
那么这里我们就不单单显示效果了就说简单解释一下这些是什么吧。此部分只是了解内容重要的部分后面会详细说明。 1. USER 含义进程的所有者用户名即哪个用户在系统中启动了这个进程。示例如果进程由 root 用户启动显示为 root如果是普通用户启动的进程则会显示该普通用户的用户名。 2. PID 含义进程的 进程标识符Process ID是系统用来唯一标识每个运行中的进程的数字。俗称编号示例1234 表示进程的唯一 ID是操作系统用来管理该进程的标识符。 3. %CPU 含义进程当前占用的 CPU 百分比表示进程在系统中使用 CPU 时间的百分比。这个值是根据进程在一定时间内消耗的 CPU 时间和系统总 CPU 时间的比例计算的。示例如果显示为 5.0意味着该进程在过去的 1 秒钟里占用了系统 CPU 总量的 5%。注意如果是多核 CPU 系统该百分比可能会超过 100%。例如4 核 CPU 下一个进程可能占用 400% 的 CPU意味着该进程使用了 4 核的全部或多个核心的 CPU 时间。 4. %MEM 含义进程当前占用的 内存百分比表示进程使用的物理内存RAM大小与系统总内存的比例。示例如果显示为 2.1意味着该进程占用了系统总内存的 2.1%。注意这是基于物理内存的百分比并不考虑交换空间swap。 5. VSZ 含义进程的 虚拟内存大小Virtual Memory Size单位为 KB。这个值包括进程使用的所有内存包括共享库、映射文件等以及可能存在的虚拟内存空间比如被交换到硬盘上的内存。示例123456 表示该进程的虚拟内存占用了 123456 KB。 6. RSS 含义进程的 常驻内存集Resident Set Size单位为 KB。RSS 表示进程当前在物理内存中占用的实际内存不包括交换到磁盘的部分也就是进程占用的非虚拟内存部分。示例23456 表示该进程的 RSS 为 23456 KB。 7. TTY 含义进程所关联的 终端Terminal表示该进程是在哪个终端下启动的。如果进程没有终端如后台进程或系统进程这个字段显示为 ?。示例tty1 表示该进程是在 tty1 终端上运行? 表示该进程没有关联终端。 8. STAT 这部分后面细讲再次省略。 9. START 含义进程的 启动时间表示该进程开始运行的时间。格式通常是 小时:分钟对于启动时间较短的进程或 日期对于启动时间较长的进程。示例12:34 表示该进程在 12:34 启动Nov 28 表示该进程在 11 月 28 日启动。 10. TIME 含义进程已经使用的 CPU 时间即该进程自启动以来占用的总 CPU 时间通常以 分钟:秒 的形式显示。示例00:02 表示该进程已占用了 2 秒钟的 CPU 时间。 11. COMMAND 含义启动进程的 命令即运行该进程的完整命令行包括路径和参数。对于较长的命令行可能只会显示部分内容如果需要查看完整命令行可以使用 ps auxww 来显示完整内容。示例/usr/bin/python3 /path/to/script.py 表示该进程是运行 Python 脚本。 在Windows下我们也通过实践得知我们使用任务管理器查看当前运行的所有进程的时候此时被打开的任务管理器也会变为进程那么同样我们在使用ps命令查看当前的进程时候也会将ps作为一个进程运行起来。 此时我们就可以ps命令与grep -V grep命令搭配使用即可只显示某一进程的信息从而过滤ps命令。
ps aux | head -1 ps aux | grep proc | grep -v grep通过系统调用获取进程的PID和PPID
通过使用系统调用函数getpid和getppid即可分别获取进程的PID和PPID。
这里我们就不man 来查看原文的介绍了这里直接解释getpid与getppid是什么 getpid是返回其当前进程的pidgetppid是返回当前进程的ppidppid也就是其当前进程的父进程的pid需要引用头文件#include unistd.h 当运行该代码生成的可执行程序后便可循环打印该进程的PID和PPID。 我们可以通过ps命令查看该进程的信息即可发现通过ps命令得到的进程的PID和PPID与使用系统调用函数getpid和getppid所获取的值相同。 注意aux是显示详细信息ajx显示的稍微少一点。我这里用的是ajx,不是aux其实用aux也行只是这里不需要显示详细信息所有用ajx就可以。
通过系统调用创建进程
fork的认识
在以前我们熟悉的创建进程的方式有两种第一种是在Windows系统下我们双击一个 .exe 文件就创建了一个进程还有一种是在Linux系统下我们通过在命令行输入 ./ 来将程序变成进程去运行
现在我们再来学习一种创建进程的方式通过系统调用fork
同样不man查看官方的解释直接用案例来演示
我们写一个这样的代码然后运行查看效果 若是代码当中没有fork函数我们都知道代码的运行结果就是循环打印该进程的PID和PPID。而加入了fork函数后代码运行结果如下
可以看到也是打印了独两份二者不会相互干扰其中蓝色标识的ppid为红色标识的pid拿着也在侧面说明了其蓝色指向的进程的父进程为红色指向的进程。也就是说test1进程与fork函数创建的进程之间是父子关系。 每出现一个进程操作系统就会为其创建PCBfork函数创建的进程也不例外。 我们知道加载到内存当中的代码和数据是属于父进程的那么fork函数创建的子进程的代码和数据又从何而来呢 我们看看以下代码的运行结果 运行结果 实际上使用fork函数创建子进程在fork函数被调用之前的代码被父进程执行而fork函数之后的代码则默认情况下父子进程都可以执行。需要注意的是父子进程虽然代码共享但是父子进程的数据各自开辟空间采用写时拷贝也就是说如果不做任何修改的情况下还是共用。
特别提醒 使用fork函数创建子进程后就有了两个进程这两个进程被操作系统调度的顺序是不确定的这取决于操作系统调度算法的具体实现。
使用if进行分流
上面说到fork函数创建出来的子进程与其父进程共同使用一份代码但是如果让父进程创建的子进程与父进程做着完全相同的事情那么这就显得子进程没有存在的意义。
所以实际上我们创建子进程的目的就是为了让其与父进程做着完全不同的事想要达到这点要求还是需要先了解其fork函数的返回值。
fork函数的返回值
如果子进程创建成功在父进程中返回子进程的PID而在子进程中返回0。如果子进程创建失败则在父进程中返回 -1。
既然父进程和子进程获取到fork函数的返回值不同那么我们就可以据此来让父子进程执行不同的代码从而做不同的事。 fork创建出子进程后子进程会进入到else if 语句的循环打印当中而父进程会进入到 else语句的循环打印当中。 最后的总结
子进程与父进程代码共享其子进程直接用父进程的代码其自己本身无代码所以子进程无法改动代码平时所说的修改是修改的数据。为什么要创建子进程为了让其父子进程执行不同的代码块。子进程的数据相对于父进程是会进行写时拷贝COW。 Linux进程状态
一个进程从创建到撤销的整个生命周期中体现了进程与程序之间的区别。尽管在同一台机器上可以同时运行多个进程有些进程可能正在占用处理器执行而另一些进程则处于等待状态无法获得处理器资源。即使有空闲的处理器由于某些条件尚未满足这些进程仍然无法执行。这一切都表明进程是动态的并且具有状态变化的特性因此引入了“进程状态”这一概念。 这里我们具体谈一下Linux操作系统中的进程状态Linux操作系统的源代码当中对于进程状态有如下定义
/*
* The task state array is a strange bitmap of
* reasons to sleep. Thus running is zero, and
* you can test for combinations of others with
* simple bit tests.
*/
static const char *task_state_array[] {R (running), /* 0*/S (sleeping), /* 1*/D (disk sleep), /* 2*/T (stopped), /* 4*/t (tracing stop), /* 8*/Z (zombie), /* 16*/X (dead) /* 32*/
};小提醒 进程的当前状态是保存到自己的进程控制块PCB当中的在Linux操作系统当中也就是保存在task_struct当中的。
在Linux操作系统当中我们可以通过 ps aux 或 ps axj 命令查看进程的状态。
ps aux ps ajx 运行状态-R
运行状态根据名字也不难理解但是在实际上一个进程处于运行状态running并不意味着进程一定处于运行当中运行状态表明一个进程要么在运行中要么在运行队列里。也就是说可以同时存在多个R状态的进程。
但是如果自己去实验ps一个当前运行的进程可能我们查看到的并不是R状态。
这是因为进程在CPU上运行的时候并不是一直在运行的而是一个进程先在CPU上运行一会大概时10ms再切换另一个进程在CPU上运行一会不断的切换进程周而复始重复运作的。这叫做基于进程切换的分时操作系统由于CPU的运行速度非常快切换速度使人类感觉不到从而使人们有种进程一直在运行的感觉。所以在实际上可能在那1ms内该进程时运行的但是在下一ms又不运行了又可能有过了几ms又运行了然后实际算下来不运行的时间比运行的长所以在查看的时候不是R状态。 特别提醒 所有处于运行状态即可被调度的进程都被放到运行队列当中当操作系统需要切换进程运行时就直接在运行队列中选取进程运行。 浅度睡眠状态-S
一个进程处于浅度睡眠状态sleeping意味着该进程正在等待某件事情的完成处于浅度睡眠状态的进程随时可以被唤醒也可以被杀掉这里的睡眠有时候也可叫做可中断睡眠interruptible sleep。杀死进程可以使用kill指令然后指定杀死对应的进程 代码当中调用sleep函数进行休眠20秒在这期间我们若是查看该进程的状态则会看到该进程处于浅度睡眠状态。
ps aux | head -1 ps aux | grep test2 | grep -v grep 而处于浅度睡眠状态的进程是可以被杀掉的我们可以使用kill命令将该进程杀掉。
这里的-9是以编号为9的杀死方式去kill。 深度睡眠状态-D
一个进程处于深度睡眠状态disk sleep表示该进程不会被杀掉即便是操作系统也不行只有该进程自动唤醒才可以恢复。该状态有时候也叫不可中断睡眠状态uninterruptible sleep处于这个状态的进程通常会等待IO的结束。
例如某一进程要求对磁盘进行写入操作那么在磁盘进行写入期间该进程就处于深度睡眠状态是不会被杀掉的因为该进程需要等待磁盘的回复是否写入成功以做出相应的应答。磁盘休眠状态。
暂停状态-T
在Linux当中我们可以通过发送SIGSTOP信号使进程进入暂停状态stopped发送SIGCONT信号可以让处于暂停状态的进程继续运行。
例如我们对一个进程发送SIGSTOP信号该进程就进入到了暂停状态。 补充 S 状态表示 中断睡眠Interruptible Sleep即进程正在等待某个事件且可以被信号中断。S 状态表示 中断睡眠并且该进程是 前台进程组的一部分通常与终端操作相关。 补充kill 指令 kill -l 对应的名称就是其缩写。 比如12SIGUSR2 SIG代表 Signal信号 USR代表 User用户 2表示第二个用户定义的信号 暂停状态-t
这个没什么好解释的这个就是其在调试代码的时候遇见断点就会变为此状态但是实际上在vim中很少调试能直接观察出代码问题的话都是不建议调试。
死亡状态-X
死亡状态只是一个返回状态当一个进程的退出信息被读取后该进程所申请的资源就会立即被释放该进程也就不存在了所以你不会在任务列表当中看到死亡状态dead。
僵尸状态-Z 子退出
当一个进程将要退出的时候在系统层面该进程曾经申请的资源并不是立即被释放而是要暂时存储一段时间以供操作系统或是其父进程进行读取如果退出信息一直未被读取则相关数据是不会被释放掉的一个进程若是正在等待其退出信息被读取那么我们称该进程处于僵尸状态zombie。关于读取退出信息的工作是要有父进程来做读取方式是调用函数wait()函数后面介绍
首先僵尸状态的存在是必要的因为进程被创建的目的就是完成某项任务那么当任务完成的时候调用方是应该知道任务的完成情况的所以必须存在僵尸状态使得调用方得知任务的完成情况以便进行相应的后续操作。 例如我们写c语言代码时都在主函数最后返回0。 你是否思考过为什么我们要写0不能写123么 答案是可以的只是一般来说在 C 语言程序中我们习惯性地返回 0表示程序的成功执行。这并不是因为语法要求而是因为操作系统、脚本以及工具通常通过这个返回值来判断程序是否成功运行。对于别的都是存在问题的。 或与等哪天你可以创造一个习惯返回1的。 那么我再这里提到了c语言的main函数那么肯定是想靠拢僵尸进程的。
虽然在 C 语言代码中main 函数是程序的入口点但从操作系统的角度来看main 函数并不是程序的“最外层”。实际上main 函数是操作系统加载并运行程序后交给程序的一部分操作系统在背后执行了许多准备工作确保程序能够顺利启动和执行也就是去调用main函数这也就是为什么一个项目只能有一个main函数。
那么如果main函数的父进程没有处理main函数的退出信息在理论上main函数同样会变为僵尸进程但是在实际情况上这种事情根本不会发生。
以上说了那么多其实都在解释其实main函数也是一个进程其也有存在变为僵尸进程的风险。只不过这种风险根本不可能发生。
那么这个0就是返回给操作系统的告诉操作系统代码顺利执行结束。在Linux操作系统当中我们可以通过使用echo $?命令获取最近一次进程退出时的退出码。
echo $? 所以总的来说如果一个子进程结束时立刻退出父进程是没有机会拿到退出结果的。所以在Linux中进程退出时一般不会立即彻底退出而是要维持一个 Z 状态也叫僵尸状态方便后续父进程读取该子进程的退出结果。
补充 进程退出的信息例如退出码是暂时被保存在其进程控制块当中的在Linux操作系统中也就是保存在该进程的task_struct当中。
僵尸进程
前面说到一个进程若是正在等待其退出信息被读取那么我们称该进程处于僵尸状态。而处于僵尸状态的进程我们就称之为僵尸进程。
俗来说僵尸状态 是给 父进程 准备的当 子进程 被终止后会先维持一个 僵尸 状态方便 父进程 来读取到 子进程 的退出结果然后再将 子进程 回收。
例如对于以下代码创建一个维持30s的僵尸进程的例子 运行该代码后我们可以通过以下监控脚本每隔一秒对该进程的信息进行检测
while :; do ps aux | head -1; ps aux | grep test4 | grep -v grep; sleep 1; done 僵尸进程的危害
僵尸进程的退出状态必须一直维持下去因为它要告诉其父进程相应的退出信息。可是父进程一直不读取那么子进程也就一直处于僵尸状态。僵尸进程的退出信息被保存在task_struct(PCB)中僵尸状态一直不退出那么PCB就一直需要进行维护。若是一个父进程创建了很多子进程但都不进行回收那么就会造成资源浪费因为数据结构对象本身就要占用内存。僵尸进程申请的资源无法进行回收那么僵尸进程越多实际可用的资源就越少也就是说僵尸进程会导致内存泄漏。
到这里值得被关注的进程状态已经讲解完毕那么下补充知识说一些别的状态这些知识虽没有放在前面说但是也是十分的重要不是了解内容是掌握的内容非常重要
孤儿进程
在Linux当中的进程关系大多数是父子关系若子进程先退出而父进程没有对子进程的退出信息进行读取那么我们称该进程为僵尸进程。但若是父进程先退出那么将来子进程进入僵尸状态时就没有父进程对其进行处理此时该子进程就称之为孤儿进程。 若是一直不处理孤儿进程的退出信息那么孤儿进程就会一直占用资源此时就会造成内存泄漏。因此当出现孤儿进程的时候孤儿进程会被1号init进程领养此后当孤儿进程进入僵尸状态时就由int进程进行处理回收。
例如对于以下代码fork函数创建的子进程会一直打印信息而父进程在打印5次信息后会退出此时该子进程就变成了孤儿进程。 重要知识补充阻塞和挂起
一个进程可以有多个状态我们先来说明两个最为核心的状态阻塞和挂起。
以下解释来源于别人写的文章。
阻塞
进程因为等待某种条件就绪而导致的一种不推进的状态叫做阻塞状态给人们最直观的感受就是程序卡住了。换句话说一个进程阻塞一定是在等待某种所需要的资源就绪的过程。
想象这样一个场景我们在下载一些资料的时候如果网断了CPU还有必要继续调度这个下载进程吗肯定是没必要了因为没有意义此时就会把该进程设置为阻塞状态。那么这个进程是如何等待网络资源就绪的呢
首先再在这里提及一个知识点在Linux操作系统下一切设备皆文件那么比如我们日常所说的显示器其实他也在操作系统面前也是文件他是文件他就一定会被管理。
我们之前讲过操作系统需要管理网卡、磁盘等外设这个过程通常是一个“先描述再组织”的过程。操作系统会创建多个结构体类型如 struct dev然后将各个外设的属性信息填充到这些结构体中接着通过数据结构将它们组织在一起。同样地操作系统管理大量的进程时也是通过“先描述再组织”的方式进行的。具体而言操作系统会定义多个结构体来描述进程的各种属性并将这些进程通过链表、队列等数据结构进行组织和调度。 当网络断开时 需要等待网络资源的进程就会把自己的PCB从CPU的某些特定队列中拿取出来连接到网卡设备结构体队列的尾部来排队等待网络资源
此时再获取到等待的资源之前该进程不会再被CPU调度。 PCB是可以被维护在不同的队列中的。进程在等待哪种资源就会被排列到哪种资源的队列中去。再举个例子当我们在C语言中使用scanf 函数时运行程序如果我们不在键盘上输入内容进程就会处于阻塞状态并在键盘的结构体中排队等待资源只有拿到数据时进程才会再次被CPU调度。
总结阻塞就是不被CPU调度——一定是因为当前进程需要等待某种资源就绪——一定是进程tesk_struct结构体需要在某种被OS管理的资源下排队。
挂起
如果有时候出现了内存资源紧张的情况而且阻塞进程的PCB被接入到了所需要等待资源的结构体队列中不被调度。这时操作系统就会把阻塞进程的代码和数据交换到磁盘中同时释放其所在内存中占据的空间从而起到节省内存空间的目的。等到进程所需要的资源就绪的时候再把该进程的代码和数据加载到内存中交由CPU调度。 其中把进程的代码和数据由OS暂时性的交换到磁盘中时称该进程处于挂起状态。全称为阻塞挂起状态。挂起可以看作一种特殊的阻塞状态。
比如在我们生活中一边走路一边玩手机很危险所以此时我们会将玩手机这个 进程挂起 即把手机揣进兜里然后 专心执行走路这个 进程。 二者区别
是否释放CPU阻塞pend就是任务释放CPU其他任务可以运行一般在等待某种资源或信号量的时候出现。挂起suspend不释放CPU如果任务优先级高就永远轮不到其他任务运行。一般挂起用于程序调试中的条件中断当出现某个条件的情况下挂起然后进行单步调试。是否主动显然阻塞是一种被动行为其发生在磁盘网络IOwaitlock等要等待某种事件的发生的操作之后。因为拿不到IO资源所以阻塞时会放弃 CPU的占用。而挂起是主动的因为挂起后还要受到CPU的监督等待着激活所以挂起不释放CPU比如sleep函数站着CPU不使用。