多线程——01
1. 进程/任务
process/task
进程是操作系统对一个正在运行的程序的一种抽象,换言之,可以把进程看做程序的一次运行过程
每个进程想要执行,都需要消耗一定的系统资源(硬件资源)
1)进程在系统中的作用
a.描述——使用类/对象(PCB结构),把被管理的一个对象的各个属性都表示出来
b.组织——使用数据结构(双向链表...),把表示出来的对象串起来(CARD)
2)进程控制块——PCB
系统中专门描述进程属性的结构体(属于操作系统,c/c++编写)
一个进程可以使用一个或多个PCB表示
系统中会使用类似双向链表的数据结构组织多个PCB
创建新的进程——创建PCB,并且把PCB插入到链表中
销毁进程——把PCB从链表上删除并释放空间
展示进程列表——遍历链表的每个节点
3)PCB的内容
PCB是相当庞大的结构体 Linux中用 task_struct 表示
PCB的内容:
a. Pid ——进程号(进程标识符)
每个进程都有一个Pi的。同一时刻 不同的进程之间的Pid的不同
b. 内存指针 ——一组属性
每个进程在运行时,都会分配一定的内存空间
(具体储存在哪里,分配的内存空间中的哪些部分以及作用)——一组指针
进程的内存空间,有专门的区域存储要执行的指令,以及指令以来的数据,存储一些运行时产生的临时数据
c. 文件描述符 ——类似顺序表的数据结构
关联哪些文件,操作哪些文件
一个进程若涉及硬件操作,需要按照文件的方式进行操作
d. 进程状态
就绪状态——进程正在CPU上执行,时刻准备好去CPU上执行
阻塞状态——某个进程的执行条件不具备,导致进程暂时无法参与CPU的调度执行
e.优先级
操作系统在调度多个进程时,有些进程会给更高的优先级,优先调度
f.上下文
进程从CPU离开之前,把当前CPU中各种寄存器的状态,记录到内存中,下次进程回到CPU上执行时,可以把保存的值恢复,进程就会沿着上次执行到的位置继续往后执行
i.记账信息
使用CPU的大小,占内存的大小。会记录当前进程持有的CPU情况
可以作为操作系统调度进程的依据
1. 内存指针——进程持有的内存资源
2. 文件描述符——进程持有的硬件资源
3. 状态,优先级,上下文,记账信息——用来完成“进程调度”(与进程持有的内存资源有关)
4. 进程太多,CPU核心太少,就需要让这些进程轮番在CPU上执行,只要轮转的速度够快,宏观上,这些进程就是在“同时”执行——并发
一个进程想要执行,需要CPU执行指令
一个CPU核心,同一时刻只能执行一个进程的指令 CPU 内核是CPU中间的核心芯片
此时就需要 1)分时复用 2)并发 来实现
如果两个进程同时在两个CPU上,微观上是“同时执行”——并行
一个CPU核心通过快速轮转的方式调度的方式,执行多个进程,宏观上是“同时执行”,微观上有先后——并发
4)寄存器
CPU中有些寄存器属于没有特定含义,只是用来保存运算的中间结果,还有些寄存器有特定的含义和作用
a. 保存当前执行到那个指令(程序计数器)
是一个2字节/4字节/8字节整数——程序下一条要执行的指令所在的位置
.exe里面包含 指令 和 数据
把exe运行起来,操作系统就会把指令和数据加载到内存中(内存地址)
CPU就会从内存中取指令,再执行指令
初始情况下,程序计数器指向进程指令的入口
每取完一条指令,程序计数器的值会自动更新默认情况下直接指向下一条(顺序执行)
若遇到跳转类指令(jmp,jcmp,call...)就会被设置成跳转到的地址
b.维护栈相关的寄存器
通过一组(一般是两个)维护当前程序的“调用栈”
栈,也是一块内存,这个内存里保存了这个程序方法调用过程中,一系列的关系(也包含成员变量和方法参数)
ebp 始终指向栈底 esp始终指向栈顶
修改esp的值就可以实现“入栈”“出栈” ——push指令
c.其他的通用寄存器,一般用来存储计算的中间结果
可以把寄存器中的值备份给上下文
一个CPU中的寄存器没多少(不同CPU不一样)
5)虚拟地址空间
操作系统引入“虚拟地址空间”概念。
不是直接分配物理内存,而是分配虚拟内存——对内存的一层抽象
A看到的内存是抽象后的虚拟内存
A操作内存中的某个数据,就需要把操作的虚拟地址告诉系统
系统把虚拟地址翻译成物理地址(有一个类似于hash表这样的映射结构,称为页表),再操作物理地址
操作系统就可以进行检查——看当前这个虚拟地址能否顺利完成翻译
如果给定的虚拟地址是非法的,是一个越界的访问,系统就能及时发现,并且对当前进程进行处理,就不会波及到其他的进程了
6)进程间的通讯方式
a.文件
b.网络(socket)
2. 线程
1)线程定义
线程 Thread 轻量级进程,java不鼓励使用多进程,而是使用多线程
a. 引入多个进程,是为了实现并发编程(基于多核CPU)
进程太重量,效率不高——创建/销毁/调度一个进程,消耗的时间较多
b. 申请资源——操作系统内部有一定的数据结构把空闲的内存管理好,申请内存时,系统会找到一块大小合适的空闲内存,返回给对应的进程
线程不能独立存在,依赖于进程
一个进程可能包含一个或多个线程
一个进程最开始的时候至少有一个线程(主线程,相当于与进程同时创建),这个线程负责完成执行代码的工作
根据需要,可以创建更多线程——并发编程
每个线程可以独立执行一些代码,每个线程也有状态,优先级...(pid相同,内存指针和文件描述符是公用的)——PCB
一个进程可能有一个或多个PCB
2)线程的特点
a. 每个线程可以独立地在CPU上执行
b. 同一个进程的多个线程之间,共用同一块内存空间和文件资源(硬盘)
创建线程不需要重复申请资源,直接复用之前已经分配给进程的资源
c. 当线程数目多了,可能会产生冲突(线程不安全)——同时访问同一个对象
当线程出现异常,若无妥善处理(catch...),容易使进程崩溃
一个系统中,可以有很对进程,每个进程都有自己的资源
一个进程中,可以有很多线程,每个线程都能独立调度,共享内存和硬盘资源
3)线程和进程的区别
1. 进程包含线程,一个进程可以由一个或多个线程
2. 都是用来实现并发编程,线程比进程更轻量,高效
3. 同一进程的线程之间,共用一份资源(内存+硬盘),省去了申请资源的开销
4. 进程和进程之间具有独立性;同一进程的线程之间可能会相互印象
5. 进程是资源申请的基本单位;线程是调度执行的基本单位
进程中包含线程 ——> 一个进程有多个PCB表示 ——> 每个PCB用来表示一个进程 ——> 每个线程有自己的状态,上下文,优先级,记账信息 ——> 每个线程都就可以独立地在CPU上调度执行 ——> 这些PCB共用了同样的内存指针和文件描述符 ——> 创建线程(PCB)不需要重新申请空间 ——> 创建/销毁的效率更高了
4)) Java 的线程 和 操作系统线程 的关系
线程是操作系统中的概念. 操作系统内核实现了线程这样的机制, 并且对用户层提供了一些 API 供用户使用(例如 Linux 的 pthread 库)
Java 标准库中 Thread 类可以视为是对操作系统提供的 API 进行了进一步的抽象和封装.