【Linux操作系统分析】进程——进程切换,进程的创建和撤销

2023-05-16

1 进程

进程是程序执行时的一个实例,可以把它看作充分描述程序已经执行到何种程度的数据结构的汇集。

从内核的观点看,进程的目的是担当分配系统资源(CPU时间,内存等)的实体。

(早期的系统中)当一个进程创建时,它几乎与父进程相同,执行的下一条指令与父进程父进程相同,但是之后的活动是与父进程没有关系,它们各自有独立的数据拷贝(栈和堆)。

多线程应用程序多个执行流的创建、处理、调度都是在用户态进行的。

Linux使用轻量级进程对多线程应用程序提供更好的支持。


2 进程描述符(process descriptor)

进程描述符是task_struct类型结构,它的字段包含了与一个进程相关的所有信息。



3 进程状态

进程描述符中的state字段描述了进程当前所处的状态。它由一组标志组成,其中每个标志描述一种可能的进程状态。

下面是进程可能的状态:

  • 可运行状态
  • 可中断的等待状态
  • 不可中断的等待状态
  • 暂停状态
  • 跟踪状态
还有两个进程状态是既可以存放在进程描述符的state字段中,也可以存放在exit_state字段中。只有当进程被终止时,进程的状态才会变为这两种状态的一种:
  • 僵死状态:进程终止,但没有发布死亡消息,数据保留,可能会用到。
  • 僵死撤销状态:最终状态,进程由系统删除。

3.1 标识一个进程

一般来说,能被独立调度的每个执行上下文都必须拥有他/她自己的进程描述符,即自己的task_struct结构。
进程和进程描述符之间有非常严格的一一对应关系,32位进程描述符地址。
另一方面,进程标识符process ID(PID)可用来表示进程,PID存放在进程描述符的pid字段中。最大的PID值是32767。在64位体系结构中,PID上限为4194303.
PID的管理:pidmap-array位图。32位体系结构中pidmap-array位图存放在一个单独的页中。
一个多线程应用程序中的所有线程都必须有相同的PID。
线程组:一个线程组中的所有线程使用和该线程组的领头线程相同的PID,也就是该组中第一个轻量级进程的PID。

3.2 进程描述符处理

对每个进程来说,Linux都把两个不同的数据结构紧凑地存放在一个单独为进程分配的存储区域(动态内存)内:一个是内核态的进程堆栈,另一个是紧挨着进程描述符的小数据结构thread_info,叫做线程描述符。这块存储区域的大小通常为8192个字节(两个页框)。当使用一个页框存放内核态堆栈和thread_info结构时,内核要采用一些额外的栈以防止中断和异常的深度嵌套而引起的溢出。
因为tread_info结构是52个字节长,因此内核栈能扩展到8140个字节。
内核使用alloc_thread_info和free_thread_info宏分配和释放存储thread_info结构和内核栈的内存区。

3.3 标识当前进程

从效率的观点来看,刚才所讲的thread_info结构与内核态堆栈之间的紧密结合提供的主要好处是:内核很容易从esp寄存器的值获得当前在CPU上正在运行进程的thread_info结构的地址(通过屏蔽低13位或低12位)。这项工作由current_thread_info()函数完成。
这三条指令执行以后,p就包含在执行指令的CPU上运行的进程的thread_info结构的指针。又因为task字段在thread_info结构中的偏移量为0,所以执行完这三条指令之后,p就包含在CPU上运行进程的描述符指针。因为进程最常用的是进程描述符地址,而不是thread_info结构的地址。进程描述符地址可以通过:current_thread_info()->task获得。
current宏经常作为进程描述符字段的前缀出现在内核代码中,例如,current->pid返回在CPU上正在执行的进程的PID。
在多处理器系统上,有必要把current定义为一个数组,每一个元素对应一个可用CPU。
Linux内核定义的list_head数据结构:

3.4 进程链表——双向链表的第一个例子

进程链表把所有进程的描述符链接起来,每个task_struct结构都包含一个list-head类型的tasks字段,这个类型的prev和next字段分别指向前面和后面的task_struct元素。
进程链表的头是init_task描述符,它是所谓的0号进程(process 0)或swapper进程的进程描述符。
for_each_process宏,它的功能是扫描整个进程链表。
TASK_RUNNING状态的进程链表:
当内核寻找一个新进程在CPU上运行时,必须只考虑可运行进程。
提高调度程序运行速度的诀窍是简历多个可运行进程链表,每种进程优先权对应一个不同的链表。
enqueue_task(p,array)函数把进程描述符插入某个运行队列的链表。类似的,dequeue(p,array)函数从运行队列的链表中删除一个进程的描述符。

