首页 > 其他分享 >02. x86处理器运行方式

02. x86处理器运行方式

时间:2024-04-27 19:11:18浏览次数:23  
标签:02 x86 地址 指令 内存 寄存器 执行 CPU 处理器


【CPU指令】

CPU控制器通过读取存储器中的指令确定要执行的功能,CPU运行需要不停的读取指令,计算机启动后CPU会从固定地址处开始读取指令,首先读取 NOR Flash 存储器中的固件,固件执行完毕后引导操作系统执行。

指令是一个二进制数据,主要由如下两部分组成:
1.操作码,设置控制器执行的功能,比如读写数据、数学运算、逻辑运算。
2.地址码,设置指令读写数据的位置,可以是寄存器、内存单元、IO端口,若要将一个固定的数据写入到其它位置,可以将数据直接存储在地址码中,存储在地址码中的数据称为立即数。

x86是英特尔发明的一种复杂指令集,最早用于16位的8086处理器,之后扩展到32位处理器,进入64位处理器时代后,由AMD扩展为支持64位处理器,此版本的x86称为x86-64,或AMD64。

在计算机中,一个数据既可以用来表示一个数学中使用的数据,也可以表示指令,为了区分两者,之后的文章将数据分为数学数据和指令数据,以免混乱。

 


【指令执行方式】

指令执行顺序

CPU使用专用寄存器记录指令数据所在的内存地址,依靠此寄存器读取指令数据执行,指令的执行顺序有两种:

1.顺序执行,本条指令执行完毕后,CPU自动增加指令地址寄存器的值,定位到下一条指令所在的地址,指令按存储顺序依次执行。
2.跳转执行,本条指令执行完毕后,不按照存储顺序执行下一条指令,称为跳转执行,可以向前跳转也可以向后跳转,跳转执行由跳转指令实现,跳转指令会修改指令地址寄存器的值,从而实现跳转执行。

指令流水线与转移预测

CPU为了提高指令的执行速度,会将每一条指令的执行分为多个步骤,使用流水线的方式执行每一个步骤,一条指令正在执行时,其它指令已经在流水线中进行前期准备工作。

若遇到跳转指令,CPU会进行分析,预测将要跳转到的地址,然后读取预测地址处的指令进入流水线,这个预测的正确率一般为90%以上,但是无法做到100%,若判断出错,就要清空指令流水线,重新来过,为了避免这种情况发生,在编写程序代码时应该尽量减少使用有条件跳转指令,高级编程语言的编译器会使用多条非跳转指令的组合代替某些有条件跳转指令。

指令调度策略

程序指令按照自身设置的顺序去执行,但是程序设置的指令执行顺序并非最优、最快的,CPU为了更快的执行指令,会将指令执行顺序重新排序,称为动态调度,同时在本条指令进入等待状态后(等待某个数据传输)执行下一条指令,动态调度的前提是不会影响指令原先执行顺序的最终执行结果。

高级语言编译器也会进行类似优化,将指令进行重新排序,不会完全按照代码的编写顺序进行编译,称为静态调度优化,可以减轻CPU动态调度的工作量。

指令执行限制

最早的计算机同一段时间只需要执行一个程序,程序的执行无需做任何限制,指令可以使用CPU的任何资源、可以读写任意内存地址、可以随意跳转执行。
之后人们需要在计算机中同时执行多个程序,此时就需要使用一个程序管理其它所有程序的执行,提供管理功能的程序称为操作系统。

x86处理器为了配合操作系统实现管理功能将指令的执行分为4个权限等级,使用0-3表示。
0级权限最高,可以使用CPU的任何资源、可以读写任意内存地址、可以跳转到低权限指令执行。(注:80486之后,0级指令也不能在只读内存区写入数据,需要首先取消只读限制)
3级权限最低,不能使用CPU的某些寄存器、只能读写操作系统为其分配的内存空间、不能跳转到高权限指令执行。

