早期的计算机,好比运行DOS的IBM PC或者早期的大型机IBM 709, 是没有内核这种概念的, 从软盘、磁带或者纸带加载程序以后程序就能够彻底的控制整个计算机硬件.linux
后来随着历史的发展,出现了一种需求:多我的同时访问一台计算机。为了知足这种需求,出现了现代化的操做系统:分时操做系统。早期流行而且至今产生深远影响的分时操做系统就是Unix。git
这里有几个概念:github
微软也编写了关于虚拟内存的文档: 虚拟地址空间 - Windows driverswindows
4. 特权级与系统调用:许多指令,好比设置页表或者中断向量表的地址,修改CPU的工做模式,都属于特权指令,普通程序禁止执行, CPU使用特权级(又称RING或者异常级别)表示当前正在执行的进程的权限.低权限的程序执行高权限的指令或者访问高权限的内存会触发异常.浏览器
权限级最低的,就是普通的用户态进程(EL0) , 最高的,就是操做系统内核(EL1)安全
其实上图有4层结构,EL2用于虚拟机管理程序,而虚拟机能够执行多个操做系统。(实际上手机不使用此功能),最高的EL3就是CPU的固件。而右侧的部分为可信计算环境,处理诸如加密,指纹等机密信息. 据华为PPT,当前鸿蒙微内核实际上运行于此(不是电视上那个鸿蒙OS).网络
为了容许用户态进程访问高权限资源,操做系统内核提供了经过陷阱调用系统功能的入口,即系统调用. 打开文件(open)写文件(write)都是系统调用。数据结构
能够看到,用户态程序和内核之间存在自然的界限.架构
须要注意的一点是,操做系统会附带一些重要的系统进程,好比Linux的init, 可是这些也只是用户态程序, 操做系统内核也许对其有特殊的标记,可是对于CPU来讲和其余进程没有区别.并发
下面讨论一下Windows,比较使用量很广.
上图中,Windows的内核主要部分即ntoskrnl.exe,也称内核映像. 处于商业的考虑,Windows实际上有4种不一样的内核映像.而smss.exe则是第一个用户态程序.
Windows的内核代号为NT, 其架构图为
这个图很复杂,咱们也没必要搞懂...紫色的内核模式和蓝色的用户模式区分很明显.
其中一个很小的kernel mode drivers(内核模式驱动程序),看起来不起眼,其实是NT内核中体积最大的部分.
数不胜数的硬件,经过这种方式被Windows兼容,经过Windows update和即插即用(PnP)机制,作到了最佳的体验。
Windows是一个闭源操做系统,也基本上无论驱动的编写(除去CPU和那些已经标准化的协议)。硬件制造商(或者毒瘤软件)想要编写驱动程序,须要安装Visual studio,Windows SDK和HDK(硬件开发工具),打开MSDN(如今更名了),照着文档里的函数接口写。
编写完的驱动包括.sys驱动程序和inf描述文件,交给微软进行兼容性测试,随后就能得到签名并发布了。微软同时保证,这些接口不会发生变化,即保证了二进制兼容性(编译好的驱动即便系统更新也不会出现问题).
固然也有例外(逃
上图都是驱动的问题....
前面提到了虚拟内存的概念,Windows大多数驱动都是内核模式驱动程序(.sys),少数是用户模式驱动程序(.dll), 微软在这里提到了区别: 用户模式和内核模式 - Windows drivers
运行 Windows 的计算机中的处理器有两个不一样模式:用户模式 和内核模式 。 根据处理器上运行的代码的类型,处理器在两个模式之间切换。 应用程序在用户模式下运行,核心操做系统组件在内核模式下运行。 虽然许多驱动程序之内核模式运行,但某些驱动程序可能以用户模式运行。
启动用户模式应用程序时,Windows 会为该应用程序建立进程 。 进程为应用程序提供专用的“虚拟地址空间” 和专用的“句柄表” 。 因为应用程序的虚拟地址空间为专用空间,所以一个应用程序没法更改属于其余应用程序的数据。 每一个应用程序都隔离运行,若是一个应用程序发生故障,则故障仅局限于该应用程序。 其余应用程序和操做系统不会受该故障的影响。
除了专用以外,用户模式应用程序的虚拟地址空间也受到限制。 在用户模式下运行的处理器没法访问为操做系统保留的虚拟地址。 限制用户模式应用程序的虚拟地址空间可防止应用程序更改以及可能损坏关键的操做系统数据。
在内核模式下运行的全部代码都共享单个虚拟地址空间。 这意味着内核模式驱动程序不会与其余驱动程序和操做系统自己隔离。 若是内核模式驱动程序意外写入错误的虚拟地址,则属于操做系统或其余驱动程序的数据可能会受到安全威胁。 若是内核模式驱动程序发生故障,整个操做系统就会发生故障。
所谓的发生故障,就是蓝屏. 因为显卡驱动故障的几率很高, 使用用户模式驱动程序会减小蓝屏, 微软也使用特殊的Windows显示驱动框架(WDDM)强制要求显卡驱动部分实如今用户模式下. 在Windows10中, Win+Ctrl+Shift+B快捷键便可重启显卡驱动和图像堆栈, 而放在XP的时代,显卡驱动出现故障就会直接蓝屏.
正是由于Windows支持用户模式驱动程序,所以Windows属于混合内核.
咱们再讨论一下GNU/Linux这边的状况, Linux仅仅是操做系统的内核, Busybox组成的initrd, Systemd, GNU libC,Xorg等等用户态进程组成了一个系统.
Linux的“官方网站”为The Linux Kernel Archives
Github上有Linus Torvalds先生维护的Linux分支的镜像torvalds/linux
广告一下我维护的Linux 0.11的代码, 能够方便的在现代系统上研究12101111/Linux-0.11
Linux是一个Unix-like(即没有Unix商标,可是兼容Unix的POSIX API)的内核, 当年仍是大学生的Linus买到了一台低配版386兼容机,不想用落后的DOS系统,而想用Unix.但当时的Unix是一款昂贵的商业产品.
当时计算机教学使用的系统叫Minix,是Andrew Stuart "Andy" Tanenbaum编写的一个流行微内核Unix-like系统,这个系统随者做者的书《操做系统:设计与实现》分发,可是不是自由软件.
当时另外一个流行的Intel 386平台的Unix系统为FreeBSD,不过当时此项目正在和AT&T公司(即Unix全部者)打官司, 并且不兼容没有387协处理器的机器, 所以Linus本身照着Andy的课本和Intel手册写出了Linux, 使用GPLv2协议成为自由软件, 并经过互联网开始协做开发.
Linus还就微内核和宏内核的问题和Andy 论战过: 塔能鲍姆-托瓦兹辩论
Linux不只如标准的宏内核同样全部功能位于内核空间中, 并且因为GPLv2, 全部驱动程序的源代码(理论上)都须要开源, 所以Linux不多有下载驱动这一操做, 由于全部驱动都是开源的, Linux源码里已经有了.后来Linux具备了模块系统,驱动能够按需加载, 不须要为了删除不须要的驱动而从新编译内核了.
和Windows不一样的是, Linux的驱动接口常常变更, 所以即便编译好的内核模块(.ko文件)常常升级Linux版本就不能用了, 所以驱动只能随着主线更新, 若是没有人维护就会从主线中剔除.
部分arm芯片厂商的内核版本就很老,就是由于它们既不想把源代码交给社区维护,也追不上社区升级版本号的速度.
因为Linux并无用户模式驱动的功能,所以编写差劲的开源nVidia驱动(虽然开发者已经很努力了)和一样编写差劲却又不开源的nVidia闭源驱动常常形成kernel panic(等价于Windows蓝屏)
那么微内核究竟是什么一种东西呢.
微内核将传统宏内核中的驱动程序,甚至包括许多功能,好比文件系统, 网络, GUI等等, 都变成用户态的进程(服务),而内核中只保留最重要的功能: 进程管理,内存管理, 进程间通讯, 以及硬件抽象层(HAL).
而Linux, 即便是驱动源码独立于内核仓库,具备明显的模块化特征,具备稳定的驱动接口,也不能是微内核,惟一的标准就是用户态的驱动程序和系统服务
Google的Fuchsia OS的 Zircon内核也是微内核, 详见: 聞其詳:许中兴博士演讲:Fuchsia OS 简介完整实录及幻灯片下载
从理论上将, 微内核将驱动独立于内核空间, 大大下降了驱动故障的风险, 提高了安全性, 热更新驱动热重启驱动也变得可能.
微内核的代码大大的简化(少去多半的驱动代码), 维护成本降低, 只要提供一个合理的驱动接口便可.
但有一个重要的问题: 这样作的效率会下降.
从开销上计算, 函数调用<<系统调用<进程间通讯(IPC)
函数调用(同一特权级): 压栈,跳转目标地址,弹栈, 返回栈中的返回地址. 函数调用能很好的被CPU的流水线感知.
系统调用(不一样特权级): 清空流水线,TLB刷新,保留现场(用户态程序各个寄存器的值)可能的进程调度,转向执行其余进程,复制用户空间里的数据到内核空间(字符串等系统调用参数),处理,将结果复制回用户空间,恢复现场,清空流水线,TLB刷新
进程间通讯: 屡次系统调用,可能须要屡次复制消息体,可能发生进程调度。
最大的问题,就在IPC的性能上.
根据目前的硬件,系统调用时,若是要传递寄存器存不下的东西,好比内存中的一段字符串,那么内核是不能直接读取用户态的内存的(页表不一样),内核须要先把用户提供的内存地址翻译到内核中的地址,而后内核分配一块内核使用的内存,将其复制进去,而后处理. 返回数据也相似.
再好比内核处理网卡的数据,并将此数据传给浏览器.
宏内核的过程: 网卡发来中断, CPU执行网卡驱动设置的处理程序,要求网卡使用DMA将数据存到内核中,而后返回.DMA完成后,再次中断,系统的IP子系统分析已经存储的IP数据报,肯定是TCP数据,根据端口号唤醒正在等待recv()的浏览器,将数据复制到recv参数里的缓冲区. 整个过程只发生一次数据的复制,两次中断.
那么微内核怎么办呢, 这里以最傻的微内核为例: 网卡发来中断, CPU执行HAL设置的处理程序, HAL肯定这个中断应该被/bin/e1000d这个进程处理,因而唤醒/bin/e1000d, 把网卡发来的中断信息发给/bin/e1000d, e1000d将“写网卡内存映射的寄存器.....要求网卡进行DMA..."这种消息发给内核,内核收到以后执行,而后DMA结束以后把内存数据复制给e1000d,e1000d处理以后使用IPC把IP数据报发送给/bin/ipd,ipd处理后把TCP数据发送给浏览器。
实际上发生了屡次数据的复制,页表的修改,屡次进程调度,效率远低于宏内核。
经过简化代码,虽然能提升IPC的性能,但远不如函数调用快。实际上上述例子中TCP/IP协议栈即便是微内核也不该该放在用户态中。
而进一步的提速,可能须要对现有的内存保护模型进行修改,避免过多的复制, 而硬件的修改,则是比软件的革新难数倍.