4 进程间的关系

程序创建的进程具有父子关系,如果一个进程创建多个子进程时,则子进程之间具有兄弟关系。
进程0和进程1是由内核创建的,进程1(init)是所有进程的祖先。
进程之间存在其他关系:一个进程可能是一个进程组或登录会话的领头进程,也可能是一个线程组的领头进程,它还可能跟踪其他进程的执行。
pidhash表及链表:
在几种情况下,内核必须能从进程的PID到处对应的进程描述符指针。顺序扫描进程链表并检查进程描述符的pid字段是可行但相当低效的。
4个散列表(因为进程描述符包含了表示不同类型PID的字段,每种类型的PID需要它自己的散列表):
内核初始化期间,动态地为4个散列表分配空间,并把它们的地址存入pid_hash数组。
散列函数并不总能确保PID与表的索引一一对应,两个不同的PID散列到相同的表索引称为冲突。
Linux利用链表来处理冲突的PID:每一个表项是由冲突的进程描述符组成的双向链表。
由于需要跟踪进程间的关系,PID散列表中使用的数据结构非常复杂。这个数据结构最主要的部分是四个pid结构的数组,它在进程描述符的pid字段中。
pid_hash数组的第二个元素存放散列表的地址,也就是hlist_head结构的数组表示链表的头。

5 如何组织进程

5.1 等待队列

等待队列在内核中有很多用途,尤其用在中断处理,进程同步及定时。
等待队列实现了在时间上的条件等待:希望等待特定事件的进程把自己放进合适的等待队列,并放弃控制权。因此,等待队列表示一组睡眠的进程,当某一条件变为真时,由内核唤醒它们。
等待队列数据结构为双向链表,其元素包括指向进程描述符的指针。
等待队列头是一个类型为wait_queue_head_t的数据结构
因为等待队列是由中断处理程序和主要内核函数修改的,因此必须对其双向连表进行保护以免对其进行同时访问,因为同时访问会导致不可预测的后果。同步是通过等待队列头中的Lock自旋锁打到的。task_list字段是等待进程聊表的头。
等待队列链表中的元素类型为wait_queue_t:
等待队列链表中的每个元素代表一个睡眠进程,该进程等待某一事件的发生,它的描述符地址存放在task字段中,task_list字段中包含的是指针,由这个指针把一个元素链接到等待相同事件的进程链表中。等待队列元素的func字段用来表示等待队列中睡眠进程应该用什么方式唤醒。
等待队列的操作:P103

6 进程资源限制resouce limit

进程资源限制 限制指定了进程能使用的系统资源数量,这些限制避免用户过分使用系统资源。对当前进程的资源限制存放在current->signal->rlim字段,即进程的信号描述符的一个字段。

7 进程切换(上下文切换)

7.1 硬件上下文

进程恢复执行前必须装入寄存器的一组数据称为硬件上下文。尽管每个进程可以拥有属于自己的地址空间,但所有进程必须共享CPU寄存器。因此,在恢复一个进程的执行之前,内核必须确保每个寄存器装入了挂起进程时的值,即硬件上下文。
prev局部变量表示切换出的进程的描述符,next表示切换进的进程的描述符。
我们把进程切换定义为这样的行为:保存prev硬件上下文,用next硬件上下文代替prev。
Linux是用软件执行进程切换。进程切换只发生在内核态。在执行进程切换之前,用户态进程使用的所有寄存器内容都已保存在内核态堆栈上,这也包括ss和esp这对寄存器的内容。

7.2 TSS(任务态段)——存放硬件上下文