操作系统使用0级权限执行指令,从而控制用户程序的执行,用户程序使用3级权限,1-2级权限被操作系统废弃不用。

x86处理器启动后,默认不对指令进行任何限制,此模式称为实模式,指令可以通过修改控制寄存器CR0的PG位进入保护模式,保护模式会开启指令的限制功能,进入保护模式后就不能再返回实模式。

 


【处理器中断】

处理器工作时可能会发生各种事件,此时需要暂停正在执行的程序,转而去处理事件,处理器中断功能提供此功能,中断用于暂停正在执行的程序,每个中断对应一个事件,每个中断都有一个编号,CPU通过中断编号确定事件类型,每个中断都可以绑定一个事件处理程序,发生中断后CPU暂停正在执行的程序,然后执行事件处理程序,处理程序执行完毕后返回之前的程序执行。

x86处理器使用地址 0 - 1023 的内存单元存储事件处理程序的执行入口地址,这段内存空间也称为中断向量表。

CPU执行中断处理程序之前,首先使用入栈指令将标志寄存器、CS、IP寄存器中的数据存储到内存中,称为保存现场,然后从中断向量表中读取事件处理程序的地址修改CS、IP寄存器,执行处理程序,处理程序执行完毕后,CPU不会自动将标志寄存器CS、IP寄存器的值恢复,恢复现场的工作由中断处理程序完成,恢复现场使用iret指令即可,执行iret执行时相当于执行如下3条指令:

pop ip
pop cs
popf

 

中断分为两大类,内中断和外中断。

内中断

由CPU内部产生的中断称为内中断,x86处理器在以下情况会发出内中断:

1.执行除法指令时出错,比如除数为0,比如被除数长度不够。
2.处理器启用了单步中断功能,每执行一条指令就产生一个内中断,用于调试程序。
3.访问内存错误,比如向只读内存区写入数据、3级指令访问不属于自己的内存空间。
4.执行int、into指令。

单步中断用于调试程序,控制器每执行完一条指令就会检测标志寄存器的TF位,若为1则产生单步中断,之后CPU执行保存现场工作,此时会将标志寄存器的TF、IF位设置为0,避免在执行处理程序时一直产生单步中断。

外中断

由CPU之外的设备产生的中断称为外中断,外中断分为可屏蔽中断和不可屏蔽中断,多数外中断都是可屏蔽的,可以通过修改标志寄存器的IF位屏蔽这些外中断。

CPU对外连接的两个引脚用于外中断功能(对应可屏蔽中断和不可屏蔽中断),CPU每执行完一条指令就会检查外中断引脚的电压,外部设备通过改变此引脚的电压来告知CPU是否有外部事件发生,比如按下键盘按键后会向CPU发出一个外中断,告知CPU输入设备有数据发送,硬件设备运行出错时也会向CPU发出一个外中断。

 


【多字节数据存储方式】

多字节数据在内存中的排序方式有两种:大端排序、小端排序,数据使用哪种排序方式没有固定规则,不同处理器有不同的安排,x86处理器使用小端排序方式。

大端排序方式

数据的低位字节存储在高地址中,高位字节存储在低地址中,比如一个4字节数据 0x12345678(0x表示16进制,2位16进制数字对应1字节),需要存储在地址0-3的内存单元中,地址0存储高位的0x12,地址0-3依次存储的数据为 0x12345678。

小端排序方式

数据的低位字节存储在低地址中,高位字节存储在高地址中,比如一个4字节数据 0x12345678(0x表示16进制,2位16进制数字对应1字节),需要存储在地址0-3的内存单元中,地址0存储低位的0x78,地址0-3依次存储的数据为 0x78563412。

 


【内存管理方式】

最简单的处理器读写内存单元时直接使用一个数据指定操作的内存地址,此时程序必须放在固定的内存地址处,放在其他位置就会执行出错,比如程序需要跳转执行时,跳转到的地址已经写死,无法改变。
这种内存管理方式适用于功能简单的单片机,它只执行一组固定的程序,程序存储在固定的地址中。

