前言
最近在b站看到了马士兵老师的 netty 课程,虽然放出来的大概约等于试看版,但是干货还是挺多的,一直深入到了计算机的组成原理,从软件到硬件把原理给你讲明白了,结合《深入理解计算机系统》这本书来学习效果更佳。
这是这个课程的第一部分:讲了计算机底层发生中断时的过程,以及为什么 IO 的效率比较低(这个只给了结论)
计算机组成:
硬件:
- CPU
- 内存
- 硬盘
- 网卡
- ...
软件:
- 操作系统
- 其他应用程序
软件是计算机的灵魂,而在 Linux 操作系统中,你编写的程序被看做一个「文件」。
【开机之后计算机中的第一个程序是什么?】
开机之后会将 kernel
加载到内存中 —— 第一个程序是 Linux 内核。
内核的作用:
- 暴露系统调用
system call
, 让其他应用程序调用硬件功能。
调用计算机底层硬件,其他应用程序如果需要使用硬件,跟「内核
」进行交互就可以,不需要程序自己编写操控硬件的代码。
但是由于「安全
」的需求,并没有上面说的这么简单。
- 如果放任应用程序直接调用底层内核,那么可能导致某块内存区域被恶意软件修改,引起安全隐患。
于是有了「对策
」:当 CPU 将 内核加载到内存中之后,会从「实模式
」转换为 「保护模式
」。
概念:
-
实模式 :实际物理内存地址模式。
-
保护模式 :将
内存地址
进行了一次转换,隐藏真实内存地址,用户无法访问真实内存地址。
这里画了个图,也就是将内存
一分为二,内核可以访问真实的内存地址,其他应用程序无法访问,只能访问被转换后的内存地址。
前后矛盾 :
之前说 内核的意义就是分层解耦,让程序可以调用操作系统,从而操作底层硬件。
但是由于安全的原因,保护模式程序又无法获取真实的内存地址,这就产生了「矛盾
」 —— 「内核既要保证 安全 ,又要让应用程序能够调用系统功能」。
怎样解决?
引入另外一个引申概念:「中断
」,这里的中断其实就是 「CPU时间片」。
下面这张图包含了:
什么是中断 :
- 物理层面: 发生晶振,每次晶振都是给 CPU 一个电信号,让 CPU 知道要中断了
- 逻辑层面: 中断发生之后应用程序如何进行切换 —— 操作系统内核中包含了切换的逻辑
中断发生之后 CPU 怎么知道要做什么?
内核中维护了一个「中断向量表」,以键值对的形式存在,发生中断之后 CPU 根据中断号找到对应的回调函数,然后执行对应的操作。
谁可以发起中断?
【能发起中断的软硬件很多】
- IO设备:键盘,鼠标
- 网卡
- 硬盘
- 应用程序
【中断概念示意图:】
Java 中调用 system.out.println("hello world") 底层发生了什么
【系统调用示意图:】
【系统调用文字描述】:
Java
程序想要输出一条语句 "hello world
" ,但是要把这条语句打印到显示器上,需要 「内核
」 来完成,于是,Java API
调用了系统 API
。- 内核接收到了这个调用,然后将一个函数写入 「
CPU寄存器
」 , 这个函数就是write("hello world")
,但是仅仅有一个函数是无法完成切换的,还要有相对应的 「CPU指令
」,于是内核紧跟着给了 CPU 一个指令 「int0x80
」,这里的int
代表的就是 「CPU指令
」 CPU
去 「内核」 中的 「中断向量表」中寻找0x80
代表的 「回调函数
」,并且执行该函数- 这个例子中的 「
回调函数
」 的内容就是进行 「切换」,暂存当前进程的信息,从「用户态
」 切换到 「内核态
」,切换完成之后,由kernel
内核完成 执行write("hello world")
这个函数 - 调用完成,
hello world
被打印到了 控制台上。
结论
「IO」 一定涉及系统调用,而「系统调用
」的成本相对较高。
问题
-
为什么成本高?
-
是否存在与系统调用相关联的概念?
答案
- 因为
IO
涉及到了硬件调用,而硬件调用就涉及到了 「内核」,涉及到内核之后就存在着从 「用户态
」 转为 「内核态
」 的转换与切换,所以成本较高。 - 与 「
系统调用
」 相对应的是 「函数调用
」 ,函数调用指的是在 同一个应用程序中,给到 CPU 一个指令,因为是同一块内存区域,所以不会涉及到内核,可以直接执行。
Q.E.D.
Comments | 0 条评论