尽管Linux不使用硬件上下文切换,但是强制它为系统中每个不同的CPU创建一个TSS。
在每次进程切换时,内核都更新了TSS的某些字段以便相应的CPU控制单元可以安全地检索到它需要的信息,因此,TSS反应了CPU上的当前进程的特权级。
每个TSS有它自己8字节的任务状态段描述符TSSD。
每个CPU只有一个TSS,因此,Busy位总置为1.
由Linux创建的TSSD存放在全局描述符表(GDT)中,GDT的基地址存放在每个CPU的gdtr寄存器中。每个CPU的tr寄存器包含相应的TSS的TSSD选择符,也包含两个隐藏的非编程字段:TSSD的Base字段和Limit字段。这样,处理器就能直接对TSS寻址而不用从GDT中检索TSS的地址。
thread字段:每个进程描述符包含一个类型为thread_struct的thread字段,只要进程被切换出去,内核就把其硬件上下文保存在这个结构中。

7.3 执行进程切换

从本质上说,每个进程切换由两步组成:
  1. 切换全局目录以安装一个新的地址空间
  2. 切换内核态堆栈和硬件上下文,因为硬件上下文提供了内核执行新进程所需要的所有信息,包括CPU寄存器
进程切换的第二步由switch_to宏执行。它是内核中与硬件关系最密切的例程之一。
首先,该宏有三个参数,它们是prev, next 和last。在任何进程切换中,涉及到三个进程而不是两个。
__switch_to()函数
(跳过)保存和加载FPU, MMX, 及XMM寄存器 P115

8 (KD)创建进程

现代Unix内核通过引入三种不同的机制来创建新进程:
  • 写时复制技术,允许父子进程读相同的物理页。只要两者有一个试图写一个物理页,内核就把这个页的内容拷贝到一个新的物理页,并把这个新的物理页分配给正在写的进程。
  • 轻量级进程允许父子进程共享进程在内核的很多数据结构,如页表、打开文件及信号处理
  • vfork()系统调用创建的进程能共享其父进程的内存地址空间。为了防止父进程重写子进程需要的数据,阻塞父进程的执行,一直到子进程退出或执行一个新的程序为止。

8.1 clone(), fork(), vfork()

clone()函数:创建轻量级进程的堆栈并且调用对编程者隐藏的clone()系统调用。调用sys_clone()服务例程。
fork()函数:用clone()实现,它的child_stack参数是父进程当前的堆栈指针。因此,父进程和子进程暂时共享同一个用户态堆栈。通常只要父子进程中有一个试图去改变栈,则立即各自得到用户态堆栈的一份拷贝。
do_fork()函数负责处理clone(), fork()和vfork()系统调用。利用辅助函数copy_process()来创建进程描述符以及子进程执行所需要的所有其他内核数据结构。
copy_process()函数:创建进程描述符以及子进程执行所需要的所有其他数据结构。(28个步骤)
do_fork()结束之后,继续完善子进程,1)把子进程描述符thread字段的值装入几个CPU寄存器;2)在fork(),vfork()或clone()系统调用结束时,新进程将开始执行。

9 内核线程

内核线程不同于普通线程:
  • 内核线程只运行在内核态,而普通线程既可以运行在内核态,也可以运行在用户态
  • 因为内核线程只运行在内核态,它们只使用大于PAGE_OFFSET的线性地址空间,另一方面,不管在用户态还是在内核态,普通进程可以用4GB的线性地址空间。
kernel_thread()函数创建一个新的内核线程。
进程0:所有进程的祖先。只有当没有其他进程处于TASK_RUNNING状态时,调度程序才选择进程0.
进程1:又叫init进程,start_kernel()函数初始化内核需要的所有数据结构,激活中断,创建另一个叫进程1的内核线程(一般叫做init进程):
               kernel_thread(init, NULL, CLONE_FS|CLONE_SIGHAND);
新创建的内核线程的PID为1,并与进程0共享进程所有的内核数据结构。当调度程序选择到它时,init进程开始执行init()函数。init()函数一次完成内核初始化:init()调用execve()系统调用装入可执行程序init。结果,init内核线程变为一个普通进程,且拥有自己的每进程内核数据结构。在系统关闭之前,init进程一直存活,因为它创建和监控在操作系统外层执行的所有进程的活动。

9 撤销进程

进程终止的一般方式是调用exit()库函数:该函数释放C函数库所分配的资源,执行编程者所注册的每个函数,并结束从系统回收进程的那个系统调用。
进程终止(两个系统调用):
  • exit_group()系统调用,它终止整个线程组,即整个基于多线程的应用。do_group_exit()
  • exit()系统调用,它终止某一个线程,而不管该线程所属线程组中的所有其他进程。do_eixt()
