站内链接:

内核态,用户态,内核空间,用户空间

内核空间

地址范围:3G-4G, 0XC000 0000 ~ 0XFFFF FFFF, 这是一个虚拟地址哦,大端和小端,实际映射到物理内存低地址, 在 32 位机上面,所有的进程内都是 4G 的虚拟地址,见程序员自我修养)

存放对象:内核代码,内核数据

用户空间

地址范围:0-3G

存放对象:用户代码,用户数据

附加:空间是一种内存的概念哦!

内核态

执行内核代码以及获取内核数据时所处的状态

用户态

在用户空间中进行操作时所处的状态称为用户态

内核态,用户态分级的意义

  • 限权:限制不同程序的访问能力,防止非法获取其他程序的数据和外部设备的数据
  • 内核态指令:访问内存中所有数据,外围设备,网卡(管控中心,CPU 资源-top 命令中的%sy)
  • 用户态指令:只能访问自己的进程空间中的内存地址(CPU 资源-top 命令中的%us)

系统优化->产生管控中心概念->管控本身产生安全问题->限权

用户态切换,进程上下文切换

用户态之间的切换

用户进程通过”系统调用”进入内核态,具有一定的性能开销;

什么是上下文

context 就是当前 APP, 当前子程序, 当前子进程, 当前线程运行所依赖的基本配置, 基本数据等信息. 在不同的框架, 不同的语言生态中, 上下文还有各种细化的专业名词和分类, 例如 Javascript 环境中执行上下文的分类:

  • 全局执行上下文: 任何不在函数内部的代码都在全局上下文中, 比如 window 对象, 全局 this 指针
  • 函数执行上下文: 实际上就上函数调用栈
  • Eval 函数执行上下文

执行上下文在几乎所有语言中都存在, 一个执行上下文实际上就是一个执行调用栈, Execution Context Stack, 大部分情况下执行上下文中的数据和配置包含三个阶段:

  • create: 进行初始化操作, 创建变量环境, 词法环境, 面向对象中 this 的指向等等.
  • execute: 变量赋值, 代码执行
  • gc-recovery: 出栈或者数据回收等

这里注意执行上下文和作用域的区别, 一般来说执行上下文的初始化会伴随着作用域的定义, 环境的初始化等, 执行上下文是一种更高层次的概念.

上下文切换

一个逻辑 CPU(并非物理 CPU)在同一时间运行一个线程,因为任务调度原因导致某一个线程一直不断的在多个 CPU 之间跳转,重复完成下面的任务:

  • 软中断中止当前进程并保存 POS 值
  • 将 CPU 中的各种 Register 值 PUSH 到进程私有内核栈中
  • 在等待了一万年的时间之后,调度器再从私有栈中获取保存的数据到 Register 中,然后重新开干吧,向着社会主义!

线程,轻量级进程

类型

线程: 一个有线程 ID, 指令指针(PC), 寄存器集合, 堆栈组成.

内核线程:运行在内核态,不受用户态上下文的影响

用户线程:

1
2
3
说明: 运行在用户态,在用户空间中创建,不依赖系统核心,各个用户线程在当前所属进程内进行资源竞争.
优点:切换开销小,速度快
缺点:同一个进程只能有一个线程在运行(如果单核,那就能够容忍),一个线程的阻塞导致整个进程的阻塞

轻量级进程

1
2
3
说明: 一切都是为了弥补父辈的债,每一个轻量级进程对应一个调度对象(内核线程,可能多个).
优点:没有优点,不是坏蛋!
缺点:创建时系统调用,数目受内核线程数目的限制,使用clone()来创建

开销: 用户线程 lt 轻量级进程 lt 内核线程

内核线程和用户线程关系

都是从用户线程角度开眼看世界哦.

一对一模型:

1
2
3
说明: 每一个用户线程被绑定到一个单一的内核线程上
例子: Linux中clone创建线程(轻量级进程)
缺点:一个字,钱(开销)!一个字,少(数量限定)!

混合线程模型: 用户线程和内核线程的交叉,喂,大家各管各的,然后有货就交易,没货滚蛋!

多对一模型:多个用户线程映射到一个内核线程,线程之间的切换在用户态完成;

多对多模型:多个用户线程映射到几个内核线程中,自由的市场,自由的世界,自由的选择,自由的呼吸,

那么问题来了:

  • 是否存在上下文切换的开销?一个 CPU 核心只跑一个上下文线程吧!
  • 资源消耗的开销?(内存同步开销,切换开销,创建和消亡的开销,调度开销)
  • 关键一点,你胜任多线程编写吗?各种 BUG

总结

在多核处理器中,一对一模型是最快的方式,当然,我们暂时抛弃先天硬伤!

参考