当计算机需要同时执行多个程序时,内存管理方式就会变的复杂,程序需要放在内存的任何位置都能正常执行,并且每个程序只能使用自己占用的内存空间,不能使用其它程序占用的内存空间,为此x86提供了分段、分页两种内存管理方式。

分段管理方式

此方式下处理器将内存分为多组使用,每一组称为一个段,内存地址使用两个数据相加得出,这样做的目的有两个:
1.形成更大范围的内存地址空间,使用容量更大的内存。
2.方便程序重定位,放在内存中的哪个位置都能够正确执行。

内存分段后,指令使用两个数据指定内存地址,分别称为段地址、偏移地址。
1.段地址,存储内存分段在内存空间中的起始地址。
2.偏移地址,存储分段内部存储单元的编号,编号从0开始分配。

内存分段方式起源于8086处理器,8086是16位处理器,内存地址寄存器也是16位的,但是8086为了使用容量更大的内存设计了20位的内存地址。
为了让16位的8086形成20位的内存地址,处理器使用两个数据相加得出内存地址,但是两个16位数据相加最大也只能形成长度17位的数据,8086的解决方法是将段地址乘以16,然后再与偏移地址相加,这样就能形成一个长度20位的二进制数据,乘以16是因为可以使用右移4位的方法代替乘法,右移运算比乘法运算更快,第一个内存分段地址为0,0 * 16 = 0,所以第一个内存分段依然从0开始。

操作系统启动后,首先在内存中创建全局描述符表,用于记录每个分段的起始地址、长度、访问属性等等信息,之后启用CPU的保护模式。
程序执行时,只能占用操作系统设置好的分段,不能自己创建分段,操作系统会为每个执行的程序创建一个局部描述符表,用于记录此程序占用的内存分段相关信息。
指令读写内存单元时只需要指定偏移地址,之后CPU在描述符表中查询段地址,并与偏移地址相加合成内存地址,这样就实现了将程序放在内存的任何位置都能正确执行。

分页管理方式

内存分段管理方式不能设置很小的分段,使用不方便,容易造成浪费,操作系统为程序分配的分段尺寸不一定是程序需要的容量。

为了解决这个问题,从80386开始有了内存分页管理方式,处理器将内存按照4KB的大小分组,每组称为一个页,程序以容量更小的页为单位占用内存,避免浪费,CPU启动后默认以分段方式使用内存,之后启动操作系统,操作系统为进程设置页表后切换到内存分页使用方式。

在32位x86处理器中,启用分页机制后,分段方式依然保留,内存地址依然使用两个数据相加的方式得出,目的是为了形成更大的内存地址,使用容量更大的内存,毕竟一个32位的二进制数据最多只能寻址4GB的内存。
在64位x86-64处理器中,使用64位的内存地址寄存器,64位数据可以寻址足够大的内存空间,启用分页机制后,分段机制将被废弃,不再使用,只使用一个64位的数据指定内存地址即可。

无论是32位处理器还是64位处理器,启用分页机制后,指令使用的内存地址都不再是真实内存地址,而是一个中间地址,称为虚拟地址、或线性地址。

计算机文件有一个文件偏移地址的概念,我更习惯称其为文件内部地址,它的作用是为文件内部数据按存储顺序分配编号,编号从0开始,第一个字节分配编号0、第二个字节编号为1、直到最后一个字节,通过这个编号调用文件数据很方便,无需知道文件存放在哪个位置,就好比使用火车托运货物,你只需要记录火车车厢的编号即可,无论火车走到哪里,你只要告诉管理人员你的货物在几号车厢就能拿走你的货物,至于火车停在哪个轨道上,你无需关心,那是管理人员的事。

虚拟地址的作用就类似文件内部地址,它用于为程序内部数据分配编号,无论程序放在内存中的哪个位置,都可以通过内部地址调用其中的数据。

