计算机网络
- 冯诺依曼体系
- 进程线程
- 内核和虚拟内存
- os管理线程
冯诺依曼体系
计算机五大组成:输入设备、输出设备、控制器、运算器、存储器
进程线程
这些应用都是进程
进程相当于一个菜谱,读取到内存中去使用。
电脑一时间能运行很多进程。
进程中为什么要有线程:
因为一个非常简单的操作,比如在第四行和第三行之间打个回车这么简单的操作,同时是有多个操作在一起进行的
1.接收键盘上的事件
2.重新渲染页面之后显示出来
3.随时往硬盘中存储
这三个操作发生在同一个文档中,也就是同一个进程里面,最好是可以同时发生的。
线程是并行的最小单位,需要并行进行执行。
CPU是轮流过来执行的,就相当于是,一个妈妈,家里两个孩子,妈妈轮流往返于两个房间,给两个孩子讲题。
孩子等待讲题这个过程,就是就绪状态,因为我们就绪好了,等待你过来讲题,讲题这个过程就是运行状态。
线程空转状态:
线程向硬盘发送访问请求,相当于等待权限,这个时候线程就进入阻塞状态,相当于我在等待写的权限呢,线程阻塞住了。
等访问结果回来,我再顺利变成非阻塞状态,就会就绪状态。
有个跟确切的说法,一个人同时接到三个项目,三个项目的人都在催你,让你赶紧干,要进度。你只能干一会A,交个结果,干一会B交个结果,干一会C再去交个结果。项目等待被干的状态,就是就绪状态,被干时候就是运行状态,项目那边跑过去申请权限,等待权限回来的那个状态就是阻塞状态,由于手续不齐全原因,项目推动被阻塞住了,那就等手续下来之后再推动项目。
内核和虚拟内存
电脑或手机开机以后,上电跑启动代码,运行OS内核,内核里也有线程,这个我们把它叫做内核态。
内核启动以后, 内核将物理内存管理起来。内核提供虚拟内存管理机制给每个进程(应用程序App)内存服务。
它的思路是什么呢?每个进程(应用App) 都有自己的虚拟内存空间,注意这里的空间只是一个数字空间,没有划分实际的物理内存。
这样做的好处是多个进程(应用App)内存都是独立的相互不影响,物理内存只有一个,多个进程(应用App)不会因为直接使用物理内存而冲突
那么OS是如何管理物理内存的呢?进程(应用App)需要内存的时候,OS分配一块虚拟内存(起点—终点),然后OS在从自己管理的物理内存里面分配出来物理内存页,然后通过一个MMU的单元**,将分配的虚拟内存与物理内存页映射起来,这样,读写虚拟内存地址最终通过映射来使用物理内存地址,**这样每个进程之间的内存是独立的,安全的。每个进程会把虚拟内存空间分成4个段(代码段, 数据端,堆,栈)
相当于先给一个分配方案,在分配方案商量妥当之后,再按照虚拟方案进行执行,在方案都没商量妥的时候就执行,很容易出现问题。
例如"在桌面上双击打开一个App", 桌面App程序会调用OS的系统调用接口fork,让OS 创建一个进程出来,OS为你准备好进程的结构体对象,将这个App的文件(xxx.exe, 存放编译好的代码指令)加载到进程的代码段,同时OS会为你创建一个线程(main thread), 在代码里面,还可以调用OS的接口,来创建多个线程,这样OS就可以调度这些线程执行了。
虚拟内存空间是进程的概念,那么线程如何使用的呢?各线程使用共享进程的代码段,数据段,堆,每个线程在进程的栈空间创建一个属于自己的栈空间。
线程们之间像是同事,一同推进一件事,因为需要共享资源和情报。
所以这样就得到一些结论如下:
每个线程共享进程的代码段内存空间,所以我们编写多线程代码的时候,可以在任何线程调用任何函数。
每个线程共享进程的数据段内存空间,所以我们编写多线程代码的时候,可以在任何线程访问全局变量。
每个线程共享进程的堆,所以我们编写多线程代码的时候,可以在一个线程访问另外一个线程new/malloc出来的内存对象。
每个线程都有自己的栈的空间,所以可以独立调用执行函数(参数,局部变量,函数跳转)相互之间不受影响。
os管理线程
OS会根据线程的优先级分配每次调度最多执行的时间片,这个时间一到,无论如何都要重新调度一次线程(也许还是调度到这个线程,这个不重要)。
除了时间片以外,线程会等待某些条件(磁盘读取文件,网卡发送完数据,线程休眠, 等待用户操作)这样也会把这个线程挂起,OS会重新找一个新的线程继续执行,只到挂起的这个线程的条件满足了,重新把这个线程放到可调度队列里面,这个线程又有机会被OS调度CPU核心来执行。
当我们打开电脑的任务管理器,**你会发现很多线程的CPU占有率为0%, 说明这些线程都由于某些条件而挂起了,**没有被OS调度。
每个线程“随时随地”都可能被OS中断执行,并调度到其它的线程执行。
OS是如何保证一个线程在调度出去后,再重新调度回来能继续之前的数据状态来执行呢?
也就是线程中,PC计数器(代表上次执行的位置)和栈是独立的。
这不得不让我想起一个形象的比喻,就是法官的案卷。
好几个案子,同时先后开庭,每次只推进一点,然后把推进的东西记到文书上,一个案子开庭之后,法官先去看文书了解上次判到哪里(相当于PC计数器),然后根据上次判决的文字记录(数据和代码),继续这次推进。
线程和进程,就像一个项目中的三个任务,三个任务都有人催要进度,A,B,C任务之间还存在相互依赖关系,比如A任务依赖B任务推进到X位置才能继续进行,B任务依赖C任务推荐都X位置才能进行,干工作的只有一个人,所以他必须推进一下A工作,然后就去推进B工作,然后再去推进一下C工作,轮流推进。三个工作共同属于一个项目,而内存空间是根据项目进行分配的,所以线程地址相同,共享大量数据,比如代码端,全局变量和堆(也就是对象)。线程之间是强调协作,共同完成动作的。
比如我们前面说的那个,读取键盘数据,渲染页面和写入硬盘,这些操作需要一同进行,一同推进,同协作完成任务。