性能文章>白话硬核技术之什么是用户态和内核态>

白话硬核技术之什么是用户态和内核态原创

2年前
482035

闪电侠的博客,开启你的硬核之旅!

白话硬核技术目录

  • 白话硬核技术系列开篇:这么多技术我们该怎么学?

  • 什么是用户态和内核态?

  • 什么是系统调用?

  • 为什么不同进程访问相同的内存地址不会产生冲突?

  • 写文件为什么一定要 flush?

  • 为什么文件不使用要关闭?

  • mmap 为什么这么快?

  • 什么是工作目录?

  • 进程睡眠到底意味着什么意思?

  • 为什么 Linux 下的软件无法在 Mac 下运行?

  • 进程上下文切换为什么这么慢?

  • 程序是如何变成进程的?

  • ...



00

谈起内核态和用户态,你脑海中一般会想起如下一幅图

 

 

可能会知道,我们的用户进程运行在用户态,而内核进程运行在内核态,但是对于操作系统为什么要这么设计,用户态和内核态到底更深一点的本质到底意味着什么,可能会比较模糊,今天,我就以白话文来向你解惑。

 

01 

为什么要区分用户态和内核态?

 

首先,我们先来看看,如果只有内核态没有用户态,会发生什么?

 

 

如上图,如果整个操作系统没有用户态,只有内核态,所有进程都运行在内核态,这会带来几个问题:

  • 如果某个进程的代码写得有问题,运行了一段时间奔溃了,奔溃的同时可能会导致其他进程也奔溃

  • 所有进程将可以读写任意一个进程的内存的数据,安全性方面将受到极大的挑战

 

举几个具体的例子,如果你的服务器只有内核态,这会导致:

  • 你在服务器上运行一段 shell 脚本,居然把一直在运行的 Web 服务器给搞奔溃了

  • 你的服务器运行着一个银行卡相关的进程,通过一分析,你可以写一个程序拿到所有内存中的银行卡账号和密码

  • 你的服务器有一个进程在吭哧吭哧读数据,你甚至可以运行另外一个程序,让这个写数据过程强行退出,这多么危险啊

     

为了防止不同进程之间相互影响,CPU 根据特权等级将进程所处的权限状态分为用户态和内核态:

  • 一般我们自己通过手工 run 起来的进程都可以叫做用户进程,大多数情况处在在用户态

  • 处在用户态的进程自己挂了也不会影响其他的进程

  • 内核态的进程基本上干嘛就干嘛,所有的内核进程都处在内核态

  • 处在用户态的进程无法执行一些危险的指令,比如:向某个 IO 设备发送读指令

 

你可能会说,这不对吧,我用户态的进程仍然可以向一个设备写数据呀,你说的没错。

 

那这又是怎么实现的呢?

 

在有用户态和内核态区分的前提下,如果用户进程想要执行一些比较危险的 CPU 指令,需要先将自己转换到内核态,转换的过程中,操作系统会进行一些安全校验,比如说你这个用户进程有没有用户授权啊,会不会影响到别的进程啊什么的,接着,由操作系统内核来统一调度执行你的这个危险指令,和你自己在用户态随意执行这个指令是完全不一样的,这个状态转换过程也就是我们下一篇文章要讲的“系统调用”。

 

所以,在某个时刻,我们的操作系统中,进程所处的整体状态可能是这样的:

 

 

虽然说,我们看到的逻辑视图是这样的,但是实际上,用户进程运行的是 CPU 指令,这个用户态或内核态准确地说是 CPU 在运行某些指令时所处的状态。

 

举个例子,用户进程想要访问磁盘了,告诉操作系统内核,内核先将这个用户进程从用户态转换到内核态,之后,本质上是说,此时此刻,当前用户进程所在的 CPU 的特权状态是内核态。

 

CPU 除了用户态和内核态还有其他的特权状态,这里就不赘述了。

 

此外,上面这幅图中,我们还可以看到两个规律:

 

  1. 所有的内核进程都运行在内核态

  2. 大多数用户进程都运行在用户态,偶尔会跑到内核态

 

那么,内核进程到底是个啥呀?

 

 

 

02

内核进程和用户进程

内核进程一般是内核创建出来的进程,用来一直常驻在内核空间干一些事情,直到关机。典型的内核进程有:

 

  1. 负责进程调度的进程:比如一个新的用户进程来了,它在合适的时机选择一个合适的 CPU 让这个进程来执行

  2. 负责定时刷磁盘的进程:定时将内存数据写到磁盘上去

  3. 负责 CPU 负载均衡的进程:尽量让每 CPU 忙闲均衡

  4. ...

 

内核进程和我们的用户进程也没什么太大的区别,也有代码,也需要定义一些数据结构,一些算法,也需要一些内存空间来暂存数据啥的。

 

为了保证用户进程不给我们苦逼的内核进程捣乱,比如不要乱访问内核进程的数据,一个隔离性方面的设计就是,将操作系统内存分为两段,一段专门给留给用户进程,一段专门给内核进程用,如下图:

用户进程中所有的指令,都只能访问整体内存中的某一段内存,而牛逼的内核进程中的指令可以访问所有的内存(比如写文件的时候,从用户进程的内存 M1中拷贝数据到内核进程所在的内存 M2,继而刷新的磁盘,这段内核进程所在的内存 M2 就是 page cache)。

 

用户进程再怎么玩,也不会干扰到我们苦逼的内核进程。

 

那么,你可能会要问了,分这么粗的话,用户进程之间不也是会相互影响吗,别急,在后续的《白话硬核技术》系列中会有专门一篇文章来介绍用户进程是如何隔离的,这个牛逼的技术就是神奇的虚拟内存技术,今天我们就点到为止。

 

 

03

总结

 

我们这篇简短的白话科普到这里就告一段落了,最后做一个简单的总结:

  • 用户进程是我们自己手动创建出来的进程,内核进程是操作系统内核创建出来的进程

  • 用户进程大多数情况在用户态,偶尔会通过系统调用陷入内核态,干一些危险的事情

  • 内核进程一直处在内核态,干一些苦逼的事情

  • 操作系统通过划分内存区间的方式让内核进程和用户进程互不影响

 

参考资料

  1. 《现代操作系统原理与实现》陈海波等著

  2. 《深入理解 Linux 内核》陈莉君等译

 

希望这篇文章能给你带来一些收获,如果有一点收获的话,欢迎点赞转发到朋友圈,支持我继续写下去!

 

下一篇将介绍用户进程如何从用户态转化到内核态,尽请期待!

 

,开启你的硬核技术之旅!

 

点赞收藏
闪电侠

嗯,程序员一枚

请先登录,查看3条精彩评论吧
快去登录吧,你将获得
  • 浏览更多精彩评论
  • 和开发者讨论交流,共同进步

为你推荐

服务器之 ECC 内存的工作原理

服务器之 ECC 内存的工作原理

从 Linux 内核角度探秘 JDK MappedByteBuffer

从 Linux 内核角度探秘 JDK MappedByteBuffer

日常Bug排查-连接突然全部关闭

日常Bug排查-连接突然全部关闭

5
3