处理器默认以实模式、内存分段管理方式运行,操作系统启动后,首先启用处理器的保护模式,之后启用内存分页管理方式,程序运行时操作系统按页为其分配内存,并为进程创建一个页表,用于记录程序占用了哪些页、每个页的属性信息(比如此页是否为只读),此时虚拟地址会与内存物理地址建立绑定关系,程序指令通过虚拟地址说明要调用程序内的第几个数据,至于这个虚拟地址对应哪个内存地址,程序不关心,也无需知道,CPU会根据页表中的记录将虚拟地址转换为内存物理地址,之后执行指令,从而实现将程序放在内存中的哪个位置都可以正确执行。

 

注:
x86-64处理器的内存地址寄存器长度为64位,使用一个64位的二进制数据可以形成非常大的内存地址空间,但实际上个人用户远用不到这么大的内存地址,所以某些处理器只使用其中的48位,这样可以简化虚拟地址的转换步骤,指令执行速度更快,剩余的高16位有两种状态,操作系统内核指令将其全部设置为1(只是单纯设置为1,不参与页表转换),用户程序指令将其全部设置为0,若不是这两种状态则是不规范的虚拟地址,处理器无法使用,而服务器CPU需要使用更大容量内存,某些服务器CPU也会开放高16位中的某些位。

 


【寄存器】

寄存器按功能可以分为5类:通用寄存器、段地址寄存器、偏移地址寄存器、标志寄存器、控制寄存器。

通用寄存器

通用寄存器可以存储任何数据,又分为多种长度类型,64位寄存器只存在于64位处理器中,但是同时包含32位、16位、8位寄存器。

x86-64处理器的通用寄存器如下:

64位:rax、rbx、rcx、rdx,继承自最早的x86指令集,x86-64将其扩展为64位。
64位:r8、r9、r10、r11、r12、r13、r14、r15,x86-64指令集新增通用寄存器。

32位:eax、ebx、ecx、edx,由64位的rax-rdx拆分形成,不能与对应的64位寄存器同时使用。
32位:r8d、r9d、r10d、r11d、r12d、r13d、r14d、r15d,由64位的r8-r15拆分形成。

16位:ax、bx、cx、dx,由32位的eax-edx拆分形成。
16位:r8w、r9w、r10w、r11w、r12w、r13w、r14w、r15w,由64位的r8-r15拆分形成。

8位:al、bl、cl、dl、ah、bh、ch、dh,由16位的ax-dx拆分形成,ax的低8位为al,高8位为ah。
8位:r8b、r9b、r10b、r11b、r12b、r13b、r14b、r15b,由64位的r8-r15拆分形成。

段地址寄存器

CS,存储指令数据的段地址。
DS,存储数学数据的段地址。
SS,存储栈空间的段地址。
ES、FS、GS,辅助段寄存器,供程序自由安排使用。

启用保护模式后,CS寄存器存储的不再是段地址,而是段选择子,段选择子用于存储了操作系统设置好的内存分段编号、并记录分段内数据的属性信息,比如指令权限级别。

在x86-64处理器中,启用内存分页管理方式后,段地址寄存器将不再使用。

偏移地址寄存器

16位的8086处理器定义了5个偏移地址寄存器:

IP,存储指令数据的偏移地址,CPU通过CS+IP合成指令数据所在地址。
SP,存储栈空间的偏移地址,push、pop指令通过SS+SP合成要操作的内存地址。
BP,存储栈空间的偏移地址,mov指令读写栈空间时使用BP寄存器指定偏移地址,不与push、pop栈指令共用SP偏移地址寄存器,避免混乱。
SI、DI,存储数学数据的偏移地址,与DS寄存器合成要操作的内存地址,服务于读写数组相关指令,比如stosb、movsb。

其中SP、BP、SI、DI也可以当做通用寄存器使用,在x86-64指令集中,SP、BP、SI、DI又可以拆分为4个8位寄存器:SPL、BPL、SIL、DIL,这4个8位寄存器只存在于x86-64指令集中。