进程删除:
wait()库函数:检查子进程是否终止。如果子进程已经终止,那么它的终止代号将告诉父进程这个任务是否已成功地完成。
不允许UNIX内核在进程一终止后就丢弃包含在进程描述符字段中的数据。只有父进程发出了与被终止的进程相关的wait()类系统调用后,才允许这样做。(即引入的僵死状态)。从技术上讲,进程已死,但必须保存它的描述符,直到父进程得到通知。

转载于:https://www.cnblogs.com/suzhou/archive/2013/05/24/3638995.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【Linux操作系统分析】进程——进程切换,进程的创建和撤销 的相关文章

  • 是否从页面缓存中的脏页面进行文件读取?

    当字节写入文件时 内核不会立即将这些字节写入磁盘 而是将这些字节存储在页缓存中的脏页中 回写缓存 问题是 如果在脏页刷新到磁盘之前发出文件读取 则将从缓存中的脏页提供字节 还是首先将脏页刷新到磁盘 然后进行磁盘读取以提供字节 将它们存储在进
  • Linux 上的 Python 3.6 tkinter 窗口图标错误

    我正在从 Python GUI 编程手册 学习 Python GUI 某项任务要求我通过将以下代码添加到我的配方中来更改窗口图标 Change the main windows icon win iconbitmap r C Python3
  • 套接字:监听积压并接受

    listen sock backlog 在我看来 参数backlog限制连接数量 这是我的测试代码 server initialize the sockaddr of server server sin family AF INET ser
  • numpy 未定义符号:PyFPE_jbuf

    我正在尝试使用一百万首歌曲数据集 为此我必须安装 python 表 numpy cython hdf5 numexpr 等 昨天我设法安装了我需要的所有内容 在使用 hdf5 遇到一些麻烦之后 我下载了预编译的二进制包并将它们保存在我的 b
  • 如何用X11复制到剪贴板?

    使用 OS X 上的框架 我可以使用以下命令将 PNG 复制到粘贴板 在 C 中 显然我可以将 NSPasteboard 与 Cocoa 一起使用 include
  • 如何在我的 AWS EC2 实例上安装特定字体?

    我有一个在 AWS EC2 Amazon Linux Elastic Beanstalk 实例上运行的 Python 应用程序 该实例需要某些特定字体才能生成输出 并且想知道如何在部署或实例启动过程中安装它们 我的代码在本地计算机 OS X
  • 如何让 clangd 转向 c++20

    当没有其他信息时 如何让 clangd 回退到 c 20 例如 在第一次构建之前 cmake 可以生成一个 这是在带有最新 LLVM 的 Arch Linux 上 这是通过 Emacs LSP 运行的 但这应该没有什么区别 你可以加 Com
  • 在 LINUX 上使用 Python 连接到 OLAP 多维数据集

    我知道如何在 Windows 上使用 Python 连接到 MS OLAP 多维数据集 嗯 至少有一种方法 通常我使用 win32py 包并调用 COM 对象进行连接 import win32com client connection wi
  • 无法安装 WWW::Curl::Easy: SZBALINT/WWW-Curl-4.17.tar.gz : make NO

    我正在尝试在我的 Fedora 26 机器上安装 WWW Curl Easy gcc c I usr include D REENTRANT D GNU SOURCE O2 g pipe Wall Werror format securit
  • 无法执行'x86_64-conda_cos6-linux-gnu-gcc':没有这样的文件或目录(pysam安装)

    我正在尝试安装 pysam 执行后 python path to pysam master setup py build 这个错误的产生是 unable to execute x86 64 conda cos6 linux gnu gcc
  • Linux 中热插拔设备时检测设备是否存在

    我正在运行 SPIcode http lxr free electrons com source drivers spi spi omap2 mcspi c在熊猫板上 我想知道其中的哪个功能code http lxr free electr
  • 在Linux中断上下文中运行用户线程

    我正在编写一些定制的应用程序 并允许更改 Linux 内核中的中断处理程序代码 我有一个用户线程正在等待中断发生 如果发生中断 那么我要做的第一件事就是执行该用户线程 有什么办法让它发挥作用吗 Thanks 创建一个字符设备 这就是内核所做
  • Linux shell 脚本:十六进制数字到二进制字符串

    我正在 shell 脚本中寻找一些简单的方法来将十六进制数字转换为 0 和 1 字符的序列 Example 5F gt 01011111 是否有任何命令或简单的方法来完成它 或者我应该为其编写一些开关 echo ibase 16 obase
  • 如何让“grep”从文件中读取模式?

    假设有一个很大的文本文件 我只想打印与某些模式不匹配的行 显然 我可以使用egrep v patter1 pattern2 pattern3 现在 如果所有这些模式都在一个文本文件中怎么办 最好的制作方法是什么egrep从文件中读取模式 g
  • C 程序从连接到系统的 USB 设备读取数据

    我正在尝试从连接到系统 USB 端口的 USB 设备 例如随身碟 获取数据 在这里 我可以打开设备文件并读取一些随机原始数据 但我想获取像 minicom teraterm 这样的数据 请让我知道我可以使用哪些方法和库来成功完成此操作以及如
  • 劫持系统调用

    我正在编写一个内核模块 我需要劫持 包装一些系统调用 我正在暴力破解 sys call table 地址 并使用 cr0 来禁用 启用页面保护 到目前为止一切顺利 一旦完成 我将公开整个代码 因此如果有人愿意 我可以更新这个问题 无论如何
  • Linux 桌面快捷方式和安装图标

    我需要添加什么到我的 spec文件来创建桌面快捷方式并在安装过程中为快捷方式分配一个图标 rpm 如果需要脚本 一个示例将非常有帮助 您在 Linux 下使用 desktop 文件作为图标 图标放置的位置取决于您使用的发行版和桌面环境 由于
  • 在 /dev/input/eventX 中写入事件需要哪些命令?

    我正在开发一个android需要将触摸事件发送到 dev input eventX 的应用程序 我知道C执行此类操作的代码结构如下 struct input event struct timeval time unsigned short
  • 嵌入式Linux poll()不断返回

    我有一个特别的问题 当我知道没有什么可读时 民意调查不断返回 因此设置如下 我有 2 个文件描述符 它们构成fd设置民意调查监视 一种用于引脚从高到低的变化 GPIO 另一个用于代理输入 代理输入出现问题 处理的顺序是 启动main函数 然
  • QFileDialog::getSaveFileName 和默认的 selectedFilter

    我有 getSaveFileName 和一些过滤器 我希望当用户打开 保存 对话框时选择其中之一 Qt 文档说明如下 可以通过将 selectedFilter 设置为所需的值来选择默认过滤器 我尝试以下变体 QString selFilte

