白话硬核技术之什么是用户态和内核态原创
关注订阅号闪电侠的博客,开启你的硬核之旅!
白话硬核技术目录
-
什么是用户态和内核态?
-
什么是系统调用?
-
为什么不同进程访问相同的内存地址不会产生冲突?
-
写文件为什么一定要 flush?
-
为什么文件不使用要关闭?
-
mmap 为什么这么快?
-
什么是工作目录?
-
进程睡眠到底意味着什么意思?
-
为什么 Linux 下的软件无法在 Mac 下运行?
-
进程上下文切换为什么这么慢?
-
程序是如何变成进程的?
-
...
00
谈起内核态和用户态,你脑海中一般会想起如下一幅图
你可能会知道,我们的用户进程运行在用户态,而内核进程运行在内核态,但是对于操作系统为什么要这么设计,用户态和内核态到底更深一点的本质到底意味着什么,可能会比较模糊,今天,我就以白话文来向你解惑。
01
—
为什么要区分用户态和内核态?
首先,我们先来看看,如果只有内核态没有用户态,会发生什么?
如上图,如果整个操作系统没有用户态,只有内核态,所有进程都运行在内核态,这会带来几个问题:
-
如果某个进程的代码写得有问题,运行了一段时间奔溃了,奔溃的同时可能会导致其他进程也奔溃
-
所有进程将可以读写任意一个进程的内存的数据,安全性方面将受到极大的挑战
举几个具体的例子,如果你的服务器只有内核态,这会导致:
-
你在服务器上运行一段 shell 脚本,居然把一直在运行的 Web 服务器给搞奔溃了
-
你的服务器运行着一个银行卡相关的进程,通过一顿分析,你可以写一个程序拿到所有内存中的银行卡账号和密码
-
你的服务器有一个进程在吭哧吭哧读数据,你甚至可以运行另外一个程序,让这个写数据过程强行退出,这多么危险啊
为了防止不同进程之间相互影响,CPU 根据特权等级将进程所处的权限状态分为用户态和内核态:
-
一般我们自己通过手工 run 起来的进程都可以叫做用户进程,大多数情况处在在用户态
-
处在用户态的进程自己挂了也不会影响其他的进程
-
内核态的进程基本上想干嘛就干嘛,所有的内核进程都处在内核态
-
处在用户态的进程无法执行一些危险的指令,比如:向某个 IO 设备发送读指令
你可能会说,这不对吧,我用户态的进程仍然可以向一个设备写数据呀,你说的没错。
那这又是怎么实现的呢?
在有用户态和内核态区分的前提下,如果用户进程想要执行一些比较危险的 CPU 指令,需要先将自己转换到内核态,转换的过程中,操作系统会进行一些安全校验,比如说你这个用户进程有没有用户授权啊,会不会影响到别的进程啊什么的,接着,由操作系统内核来统一调度执行你的这个危险指令,和你自己在用户态随意执行这个指令是完全不一样的,这个状态转换过程也就是我们下一篇文章要讲的“系统调用”。
所以,在某个时刻,我们的操作系统中,进程所处的整体状态可能是这样的:
虽然说,我们看到的逻辑视图是这样的,但是实际上,用户进程运行的是 CPU 指令,这个用户态或内核态准确地说是 CPU 在运行某些指令时所处的状态。
举个例子,用户进程想要访问磁盘了,告诉操作系统内核,内核先将这个用户进程从用户态转换到内核态,之后,本质上是说,此时此刻,当前用户进程所在的 CPU 的特权状态是内核态。
CPU 除了用户态和内核态还有其他的特权状态,这里就不赘述了。
此外,上面这幅图中,我们还可以看到两个规律:
-
所有的内核进程都运行在内核态
-
大多数用户进程都运行在用户态,偶尔会跑到内核态
那么,内核进程到底是个啥呀?
02
—
内核进程和用户进程
内核进程一般是内核创建出来的进程,用来一直常驻在内核空间干一些事情,直到关机。典型的内核进程有:
-
负责进程调度的进程:比如一个新的用户进程来了,它在合适的时机选择一个合适的 CPU 让这个进程来执行
-
负责定时刷磁盘的进程:定时将内存数据写到磁盘上去
-
负责 CPU 负载均衡的进程:尽量让每颗 CPU 忙闲均衡
-
...
内核进程和我们的用户进程也没什么太大的区别,也有代码,也需要定义一些数据结构,一些算法,也需要一些内存空间来暂存数据啥的。
为了保证用户进程不给我们苦逼的内核进程捣乱,比如不要乱访问内核进程的数据,一个隔离性方面的设计就是,将操作系统内存分为两段,一段专门给留给用户进程,一段专门给内核进程用,如下图:
用户进程中所有的指令,都只能访问整体内存中的某一段内存,而牛逼的内核进程中的指令可以访问所有的内存(比如写文件的时候,从用户进程的内存 M1中拷贝数据到内核进程所在的内存 M2,继而刷新的磁盘,这段内核进程所在的内存 M2 就是 page cache)。
用户进程再怎么玩,也不会干扰到我们苦逼的内核进程。
那么,你可能会要问了,分这么粗的话,用户进程之间不也是会相互影响吗,别急,在后续的《白话硬核技术》系列中会有专门一篇文章来介绍用户进程是如何隔离的,这个牛逼的技术就是神奇的虚拟内存技术,今天我们就点到为止。
03
—
总结
我们这篇简短的白话科普到这里就告一段落了,最后做一个简单的总结:
-
用户进程是我们自己手动创建出来的进程,内核进程是操作系统内核创建出来的进程
-
用户进程大多数情况在用户态,偶尔会通过系统调用陷入内核态,干一些危险的事情
-
内核进程一直处在内核态,干一些苦逼的事情
-
操作系统通过划分内存区间的方式让内核进程和用户进程互不影响
参考资料
-
《现代操作系统原理与实现》陈海波等著
-
《深入理解 Linux 内核》陈莉君等译
希望这篇文章能给你带来一些收获,如果有一点收获的话,欢迎点赞转发到朋友圈,支持我继续写下去!
下一篇将介绍用户进程如何从用户态转化到内核态,尽请期待!
关注订阅号闪电侠的博客,开启你的硬核技术之旅!