在32位x86处理器中,偏移地址寄存器长度为32位,使用32位模式时需要在名称前添加字母E,比如:EIP、EDI。
在64位x86处理器中,偏移地址寄存器长度为64位,使用64位模式时需要在名称前添加字母R,比如DI表示16位模式、EDI表示32位模式、RDI表示64位模式。

标志寄存器

标志寄存器(FLAGS)用于记录指令执行结果、设置处理器某些功能是否启用,标志寄存器使用二进制位存储信息和设置功能,常用位如下:
0位,进位标志位(CF),记录运算指令是否发生进位行为。
2位,奇偶标志位(PF),记录运算指令结果的低8位中数字1的数量是双数还是单数,若数量为双数或0则PF存储1,若数量为单数PF存储0。
4位,AF位,服务于BCD数据。
6位,0标志位(ZF),记录数学运算、逻辑运算指令执行结果中是否所有二进制位都为0,若都为0则ZF位存储1,否则存储0,用于判断执行结果是否为0。
7位,符号标志位(SF),记录有符号数运算结果是否为负数,若为负则SF存储1,若为正存储0。
8位,单步终端标志位(TF),设置是否产生单步中断,若为1则每执行一条指令就产生一个单步中断。
9位,屏蔽外中断标志位(IF),设置处理器是否接收可被屏蔽的的外中断,若为1则接收,为0不接收。
10位,方向标志位(DF),设置循环读写数据指令(stosb、lodsb、movsb)的读写方向,地址递增或是递减。
11位,溢出标志位(OF),记录有符号数运算是否发生溢出,若溢出则OF设置为1,否则为0。
12-13位,IOPL位,设置指令处于不同权限时,读写IO地址空间的权利。
14位,NT位,控制IRET指令的执行方式。
16位,RF位,设置CPU是否接收调试程序时产生的故障,RF为0则接收,RF为1则不接收。
17位,VM位,模拟8086CPU工作方式的开关。
18位,AC位,设置是否开启地址对齐检查,需要与控制寄存器CR0的AM位共同使用。

其中CF位有三种功能:
1.记录执行无符号数加法运算时结果是否因长度过大导致溢出,若溢出则CF位存储1,否则CF为0。
2.记录执行无符号数减法运算时低位是否向高位借位,若参与减法运算的数据长度超过寄存器长度,处理器将减数分为高低位两部分进行运算,若低位减法发生了向高位借位则CF存储1,否则CF为0,之后进行高位减法时会额外减去CF位的值。
3.记录乘法运算结果是否使用了存储高位的寄存器,执行乘法运算时计算结果使用两个寄存器存储,防止乘法结果长度过大导致溢出,若使用了高位寄存器则CF为1,否则CF为0。

控制寄存器

控制寄存器用于设置处理器工作模式、缓存的禁用与启用、虚拟地址的转换等等功能。
32位x86处理器有4个控制寄存器:CR0 - CR3,其中CR1保留不用,而x86-64定义了CR0-CR15共16个控制寄存器,但只使用其中的CRO、CR2、CR3、CR4、CR8。

CR0的PE位用于设置是否启用保护模式,若为1则启用保护模式,进入保护模式后不能再回到实模式,PG位用于设置是否启用内存分页管理模式,启用分页模式之前必须首先启用保护模式。

 


【缓存】

内存的读写速度虽然很快,但是依然不能满足CPU的需求,为此CPU设计了缓存,缓存是位于CPU内部的存储器,读写速度比内存更快。
不同处理器的缓存容量以及布局不同,缓存内部的存储单元会进行多级分组,首先分为多个大组,大组再分为多个小组,小组有32字节、64字节、或其它容量,缓存小组也称为缓存行或缓存块,不同的人有不同的称呼,创建内存映射时以缓存小组容量创建。