随机推荐

  • 删除SQL server 实例

    在网上找到下面几种方法 xff0c 本人使用的是第一种 xff0c 很实用 1 删除 SQL Server 的特定实例 若要删除 SQL Server 的某个特定实例 xff0c 请按照以下步骤操作 xff1a 找到并删除 drive Pr
  • 神舟战神 HotKey 主面板无法打开? Fn+Esc失效?

    战神笔记本的 Fn 43 Esc快捷键是打开 HotKey 主面板的 xff0c 其实就是 Control Center Windows更新可能导致 Control Center 无法打开 xff0c 解决办法就是 更新驱动 更新驱动之前
  • K8S镜像删除及环境清理

    环境清理 删除所有容器 sudo docker rm f sudo docker ps qa 删除 var etcd目录 sudo rm rf var etcd 删除 var lib kubelet 目录 xff0c 删除前先卸载 for
  • HTTP认证之摘要认证——Digest(二)

    导航 HTTP认证之基本认证 Basic xff08 一 xff09 HTTP认证之基本认证 Basic xff08 二 xff09 HTTP认证之摘要认证 Digest xff08 一 xff09 HTTP认证之摘要认证 Digest x
  • 关于AD10如何输出自己想要的BOM表

    新年新开始 xff0c 过完年来上班 xff0c 脑子里面的东西很多又忘光了 xff0c 索性写下来做个备忘录 xff0c 今天为了输出一个自己想要的BOM表 xff0c 结果发现去年会弄的 xff0c 但是现在尴尬的又忘了怎么弄了 xff
  • authorization权限控制_shiro入门学习--授权(Authorization)|筑基初期

    写在前面 经过前面的学习 xff0c 我们了解了shiro中的认证流程 xff0c 并且学会了如何通过自定义Realm实现应用程序的用户认证 在这篇文章当中 xff0c 我们将学习shiro中的授权流程 授权概述 这里的授权指的是授予某一系
  • Spring Boot+Spring Security+JWT 实现 RESTful Api 认证(二)

    摘要 上一篇Spring Boot 43 Spring Security 43 JWT 实现 RESTful Api 认证 xff08 一 xff09 我们已经sql教程 实现了基本的登录和token认证接口 xff0c 但是这里有个问题
  • vmware vsphere各版本差别,及各套件差别

    最近要开始全面支持虚拟化了 xff0c 客户私有云环境用的多的为vmware vsphere xff0c 特地恶补下vmware vsphere的各个差别 首先是vSphere xff0c ESXi和vCenter 的区别 ESXi vSp
  • 第五周课程总结&试验报告(三)

    第五周课程总结 xff1a 一 xff1a String类 1 xff1a 实例化String对象 xff0c String可以采取直接赋值的方式进行操作 xff1b 2 xff1a 61 61 可以用来进行内容的比较 xff0c 但是单纯
  • CSS3 选择器

    CSS3 选择器 在 CSS 中 xff0c 选择器是一种模式 xff0c 用于选择需要添加样式的元素 34 CSS 34 列指示该属性是在哪个 CSS 版本中定义的 xff08 CSS1 CSS2 还是 CSS3 xff09 选择器例子例
  • JavaScript中使用typeof运算符需要注意的几个坑

    typeof是一个运算符 xff0c 它对操作数返回的结果是一个字符串 xff0c 有6种 只针对ES xff0c 不包含HOST环境对象 1 39 undefined 39 2 39 boolean 39 3 39 string 39 4
  • unity视频教程

    英雄联盟教程 http pan baidu com s 1i3rkMS9 密码 bv6r https pan baidu com share link shareid 61 2589856556 amp uk 61 371904234 li
  • 相册列表 鼠标悬停显示照片介绍

    lt DOCTYPE HTML PUBLIC 34 W3C DTD HTML 4 01 Transitional EN 34 34 http www w3 org TR html4 loose dtd 34 gt lt html gt lt
  • 图书管理系统(毕业论文)

    毕 业 设 计 论 文 题 目 xff1a 图书管理系统 院 系 xff1a 计算机学院 专 业 xff1a 软件技术 姓 名 xff1a XXX 指导教师 xff1a XX 2017年 10 月 23 日 1 引言 5 2 相关技术突破
  • C#中定义数组--字符串及数组操作

    一 一维 xff1a int numbers 61 new int 1 2 3 4 5 6 不定长 int numbers 61 new int 3 1 2 3 定长 二 多维 int numbers 61 new int 1 2 3 1
  • 迭代器分类

    输入迭代器 读 xff0c 不能写 xff1b 只支持自增运算 istream iterator 61 61 61 43 43 gt 输出迭代器 写 xff0c 不能读 xff1b 只支持自增运算 ostream iterator 43 4
  • VC++中隐藏代码

    1 引言 在VS编辑器中可以对类中的方法 注释等内容进行隐藏 xff0c 单击左侧的 号即可完成隐藏 xff0c 隐藏后变为 43 xff0c 单击 43 号可以将隐藏的代码展开 2 隐藏任意代码 如果想在编辑器中隐藏任意代码段 xff0c
  • 常见签名算法之SHA256withRSA

    概述 在https blog csdn net chinoukin article details 100934995章节中 xff0c 我介绍了用Hmac算法用于签名算法中的方法 xff0c 本章节中将对常见的签名算法 SHA256wit
  • httpclient4封装类与HttpParser封装类

    httpclient4封装类与HttpParser封装类 最近工作中需要做一个爬虫去抓取指定页面的一些内容 xff0c 准备使用HttpParser来解析页面结构 xff0c 顺便看了一下httpclient4 xff0c 可以将它们配合使
  • 【Linux操作系统分析】进程——进程切换,进程的创建和撤销

    1 进程 进程是程序执行时的一个实例 xff0c 可以把它看作充分描述程序已经执行到何种程度的数据结构的汇集 从内核的观点看 xff0c 进程的目的是担当分配系统资源 xff08 CPU时间 xff0c 内存等 xff09 的实体 xff0