在实模式下,程序可以直接访问物理内存,并且有权访问所有的硬件设备,包括所有的输入/输出端口和设备控制器。实模式可以类比为一个没有围墙的小镇,所有的房子(硬件设备)都是开放的,任何人(程序)都可以随意进出。
解决问题:实模式极大地简化了早期计算机系统的设计和程序的编写。但是,由于没有任何防护措施,程序可能会意外地(或故意地)修改其他程序的数据,或者错误地操作硬件设备。
保护模式可以类比为一个设有安全系统的城市,每一座建筑(硬件设备)都有自己的访问权限,而这些权限只能由市政府(操作系统)控制和分配。
例如,如果一个程序试图修改其他程序的数据,操作系统就可以阻止这种操作并返回一个错误。
使用场景:实模式通常用于引导程序和嵌入式系统,而保护模式则用于现代的操作系统,例如Windows,Linux和macOS。
内核态(Kernel mode)是指操作系统在运行时的一种特殊工作模式,在这个模式下,操作系统可以访问所有的硬件资源,并执行所有的指令。在这个模式下,操作系统有着最高的权限,可以对系统进行完全的控制。
相比之下,用户态(User mode)是指应用程序在运行时的一种工作模式。在这个模式下,应用程序只能访问被允许的资源,并执行受到限制的指令集。
内核态适用于需要访问和控制系统底层资源的任务,如操作系统内核、驱动程序、虚拟机监控器、安全软件等,而用户态则适用于大部分应用程序和库文件、Shell脚本等。
微内核(Microkernel)是一种操作系统设计理念,其基本思想是将内核的功能最小化,只保留最基本的系统管理功能,比如线程调度,内存管理和基本的进程间通信机制。其他的传统内核服务,如设备驱动,文件系统,网络协议等,被实现为在用户态运行的服务。
汇编:汇编语言是一种低级编程语言,与特定的计算机硬件架构紧密相连。其语法简单直接,几乎是一一对应的关系。它直接表达计算机硬件的操作和管理机制,因此它能够直接操控硬件,比如CPU的寄存器,内存地址等。汇编语言程序可以被汇编器转换为机器语言代码,这些代码可以直接在硬件上执行。
CPU架构:CPU架构定义了CPU的组织和功能,以及它如何处理数据和执行指令。不同的CPU架构有不同的指令集——即它们可以理解和执行的机器语言指令。汇编语言的设计通常依赖于特定的CPU架构,因为汇编语言的指令是CPU架构的直接反映。
硬编码:硬编码是指在程序中直接写入可能需要更改的值,而不是让这些值在运行时被确定或者通过配置文件设定。硬编码的值可以是任何类型的数据,比如一个数值,一个字符串,一个内存地址等。在某种意义上,汇编语言的代码可以看作是硬编码的,因为它们直接描述了硬件如何操作数据和执行指令。
汇编:汇编语言就像是给工厂工人的详细操作指南,比如:首先把零件A放到位置B,然后用工具C固定它,接着把零件D和E连接起来等等。这些操作都是非常直接和具体的,完全依赖于工厂的具体情况(也就是计算机硬件)。
CPU架构:CPU架构就像工厂的布局和机器的设计。比如,有多少生产线,每条生产线有什么样的设备,这些设备如何排列等。工人的操作指南(汇编语言)必须完全符合工厂的布局和机器的设计,否则他们就无法完成工作。
硬编码:硬编码就像是工人的操作指南中的一些固定值。比如,工人可能被告知要把零件A放到距离墙壁1米的位置。这个1米就是硬编码的值,它是直接写在操作指南中的,而不是让工人根据实际情况去测量。这样做可以简化操作,但也降低了灵活性。如果工厂的布局发生变化,比如墙壁被移动了,那么操作指南就需要被更新。
这四种语言就像是四种不同等级的厨房,它们提供了不同级别的抽象和控制。更高级的语言(如Java)提供了更高级别的抽象,使编程变得更简单,但牺牲了一些直接控制硬件的能力。而更低级的语言(如汇编和C)则提供了更直接的硬件控制能力,但编程就会更复杂,更容易出错。
寄存器就像是你手边的小容器,它非常快速且容量很小,可以立即访问并存储一些最经常使用的数据
CPU缓存比寄存器大但仍然足够快速,可以存储更多的数据并且能够快速地读取和写入
内存比前两者慢,但容量更大且能够长时间保存数据
通用寄存器就像是你手边的小碗,它可以暂时存放一些需要处理和临时保存的食材,如鸡蛋、豆腐等。虽然它容量小,但非常快速,可以迅速地保存和处理数据。通用寄存器一般用于临时存储计算结果、数据传输、地址计算等操作
控制寄存器则相当于你准备菜肴时使用的中等容量盘子,在其中可以暂时存放一些需要特殊处理或者调整的食材,如切好的蔬菜、糖盐等。这些食材需要在后续的步骤中进行特殊处理,因此需要单独存放。 CR0寄存器控制着保护模式、分页机制、缓存等特性;CR2和CR3寄存器控制着页表的映射和切换等操作
状态寄存器则类似于你准备菜肴时使用的大容量盘子,在其中可以存放已经处理好的所有食材。同时,它也记录了你当前做菜的状态和进展,如是否完成、是否成功等。这些信息对于你来说很重要,因为它们会影响你接下来的行动和决策。 状态寄存器记录了CPU当前的运行状态,包括标志位、错误码等信息。
系统执行流一般包含以下几个阶段:
1 就绪状态:进程已经分配到了必要的资源(如内存、CPU等),并等待操作系统进行调度,使其进入运行状态。
2 运行状态:进程已经获得了CPU的使用权,正在执行指令,这个阶段也称为CPU执行阶段。
3 阻塞状态:当进程需要等待某些事件发生时,比如输入输出完成、等待同步信号等,会进入阻塞状态,并释放CPU的使用权。当需要的事件发生后,进程从阻塞状态重新回到就绪状态。
4 终止状态:进程执行结束,释放所有资源并退出系统。
需要注意的是,系统执行流并不是线性的,即进程不是按照预定的顺序依次执行下去的。由于操作系统的调度和切换,一个进程在执行的过程中可能会被暂停、重新调度、恢复等。这种非线性的执行流可以提高系统的并行度和效率,使得多个进程可以在相同的时间内完成更多的任务,提高系统的吞吐量。
ROP技术(Return Oriented Programming)是一种利用程序中的现有代码段构造攻击代码的方法,能够破坏系统正常的执行流。其基本思想是通过操纵栈和调用返回地址等方式,将已有代码段组合成新的攻击代码,绕过系统的安全机制,实现攻击目标。
计算机就像一辆汽车,而CMOS、BIOS、UEFI、MBR、GPT和GRUB则分别相当于这辆汽车的引擎、电子控制单元(ECU)、变速器、排气管、车轮和启动器。每个组件都承担着不同的任务,但是协同工作,使得整个系统能够高效地运转。
CMOS相当于引擎中的电路板,存储了基本的设置信息,在操作系统启动前被加载。
BIOS和UEFI相当于汽车的ECU,负责初始化硬件和设置参数。BIOS是旧的标准,而UEFI是新的标准,具有更快的速度、更好的安全性和更大的灵活性。
MBR和GPT相当于变速器,用于指示计算机从哪个分区启动。MBR只能存储4个分区表项,而GPT可以支持更多的分区,并且使用GUID标识符来标识分区,不受MBR的限制。
GRUB相当于汽车的启动器,读取硬盘上的文件系统,并提供菜单界面给用户选择需要启动的操作系统。
总之,CMOS、BIOS、UEFI、MBR、GPT和GRUB在计算机启动过程中各司其职,协同工作,使得计算机能够正常启动并加载操作系统。就像汽车的引擎、ECU、变速器、排气管、车轮和启动器一样,各组件之间密切配合,使整个系统高效稳定地运行。
GDT 表是全局描述符表,用于存放系统中所有进程所共享的全局描述符。而 LDT 表是本地描述符表,用于存放每个进程独有的局部描述符。GDT 表和 LDT 表都是用来保存描述符,描述符中包含了段的基地址、段大小、权限等信息。
举个简单例子来说,就好比一幢楼房,GDT 表相当于整栋楼房的设计图纸,它规定了整个楼房的建筑面积、楼层数、每层高度、门窗位置等属性信息;而 LDT 表则是每个住户收到的装修自由权,在规定的总面积中可以自由划分房间大小、位置和样式。这样,GDT 表和 LDT 表就能够有效地管理内存,实现进程之间的隔离和保护。
gcc是GNU Compiler Collection的缩写,是一个开源的编译器。它可以将C/C++等高级语言编写的代码转换成计算机能够理解的机器语言。gcc的出现解决了用汇编语言手动编写程序的繁琐和容易出错的问题。
举个例子来说,如果你想要编写一个简单的程序,比如输出"Hello, World!",如果没有gcc这样的编译器,你就需要手动使用汇编语言编写程序,并且需要知道各种指令的含义和操作方法。而有了gcc,你只需要使用高级语言编写程序,然后通过gcc进行编译即可。
实模式(Real Mode):实模式是早期x86计算机使用的一种简单的地址转换模式。在实模式下,物理地址直接映射到内存中的物理地址,没有地址转换。实模式只能寻址1M内存空间,因此无法支持大型程序和多任务。
保护模式(Protected Mode):保护模式是一种比实模式更复杂的地址转换模式。在保护模式下,CPU将虚拟地址转换为线性地址,再通过页表将线性地址转换为物理地址。保护模式支持4GB的内存寻址空间,并且可以同时运行多个程序,具有更高的安全性和稳定性。
32位扁平模式(Flat Model):32位扁平模式是保护模式下的一种特殊模式。在32位扁平模式下,所有的进程共享同一个内存空间,每个进程都可以访问4GB的物理内存空间,但是这也使得进程之间的隔离性较弱,容易受到恶意程序的攻击。
64位长模式(Long Mode):64位长模式是现代计算机使用的一种高级地址转换模式。在64位长模式下,CPU可以访问广阔的地址空间(16EB),并且进程之间具有更强的隔离性和安全性。同时,64位长模式还支持更多的物理内存、更高的处理器性能和更多的指令集扩展。
实模式就像是一间小房子,里面虽然简单而温馨,但空间狭小,只能容纳很少的人。
保护模式就像是一栋大楼,它大而全面,可以容纳更多的人和物品,同时还有门禁、监控等安全措施,确保人们在其中工作和生活的安全性和舒适性。
32位扁平模式就像是一个大仓库,所有进程共享同一个内存空间,进出方便,但也缺乏隔离性,防患于未然的能力相对较弱。
64位长模式就像是一个高层写字楼,每个企业都有独立的办公空间,进出方便、安全性高,同时支持更高级的处理器和指令集扩展,满足更复杂的计算需要。
协程,英文名 Coroutine,是一种比线程更轻量级的存在,常被用于异步编程中。相较于常规函数只能从头到尾顺序执行,协程可以在任何位置暂停执行,并可以在适当的时候恢复执行。
如果我们把一个程序比作是一部电影,那么常规函数就像是电影的每一幕,它们必须按照顺序一幕一幕地播放,不能中断。而协程就像是可以随时暂停并切换播放的DVD,我们可以在看一部电影的同时,暂停并切换到另一部电影,之后再返回继续观看。
那么,协程解决了什么问题呢?
协程主要解决了程序在处理I/O操作(如读写文件,网络请求等)时的等待问题。在传统的同步编程中,当一个函数进行 I/O 操作时,整个程序会等待操作完成才能继续执行下一步,这样就会浪费很多CPU的时间。而在协程中,当遇到 I/O 操作时,可以暂停当前协程的执行,切换到其他协程继续执行,从而提高了程序的效率。
举个例子,假设你正在厨房做饭,你需要烧水、切菜、炒菜。在同步编程中,你会先烧水,然后等水烧开后再切菜,切完菜后再炒菜。这样做的问题是,当你在等待水烧开的时候,你无法做其他事情,这浪费了你的时间。而在使用协程的异步编程中,你可以先烧水,然后在等待水烧开的过程中去切菜,切菜的同时如果水烧开了,你可以暂停切菜,去处理烧开的水,然后再回来继续切菜,这样就提高了你做饭的效率。
如果没有协程,那么在处理大量的I/O操作时,程序可能会因为频繁地等待而导致效率低下。同时,使用多线程虽然可以解决这个问题,但是线程之间的切换成本较高,而且线程的数量通常受到系统资源的限制。
协程的使用场景主要包括网络编程、爬虫等需要进行大量I/O操作的场景。例如在网络编程中,服务器需要同时处理多个客户端的请求,如果采用一线程一请求的模式,当请求量非常大的时候,会消耗大量的系统资源。而使用协程,就可以在一个线程内部,使用多个协程来处理多个请求,大大提高了系统的处理能力。
任务切换是指系统从当前正在执行的进程或线程中切换到另一个进程或线程的过程。在多任务操作系统中,任务切换是实现多任务的关键之一,因为它可以让多个进程或线程共享CPU资源,并在后台运行。任务切换的过程包括保存当前任务的状态、加载下一个任务的状态等,这需要一定的时间和系统开销。因此,在设计多任务系统时需要尽量减少任务切换的次数和开销,以提高系统的性能。而协程的出现,则可以减少任务切换的次数和开销,提高多任务系统的性能。
在计算机系统中,为了保护系统的安全性和稳定性,通常会将操作系统内核(包含系统核心服务和硬件驱动)运行在内核态,而将应用程序运行在用户态。内核态有着更高的执行优先级和访问权限,可以执行任何CPU指令、访问所有内存地址。用户态则权限较低,不可以执行所有CPU指令,也不能直接访问所有的内存地址,这样避免了应用程序对系统核心服务和硬件的误操作。
当用户态的程序需要访问一些特权资源(如硬件设备)或者执行一些特权操作(如创建新的进程)时,必须通过系统调用(System Call)请求操作系统在内核态中代为执行。这个过程就是用户态向内核态的切换。而当操作系统完成这些操作后,又会将控制权返回给用户态程序,这就是内核态向用户态的切换。
比如,假设你在家中安装了一扇需要密码才能打开的门。这扇门可以比作内核态,而你自己可以比作用户态。在这个情况下,你不能直接开启这扇门,而需要通过输入密码(即进行系统调用)请求门(即操作系统)为你打开。门确认密码正确后,就会为你打开,这个过程就像是用户态向内核态的切换。而当你通过了这扇门之后,你便可以进行其他操作,这就像是内核态向用户态的切换。
要注意的是,用户态和内核态的切换是有一定的代价的,因为它会涉及到一系列的上下文切换操作,比如保存用户态的上下文,加载内核态的上下文,再返回时再保存内核态的上下文,恢复用户态的上下文等。因此在设计程序时,应尽量减少不必要的系统调用,以提高程序的性能。
socket(也被称为套接字)是一个抽象层,它位于应用层和传输层之间,是一种提供网络通信服务的API。它的主要作用是允许应用程序通过网络进行通信,这样的通信可以是同一台计算机内部的不同进程之间的通信,也可以是不同计算机之间的通信。
如果把网络通信比作是电话通话,那么Socket就像是电话机。你需要通过电话机(Socket)拨打电话号码(例如IP地址和端口号)才能进行通话(数据传输)。就像没有电话机就无法进行电话通话一样,没有Socket,应用程序也就无法进行网络通信。
那么,Socket解决了什么问题呢?
Socket主要解决了应用程序如何进行网络通信的问题。在Socket出现之前,如果一个应用程序需要通过网络和另一个应用程序通信,就需要直接操作底层的网络协议(如TCP/IP或UDP),这对于开发者来说是非常复杂和困难的。而有了Socket,开发者就可以通过简单的API接口来进行网络通信,无需关心底层的网络协议细节。
例如,一个web服务器和一个web客户端(例如浏览器)之间的通信,就是通过Socket来实现的。服务器会创建一个Socket,然后监听一个特定的端口,等待客户端的连接请求。客户端也会创建一个Socket,然后通过服务器的IP地址和端口号建立连接。一旦连接建立,服务器和客户端就可以通过这个Socket来互相发送和接收数据。
Socket的使用场景非常广泛,几乎所有需要进行网络通信的应用程序都会使用到Socket,例如web服务器、聊天软件、在线游戏等。
TCP/IP是一种计算机网络通信协议。它实际上是一组协议的集合,其中包括两个主要的协议:传输控制协议(TCP)和互联网协议(IP)。
这个协议集解决了许多问题,但主要是数据包在网络上的发送和接收。让我们用一个邮局的比喻来解释。
假设你需要寄一封非常重要的信,那么你可能会选择快递,让邮递员确认收件人已经收到。这就像TCP协议,它确保数据已经成功地从一台计算机传输到另一台计算机。如果信件太大,无法放入一个包裹,你可能需要将它分成几个部分,然后分别寄出。这就像TCP将数据分片的方式。
但是,你需要知道收件人的地址,以便邮递员可以准确地送到。这就像IP协议,它负责确定数据应该送到哪里。此外,IP协议还确定了数据包的路由,即如何从源头到达目的地。这就像邮递员根据地图规划他的路线一样。
如果我们没有TCP/IP协议,那就像没有邮局或邮递员,每个人都需要自己寻找将信息送到正确地点的方式,这将会非常混乱和低效。没有统一的协议,网络通信将变得非常困难,因为每台计算机可能会使用不同的方式来发送和接收数据。
TCP/IP协议在许多场景中都被使用。任何涉及到在网络上发送和接收数据的场景都会用到它。例如,当你浏览网页时,你的浏览器会使用TCP/IP协议从网页服务器接收数据;当你发送电子邮件时,你的邮件客户端会使用TCP/IP协议将邮件发送到邮件服务器。
当你在程序中打开一个文件进行读写操作时,操作系统会返回一个文件描述符。之后,你就可以通过这个文件描述符来读取、写入或修改文件。
使用比喻来说,你可以把文件描述符看作是酒店的房间钥匙。当你在酒店入住时,你会得到一把钥匙,这把钥匙有一个特定的房间号。这个房间号就像是文件描述符,它代表了特定的房间(即文件或其他I/O资源)。你可以用这把钥匙进入你的房间、离开你的房间,就像你可以用文件描述符读取和写入文件。
文件描述符的使用场景很广泛,主要出现在任何需要进行文件或网络I/O操作的地方。例如,当你读取或写入文件,接受网络连接,或者打开一个管道进行进程间通信时,你都会使用到文件描述符。
select、poll和epoll都是操作系统提供的一种方法,用于监控多个文件描述符以检查其是否准备好进行读写操作。他们都是I/O多路复用技术的一部分,帮助应用程序高效处理多个并发连接。
让我们用一个电话交换机的比喻来解释。
select 是最早的一种系统调用,允许应用程序监视一组文件描述符(在我们的比喻中,可以看作是一组电话线)。当某个描述符(电话线)上有数据可以读取(即有电话打来)时,select会通知应用程序。但是,如果电话交换机变得非常大,select会遇到困难,因为它每次都要遍历所有的电话线,即使其中的大多数并没有电话打来。
poll 是对select的改进。它没有最大文件描述符数量的限制,这在我们的比喻中就像没有电话线的最大数量限制。然而,poll仍然需要遍历所有的电话线,这在处理大量连接时仍然会造成性能问题。
epoll 是一种更先进的解决方案,它消除了select和poll的主要问题。在我们的比喻中,epoll就像一个智能电话交换机,它只会告诉你那些真正有电话打来的电话线,而不是所有的电话线。这大大提高了处理大量并发连接的效率。
如果没有这些技术,处理多个并发连接将变得非常困难和低效。就像如果没有电话交换机,接线员需要在每条电话线之间切换,检查是否有电话打来。
网卡驱动解决了操作系统与网卡之间的通信问题。对于操作系统来说,网卡的内部工作原理是复杂且多样的,通过网卡驱动,操作系统可以通过一致的接口与网卡进行交互,而不必了解网卡的具体实现细节。
如果你的计算机系统遭到了病毒攻击,可能会导致系统出现异常,无法正常启动。这时候,你可以尝试进入安全模式,禁止所有第三方应用程序和服务的自动启动,从而防止病毒再次侵入并导致更多的破坏。