缓存由CPU自动管理,程序无法直接读写,缓存是内存的映射体,那什么是映射呢,映射表示行为结果的转移,这很像中国古装电影里的一个剧情,制作一个纸人并在上面写上某人的名字,之后做法,即可将纸人变成某人的映射体,对纸人施加伤害会转移到原体中,原体的属性也会转移到纸人中,缓存就是如此,CPU将常用的一组数据从内存读取到缓存,之后在缓存中读写数据,对缓存的操作会转移到内存中,长时间不使用的内存映射体会在缓存中删除。

缓存映射内存的方式

读取一个内存地址时CPU会将此数据以及周围的一组数据全部读取到缓存块中(缓存块完全填充满之前指令可以照常执行,无需等待),因为多数情况下之后执行的指令会使用此数据周围的其它数据,之后读取内存时会首先判断缓存是否映射了此内存地址,若有映射则直接读取缓存,若没有映射则重复以上步骤。

向内存地址写入数据时CPU首先判断缓存是否映射了此地址,若有映射则直接将数据写入缓存,之后执行下一条指令,指令无需等待CPU将数据从缓存写入内存,若没有映射则直接将数据写入内存,多核CPU还会涉及到每个核心专用缓存的更新,保证多个核心缓存的一致性,这里不详细讨论此问题。

地址对齐

因为缓存的存在,CPU实际上读取的是缓存,而非内存,内存数据会首先读取到缓存中再使用,CPU在读取内存数据时并非只读取需要的数据,而是将附近数据一起读取到缓存,对于64位CPU,读取内存时每次可以读取8字节(8*8=64位),此时CPU可以对内存单元按8字节长度进行分组使用,比如地址0-7、8-15、16-23、24-31,然后按此分组将数据从内存读取到缓存,这样每次读取的内存地址低3位都为0,这3个低位值可以无需记录,从而简化缓存内部结构、降低发热量、增加器件运行频率,所以一个数据在内存中存储时就需要尽量放在同一组内存单元中,方便CPU整体读取,此时数据的存储需要满足如下规则:

2字节数据,需要放在地址为2的倍数内存地址中。
4字节数据,需要放在地址为4的倍数内存地址中。
8字节数据,需要放在地址为8的倍数内存地址中。

若有一个长度8字节的数据,放在起始地址为4的8个内存单元中,则CPU需要分2次读取,这种存储方式就是地址不对齐,对于访问非地址对齐数据的行为,不同CPU有不同的执行结果,x86处理器默认只是增加数据的读写时间,降低读写速度,而ARM处理器默认会产生错误和异常。


下面使用C语言代码验证地址对齐方式,计算机使用x86-64处理器、linux操作系统、gcc编译器。

#include <stdio.h>
char a = 1;         //长度1字节
int b = 2;          //长度4字节
short c = 3;        //长度2字节
long long d = 4;    //长度8字节
int main()
{
	printf("%d\n%d\n%d\n%d\n", a,b,c,d);
	return 0;
}

变量a、b、c、d的存储方式如下:

Contents of section .data:
 404028 00000000 00000000 00000000 00000000
 404038 01000000 02000000 03000000 00000000
 404048 04000000 00000000

左边的404028表示虚拟地址(使用16进制),右边的数据为程序全局变量,同样使用16进制表示,2位16进制数字对应一个字节,使用小端序存储方式,实际查看时需要按字节重新排序。

地址404038处存储变量a,变量b并非在404039处,因为此处并非4的倍数,而是在40403c处存储,40403c是4的倍数,之后变量c存储于404040处,最后的变量d需要存储在倍数8的地址中,所以中间空余6个字节,在404048处开始存储。

 

标签:02,x86,地址,指令,内存,寄存器,执行,CPU,处理器
From: https://www.cnblogs.com/alixyy/p/18162205

