文章目录
进程和线程
进程的概念
程序:是静态的,就是一个存放在磁盘里的可执行文件,就是一系列的指令集合。
进程:是动态的,是程序的一次执行过程。在现在的操作系统中,用户程序以进程方式占用系统资源。
典型的进程定义:
- 进程是程序的一次执行
- 进程是一个程序以及其数据在处理机上顺序执行时所发生的活动
- 进程是系统进行资源分配和调度的一个独立单位
操作系统负责创建进程 ,为进程分配资源 、调度进程占用处理机等。
进程和程序的区别
- 进程是动态的,程序是静态的:程序是有序代码的集合,通常对应着文件、静态和可以复制。进程是程序的执行
- 进程是暂时的,程序是永久的,进程是一个状态变化的过程,程序可长久保存。
- 进程与程序组成不同 :进程的组成包括程序、数据和PCB(进程控制块) ,PCB是进程存在的唯一标志
- 进程与程序的对应关系:进程是程序在数据集上的一次执行,通过多次执行,一个程序可以对应多个进程,通过调用关系,一个进程可以包括多个程序。
举个例子:正在运行的QQ音乐是一个进程,正在登录微信也是一个进程。同时登录多个QQ就是多个不同的进程。
PCB(进程控制块)
PCB是进程存在的唯一标志,当进程被创建时,操作系统就会为其创建PCB,但你进程结束时,会回收其PCB。
当进程被创建时,操作系统会为该进程分配一个唯一的、不重复 的进程id,叫做pid。
- 操作系统要记录PID、进程所属用户ID(UID),PID用来区分不同的进程
- 还要记录给进程分配了哪些资源(如:分配了多少内存、正在使用哪些I/O 设备、正在使用哪些文件),可以用于实现操作系对资源的管理
- 还要记录进程的运行情况(如:CPU使用时间、磁盘使用情况、网络流量使用情况等),可以用于实现操作系统对进程的调度
这些信息都要被保存在一个数据结构PCB中,PCB也叫做进程控制块 。操作系统需要对各个并发运行的进程进行管理,但凡管理时所需要的信息,都会被放到PCB中
PCB包含以下信息:
-
进程标识符
- 外部标识符:创建者提供,便于记忆,比如进程名(可以基于执行的文件名)
- 内部标识符:系统为每个进程分配一个唯一的pid
-
处理机状态信息
处理机在运行时,许多信息都放在寄存器中。当处理机被中断时,所有这些信息都必须保存在PCB 中,以便在该进程重新执行时,能从断点继续执行。
- 通用寄存器:用户访问,用于暂存信息(进程运行的现场信息
- 指令计数器:存放了进程下执行的下一条指令的地址
- 程序状态字:含有状态信息,如条件码,执行模式,中断屏蔽标志
- 用户栈指针:指每个用户进程都有一个或若千个与之相关的系统栈,用存放过程和系统调用参数及调用地址,栈指针指向该栈的栈顶。过程调用/系统调用/中断处理和返回时需要用
-
进程调度(控制和管理)信息
- 进程状态,指明进程的当前状态,作为进程调度和对换时的依据;
- 进程优先级,用于描述进程使用处理机的优先级别的一个整数,优先级高的进程应优先获得处理机;
- 进程调度所需的其它信息,它们与所采用的进程调度算法有关,比如,进程已等待 CPU 的时间总和、进程已执行的时间总和等;事件,阻塞原因
-
进程控制信息(资源分配清单)
- 程序和数据的地址;
- 进程同步和通信机制:
- 资源清单:除CPU以外的全部资源及已经分配到该进程的资源清单
- 链接指针:本进程PCB所在队列中的下一个进程的PCB的首地址
PCB其实就是一个C语言的结构体,这个结构体里存放着一个进程的信息
c
struct task_struct {
// 进程标识符
pid_t pid;
// 当前进程的状态
volatile long state;
//打开文件信息
struct files_struct *files;
//进程的优先级.....
}
一个系统中,通常拥有很多个PCB。常用的组织方法有三种
-
线性方式
所有PCB组织在一张线性表中,存表的首地址,每次扫描整表
-
链接方式
执行指针、就绪队列、阻塞队列、空闲队列等
-
索引方式
根据状态不同,建立索引表(就绪索引表、阻塞索引表
程序是如何运行的
一个进程的实体由PCB、程序段和数据段 组成,进程是动态 的,进程实体是静态的。进程实体可以反应进程在某一时刻的状态,(如a=10,a++后等于11)
- PCB是给操作系统使用的数据结构
- 程序段和数据段是给进程自己使用的。
比如说我们编写一段C语言程序,先要编译成一个.exe的可执行文件,这个可执行文件是存放在硬盘里面的,在运行这个.exe的可执行程序之前需要把程序放入内存当中。一个程序开始运行前需要为其创建对应的进程,也就是创建对应的PCB。
除了PCB之外,还需一个在内存中开辟一个程序段 和数据段 。这个程序段 就包含了程序要执行的指令,而数据段包含的是程序运行过程中产生的各种数据。
进程是进程实体的运行过程,是操作系统分配资源的最小单位
进程的特征
-
动态性:
动态性是进程最基本的特征,进程是程序的一次执行过程,是动态产生、变化和消亡的。
-
并发性
内存中同时有多个进程实体,各个进程可以并发执行
-
独立性
每个进程都是独立运行互不影响的,独立获得资源、独立接收系统调度
-
异步性
各个进程按各自独立的,不可预知的速度向前推进,也就是说在不考虑资源共享的情况下,各个进程的执行是独立的,操作系统要提供"进程同步机制"来解决一步问题,异步性会导致并发程序的执行结果具有不确定性。
-
结构性
每个进程都会配置一个PCB,结构上看,进程由程序段、数据段、PCB组成
进程的状态和状态转换
-
创建态
一个进程被创建出来,操作系统会为进程分配资源,初始化PCB,此时进程处于创建态
-
就绪态
当进程创建完成后,就会进入就绪态 ,处于就绪态的进程就已经具备了运行条件了,但由于没有空闲CPU,就暂时不能运行,处于就绪态的进程获得了除CPU以外的所有资源
-
运行态
当CPU空闲时,操作系统就会从就绪队列中选择一个处于就绪态 的进程让它上CPU上运行,此时这个真正运行的进程的状态就是运行态 ,CPU会执行该进程对应的程序(执行指令),此时进程占有了CPU在内的全部资源 ,在单核CPU 的情况下同一时刻最多只能有一个进程处于运行态。
-
阻塞态
在进程运行的过程中,可能会请求等待某个事件的发生 (如等待某种系统资源的分配,或者等待其它进程的响应)。在这个事件发生之前,进程无法继续往下执行,此时操作系统会让这个进程下CPU,并让它进入阻塞态
比如说打印机正在为别的进程服务,此时另外一个进程2也想使用打印机,因为此时打印机正在为别的进程服务没空处理这个进程的请求,为了不让进程2占用CPU资源,此时就会让这个进程2进入阻塞态。
此时CPU处于空闲状态的话,操作系统就又会从内存中选择一个处于就绪态的进程上CPU行执行。
等使用打印机的进程结束了,此时打印机资源就空闲下来,此时操作系统就会将打印机资源分配给进程2,此时进程2等待事件就已经发生了 ,此时就把进程2从阻塞态变成就绪态
-
终止态
假设当进程1运行结束的时候,它会发出一个exit的系统调用,请求操作系统终止该进程,此时该进程就会进入终止态 ,操作系统会让该进程下CPU,并回收内存空间等资源,最后还有回收改进程的PCB,PCB消失说明进程已经彻底消失了。
五态模型
- 阻塞态--->就绪态,是一种被动行为
- 运行态--->阻塞态是一种进程自身做出的主动行为
需要注意的是,不能由阻塞态直接转换为运行态,也不能由就绪态直接装换为阻塞态 。因为进入阻塞态是进程主动请求的,必然需要进程在运行时才能发出请求
进程控制
进程控制的主要功能是对系统中的所有进程控制实施有效的管理,它具有创建新进程、撤销已有进程、进程状态转换等功能。进程控制就是实现进程状态转换。
通过原语就能实现进程控制,原语的执行具有原子性,是不能不打断的。
进程状态装换为啥需要保证原子性
为啥进程控制(状态装换)的过程需要保证原子性?
假设PCB6此时处于阻塞队列如下图,如果此PCB6的等待事件发生,那么负责进程控制的内核程序至少要做这样两件事。
- 将PCB6的状态修改为1
- 将PCB6从阻塞队列放入就绪队列
那么如果执行完第一步的时候,此时CPU收到了一个中断信号,此时的PCB6的state=1,确处于阻塞队列显然不合理,可能会影响操作系统后续的一些操作。
如何实现原语的原子性?
原语 的执行具有原子性 ,也就是执行过程中不允许被中中断 。可以使用**"开中断"和 "关中断"指令,这两个特权指令实现原子性**。
正常情况:cpu每执行完一条指令都会例行检查是否有中断信号需要处理,如果有
则暂停运行当前这段程序,转而执行相应的中断处理程序。
CPU如果执行了关中断指令 ,就不在和以前一样,每执行一条指令都会检查是否有中断信号需要处理,而是直接继续往下执行对应的指令,直到执行开中断指令才会恢复检查中断信号,再执行中断处理程序。
为啥开中断关中断这两特权指令允许用户使用会发送什么?
如果开关中断可以给用户使用的话,那么用户只需在程序运行开始执行一条开中断指令,在应用程序结束前才执行开中断,那么这个用户的应用程序就会一直在CPU上运行。
进程控制相关原语
进程创建
创建原语
- 申请空白PCB:获得唯一标识pid
- 为新进程分配所需资源:为新进程的程序和数据以及用户栈分配必要的内存空间
- 初始化PCB:设置进程的状态、设置进程属性、初始化寄存器和上下文等
- 将PCB插入就绪队列
引起进程创建的事件
- 用户登录:分时系统中,用户登录成功,系统会为其建立一个新的进程
- 作业调度:多道批处理系统中,有新的作用放入内存时,会为其建立一个新的进程
- 提供服务:用户向操作系统提出某些请求时,会创建新的进程处理请求
- 应用请求:用用户进程主动请求创建一个子进程
进程终止
撤销原语
撤销原语可以让进程从就绪态/阻塞态/运行态直接变成终止态
- 从PCB集合中找到终止进程的PCB
- 若进程正在运行,立即剥夺CPU,将CPU分配给其它进程
- 终止其所有子进程
- 将该进程拥有的所有资源归还给父进程或操作系统
- 删除PCB
引起进程终止的事件
- 正常结束(进程自己请求终止,exit系统调用)
- 异常结束(整数除以0,非法使用特权指令被操作系统强行杀死)
- 外界干预(在Linux中kill掉一个进程)
进程的阻塞和唤醒
阻塞原语和唤醒原语必须是成对使用的
进程的阻塞
阻塞原语
- 找到阻塞的进程对应的PCB
- 保护进程运行现场,将PCB状态信息设置为"阻塞态",暂停停止进程运行
- 将PCB插入相应事件的等待队列
引起进程阻塞的事假
- 需要等待系统分配某种资源
- 需要等待相互合作的其他进程完成工作
进程的唤醒
唤醒原语
- 在事件等待队列中找到PCB
- 将PCB从等待队列中移除,设置进程为就绪态
- 将PCB插入到就绪队列,等待被调度
引起进程唤醒的事件
- 等待事件的发生(因何事阻塞,就应由何事唤醒)
进程的切换
切换原语
- 将运行环境(进程上下文)信息存入PCB,包括程序计数器和其它寄存器信息。
- PCB移入相应队列
- 选择另一个进程执行,并更新PCB
- 根据PCB恢复新进程所需要的运行环境
引起进程切换的事件
- 当前进程时间片到
- 有更高优先级的进程到达
- 当前进主动阻塞
- 当前进程终止
进程切换的过程
- 保存处理机上下文,包括程序计数器和其他寄存器
- 更新PCB信息。
- 把进程的PCB移入相应的队列,如就绪、在某事件阻塞等队列
- 选择另一个进程执行,并更新其PCB
- 更新内存管理的数据结构
程序是如何执行的?
- PSW:记录当前程序状态
- PC:程序计数器,记录下一条要执行的指令
假设执行完指令2后,另外一个进要上CPU上执行,也要使用这些寄存器,就可以把一些像PSW、PC、IR,通用寄存器这些必要的信息保存到PCB当中,再让新的进程调度到CPU上执行。
进程通信
进程间通信是指两个进程之间产生数据交互,比如说王者荣耀登录时需要调用QQ或者微信进行登录,这就涉及到进程间的通信。
进程间的通信为什么需要操作系统的支持?
进程是操作系统分配资源的最小单位,因此各进程 拥有的内存地址空间是相互独立的,也就是说进程A不能直接访问进程B的地址空间,这属于内存越界访问,如果进程A只可以直接访问进程B的地址空间的话,那么进程B的数据可能就会被进程A给破坏,又或者说进程A把进程B的隐式数据给直接读取走。
共享存储
基于存储区共享
共享存储的进程通信方式指的是,一个进程可以在内存中开辟一块内存空间共享存储区 ,这个共享存储区可以被其它进程给共享。假设进程A要给进程B发送数据,那么进程A就可以把要发送的数据写入到共享存储区,进程B再从这个共享存储区读取数据。需要注意的是为了避免出错,各个进程对共享空间的访问应该是互斥的 。基于存储区的共享是操作系统在内存中划出一块共享存储区,数据的形式、存放位置都由通信进程控制 ,而不是操作系统。这种共享方式速度很快 ,是一种高级通信方式 。这种通信方式自由度高,开辟出来的共享存储区,想怎么分配数据怎么存都有进程自己决定。
基于数据结构共享
基于数据结构的共享:比如共享空间里只能放个长度为10的数组。这种共享方式速度慢 、限制多,是一种低级通信方式 。而这种通信方式自由度比较低,就比如是一个Int数组的结果,存储或者读取数据只能以Int为单位。
消息传递
直接通信方式
进程间的数据交换以格式化的消息 (Message)为单位。进程通过操作系统提供的"发送消息/接收消息"两个原语 进行数据交换。这种指明接收进程ID的消息传递方式,叫做直接通信方式
进程A通过发送原语send发送消息给进程B,这个发送原语会让操作系统接收到这个消息,就会将这个进程A的msg复制到进程B的PCB中的消息队列里。
然后进程B通过接收原语receive指定接收进程A的消息,那么就会从操作系统内核中的进程B的PCB中的消息队列中将进程A发送过来的msg拿到进程B的地址空间中。
间接通信方式
间接通信方式通过"信箱"间接地通信,因此又称为"信箱通信方式"。通过信箱的方式间接的获取到另外一个进程的消息。
进程A通过系统调用向操作系统申请一个或者多个信箱,通过发送原语把msg发送到信箱Q里面。
然后进程B通过接收原语,从指定从信箱Q中接收消息,从而获得msg。
通常来说操作系统是可以允许多个进程往同一个信箱send消息,也可以多个进程从同一个信箱中receive消息。
管道通信
管道通信和水管类似,只能从一边写数据或者从一边读数据,不能同时写数据和读数据,这种也被称之为半双工。
"管道"是一个特殊的共享文件,又名pipe文件,其实就是某一个进程通过系统调用的方式 来申请一个管道文件,操作系统会新建这个管道文件,这个文件的本质在内存中开辟一个大小固定的内存缓冲区,然后两个进程可以进程写数据和读数据,这些数据是先进先出的。
管道通信和共享存储的区别:
共享存储通信开辟的空间想往哪写就往哪写,想从哪读就往哪读。
而管道通信只能安照指定的顺序写数据,同时按照指定的方式读数据。其实管道底层使用循环队列这种数据结构实现的。
- 管道只能采用半双工通信 ,如果要实现全双工通信就需要向操作系统申请两个管道文件
- 各个进程要互斥地访问管道(由操作系统实现)
- 当管道写满 时,写进程 将会阻塞,直到读进程将管道中的数据取走,即可唤醒进程
- 当管道为空 时,读进程 将阻塞,直到写进程往管道中写入数据,即可唤醒读进程。
- 管道中的数据一但被读出就彻底消失,因此当多个进程读同一个管道时,可能会错乱。对此,通常有两种解决方案:
- 一个管道允许多个写进程,一个读进程(考试?)
- 允许有多个写进程,多个读进程 ,但系统会让各个读进程轮流从管道中读数据 (Linux的方案)
线程概念
在很久以前没有引入进程时,系统中各个程序只能串行执行。也就是我们不能边听音乐边写代码以及QQ聊天。
但是引入进程之后,我们可以边听音乐边写代码,还可以边听音乐边用QQ聊天。但QQ不仅可以聊天,还可以进行视频和传输文件,进程是程序的一次执行,但这些功能显然不可能是由一个进程顺序处理就能实现的。
有的进程可能需要"同时"做很多事情,而传统的进程只能串行的执行一系列程序,因此引入了**"线程",来增加 并发度**。
传统的进程是程序执行流的最小单位,但引入了线程之后,CPU的调度服务对象就不再是进程,而是进程当中的线程 ,那么线程就成为了程序执行流的最小单位。
为啥要引入线程
可以把线程理解成为**"轻量级进程",线程是一个 基本的CPU执行单元**,也是程序执行流的最小单位。
引入线程之后,不仅是进程之间可以并发,进程内的各线程之间 也可以并发 ,从而进一步提升了系统的并发度,使得一个进程内也可以并发处理各种任务 (如QQ视频、文字聊天、传文件)。
引入线程后,进程 只作为除CPU之外的系统资源的分配单元(如打印机、内存地址空间等都是分配给进程的),这些资源都是分配个进程对的而不是线程的。
传统的进程之间并发,需要切换进程的运行环境,系统开销很大。因为进程之间的切换需要保存和恢复整个进程的上下文,开销较大。而线程是在进程内进行调度和切换的基本单位,线程切换的开销较小,因为线程共享进程的上下文。
线程之间的并发,如果是同一进程内的线程切换,则不需要切换进程环境,系统开销小。
引入线程后,并发所带来的系统开销减小。
线程的属性
线程是处理机调度的单位
多CPU计算机中,各个线程可占用不同的CPU
每个线程都有一个线程ID、线程控制块 (TCB)
线程也有就绪、阻塞、运行三种基本状态
线程几乎不拥有系统资源
同一进程的不同线程间共享进程的资源
由于共享内存地址空间,同一进程中的线程间通信甚至无需系统干预
同一进程中的线程切换,不会引起进程切换
不同进程中的线程切换,会引起进程切换
切换同进程内的线程,系统开销很小
切换进程,系统开销很大。
线程的实现方式
用户级线程
早期的操作系统(如早期的unix)只执行进程,不支持线程,当时的"线程"是由线程库来实现的。
假设下面这段伪代码就是一个简单的线程库,QQ要处理视频、聊天和问文件传输。
c
//伪代码
int main()
{
int flag = 0;
while (true) {
if (flag == 0)
{
// 处理视频聊天代码
}
else if(flag == 1)
{
// 处理问题聊天的代码
}
else if (flag == 2)
{
// 处理文件传输的代码
}
flag = (flag+1)%3;
}
return 0;
}
假设下图是早期操作系统来实现QQ进程,QQ的多线程是通过线程库来实现的,线程库完成了对线程的管理工作,比如说线程调度。很多编程语言提供了强大的线程库,可以时间线程的创建、销毁、调度等功能。
需要注意的这里的用户线程,是程序员写的一个线程库,创建的是逻辑上的线程。
所以,用户级线程 的管理是通过应用程序来通过线程库来完成的,线程的切换也不需要从用户态到内核态的切换,由线程库在用户态来完成。当然操作系统也是看不到这3个逻辑线程的存在的,操作系统只能看到这个进程的存在,进程中有一堆代码。
- 用户级线程 由应用程序通过线程库实现所有的线程管理工作都由应用程序负责 (包括线程切换)
- 用户级线程中,线程切换可以在用户态下即可完成,无需操作系统干预。
- 在用户看来,是有多个线程。但是在操作系统内核看来,并意识不到线程的存在 。
"用户级线程"就是"从用户视角看能看到的线程
优缺点
- 优点:用户级线程的切换在用户空间即可完成,不需要切换到核心态,线程管理的系统开销小,效率高
- 缺点:当一个用户级线程被阻塞后,整个进程都会被阻塞,并发度不高。多个线程不可
在多核处理机上并行运行。
内核级线程
内核级线程又叫**"内核支持的线程"**
- 内核级线程的管理工作由操作系统内核完成。
- 线程调度、切换等工作都由内核负责 ,因此内核级线程的切换必然需要在核心态下才能完成。
- 操作系统会为每个内核级线程建立相应的TCB (Thread Control Block,线程控制块)
通过TCB对线程进行管理。"内核级线程"就是"从操作系统内核视角看能看到的线程
优缺点
- 优点:当一个线程被阻塞后,别的线程还可以继续执行,并发能力强。多线程可在多核
处理机上并行执行。 - 缺点:一个用户进程会占用多个内核级线程线程切换由操作系统内核完成,需要切换到核心态,因此线程管理的成本高,开销大。
多线程模型
一对一模型
一对一模型 :一个用户级线程映射到一个内核级线程。每个用户进程有与用户级线程同
数量的内核级线程。
- 优点:当一个线程被阻塞后,别的线程还可以继续执行,并发能力强。多线程可在多核处理机上并行执行。
- 缺点:
一个用户进程会占用多个内核级线程线程切换由操作系统内核完成,需要切换到核心态,因此线程管理的成本高,开销大。
多对一模型
多对一模型 :多个用户级线程映射到一个内核级线程。且一个进程只被分配一个内核级
线程。
-
优点:用户级线程的切换在用户空间即可完成,不需要切换到核心态,线程管理的系统开销小,效率高
-
缺点:当一个用户级线程被阻塞后,整个进程都会被阻塞,并发度不高 。多个线程不可在多核处理机上并行运行
操作系统只"看得见"内核级线程,因此只有内核级线程才是处理机分配的单位。
多对多模型
在支持内核级线程的系统中,根据用户级线程和内核级线程的映射关
系,可以划分为几种多线程模型。
多对多模型:n用户及线程映射到m个内核级线程(n >= m)。每个用户进程对应m 个内核
级线程。克服了多对一模型并发度不高的缺点 (一个阻塞全体阻塞),又克服了一对一模型中一个用户进程占用太多内核级线程,开销太大的缺点
可以这么理解:
用户级线程是"代码逻辑"的载体内核级线程是"运行机会"的载体。
内核级线程才是处理机分配的单位,列如:多核CPU环境下,上图这个进程最多能被分配两个核。
一段"代码逻辑"只有获得了"运行机会"才能被CPU执行内核级线程中可以运行任意一个有映射关系的用户级线程代码,只有两个内核级线程中正在运行的代码逻辑都阻塞时,这个进程十会阻塞。
线程的状态和转换
线程的状态的装换和进程的状态转换完全一致,并且线程的状态我们只是关注最核心的三个状态,运行、阻塞、就绪。
线程的组织与控制
线程的控制通过**线程控制块(TCB)**完成,TCB中包含以下主要字段
- 线程标识符TID和PID类似
- 程序计数器PC:线程目前执行到哪里了
- 如果一个线程下CPU变成就绪或者阻塞状态,就需要把CPU中PC的值保存到程序计数器PC当中
- 如果一个线程又要上PCB上运行,就要恢复线程的运行现场和运行环境,就可以从TCB上取出PC放到PC寄存器里
- 其它寄存器:线程运行的中间结果,同理进行线程切换是需要保存中间的一些结果,就是将一些其它寄存器里的值保存到TCB中
- 堆栈指针:堆栈保存函数调用信息,局部变量等,比如说A函数掉用了B函数就会被记录到堆栈中
- 用于存储线程的局部变量、函数调用信息和临时数据。
- 比如说A调用B、B再调用C,并且会记录每次函数调用的返回地址是什么
- 同时每个函数的局部变量也会放到堆栈里
- 线程的堆栈是比较大的,全部放到堆栈指针里是没有必要的,只需要保存堆栈定指针
- 线程运行状态:运行/就绪/阻塞
- 优先级:线程的调度、资源分配的参考
程序计数器PC、其它寄存器、堆栈指针都是线程切换需要保存恢复的
通过线程表可以将多个TCB进行组织,可没一个进程有一张TCB线性表,又或者根据线程的状态组织一张线性表,不同的操作系统实现方式不同。
进程和线程的区别
- 进程是操作系统分配资源的基本单位
- 线程是程序执行的基本单位
- 进程是包含线程的,一个进程可以包含一个或者多个线程
- 进程拥有内存空间和文件资源,而线程共享进程的内存和文件资源
- 进程之间的通信需要操作系统参与,而线程之间可以直接共享
- 进程的切换开销比较到,需要保存上下文和状态,而线程的切换代价比较小,因为它们共享进程的资源。
- 当两个线程是属于同一个进程,因为虚拟内存是共享的,所以在切换时,虚拟内存这些资源就保持不动,只需要切换线程的私有数据、寄存器等不共享的数据;