相关文章

  • 2024.04.27 笔记,下午
    b/s架构和c/s架构(重点)(1)bs:浏览器------服务器(web)b:broeser浏览器s:server服务器bs的应用:论坛,百度,知乎,豆瓣,csdn,博客园(2)cs架构:客户端-----服务器(app)c:client客户端s:server服务器cs应用:抖音,微信,qq,快手,酷狗区别:(1)bs不需要更新,直接通过浏览器输入网址进行访问;......
  • 01-linu核心基础-02运维基础重要概念
    ip地址IPv4(第四版本的IP协议)是构成现今互联网技术的基石协议查看自己的IP(公网)访问该网址、即可得知自己在互联网中的IP地址。http://www.net.cn/static/customercare/yourip.aspIP地址理解Ip地址由两部分组成,网络部分+主机部分网络部分指的是同一个网段、好比咱们这间教室......
  • es笔记20240427
    postfilter    聚合结果统计topagges对聚合结果源数据统计   ......
  • XYCTF2024-web-wp
    怎么全是傻逼绕过题。不想评价,就随便打着玩,除了最后一道java反序列化搞心态,其他的ak了:简单题不想说,http注意一下代理是用Via就行,warmup直接:http://xyctf.top:37034/?val1=240610708&val2=QNKCDZO&md5=0e215962017&XYCTF=240610708&XY=240610708LLeeevvveeelll222.phpget......
  • 2024-04-27:用go语言,在一个下标从 1 开始的 8 x 8 棋盘上,有三个棋子,分别是白色车、白色
    2024-04-27:用go语言,在一个下标从1开始的8x8棋盘上,有三个棋子,分别是白色车、白色象和黑色皇后。给定这三个棋子的位置,请计算出要捕获黑色皇后所需的最少移动次数。需要注意的是,白色车可以垂直或水平移动,而白色象可以沿对角线移动,它们不能跳过其他棋子。如果白色车或白色象......
  • 题解:P10329 [UESTCPC 2024] Add
    Add题意将序列进行一系列的操作,输出对\(a_{1}\)的期望值。题目中操作说的比较明了,再次就不特殊声明了。思路据题意所知,每一个\(n\)应该对应了一个固定的答案。于是我就想到可以打表,就打出了下面的式子。n=1时ans=1n=2时ans=5n=3时ans=14n=4时ans=30n=5时ans=5......
  • 2024 天元公学邀请赛夺金记
    2024.4.14今天是初赛,本来以为初赛就上机,没想到是笔试。考试时有个程序没想出来是什么算法,只能手动模拟,算了很久,后来算出一个580,结果发现最近的一个是579,其他都差得很远,直接选了上去。因为当时快没时间了,赛后才发现少减了一个,xjy和cjz都说是容斥,%%%。2024.4.17初赛稳稳当当......
  • Nessus 10.7 Auto Installer for Ubuntu 22.04 (updated Apr 2024)
    Nessus10.7AutoInstallerforUbuntu22.04(updatedApr2024)发布Nessus试用版自动化安装程序,支持macOSSonoma、RHEL9和Ubuntu22.04请访问原文链接:https://sysin.org/blog/nessus-auto-install-for-ubuntu/,查看最新版。原创作品,转载请保留出处。作者主页:sysin.org......
  • 电阻串并联计算器2025下载CResistors 2025 download
    可以算多个串联或者并联电阻的阻值、电压、电流、功率、电导之间的相互计算。本软件为支持64位x64奔腾指令的共享软件,价格很便宜,50元1套,就是2包烟钱。很划算。Itcancalculatethemutualcalculationofresistance,voltage,current,powerandconductanceofmultipleser......
  • Rabbitmq系列02---Exchange
    个人理解:交换机的类型划分个人理解是能过routingkey来划分的,一是否按routingkey找队列;fanout就是不按routingkey找队列,Direct和Topicr按routingkey找队列,只是一个模糊找,一个精准找,而headers不按routingkey是按消头中的内容找队列。一、Fanout(订阅模式|广播模式)  Fanout......