操作系统-硬件结构学习心得

news2024/12/25 23:55:48

1. 程序执行基本过程

那CPU执行程序的过程如下: 
●第一步,CPU读取[程序计数器」的值,这个值是指令的内存地址,然后CPU的「控制单元操作
「地址总线」指定需要访问的内存地址,接着通知内存设备准备数据,数据准备好后通过「数据总线」
将指令数据传给CPU, CPU收到内存传来的数据后,将这个指令数据存入到「指令寄存器」。
●第二步,[程序计数器」的值自增,表示指向下一条指令。这个自增的大小,由CPU的位宽决定,比
如32位的CPU,指令是4个字节,需要4个内存地址存放,因此「程序计数器」的值会自增4;
●第三步,CPU分析「指令寄存器」中的指令,确定指令的类型和参数,如果是计算类型的指令,就把
指令交给[逻辑运算单元」运算;如果是存储类型的指令,则交由「控制单元执行;
简单总结一下就是,一个程序执行的时候,CPU会根据程序计数器里的内存地址,从内存里面把需要执行
的指令读取到指令寄存器里面执行,然后根据指令长度自增,开始顺序读取下一条指令。
CPU从程序计数器读取指令、到执行、再到下-条指令,这个过程会不断循环,直到程序执行结束,这个不断循环的过程被称为CPU的指令周期。

举例:

编译器会把a=1+2翻译成4指令,存放到正文段中。如图,这4条指令被存放到了0x100~
0x10c的区域中:
●0x100的内容是load 指令将0x200地址中的数据1装入到寄存器RO ;
●0x104的内容是load 指令将0x204地址中的数据2装入到寄存器R1 ;
●0x108的内容是add 指令将寄存器RO和R1 的数据相加,并把结果存放到寄存器R2 ;
●0x10c 的内容是store指令将寄存器R2中的数据存回数据段中的0x208地址中,这个地址也就是变量a内存中的地址;
编译完成后,具体执行程序的时候,程序计数器会被设置为0x100地址,然后依次执行这4条指令。上面的例子中,由于是在32位CPU执行的,因此一条指令是占32位大小,所以你会发现每条指令间隔4个字节。而数据的大小是根据你在程序中指定的变量类型,比如int 类型的数据则占4个字节,char 类型的数据则占1个字节。

2. 存储器的层次结构

2.1 寄存器

寄存器是最靠近CPU的控制单元和逻辑计算单元的存储器,速度最快价格最高。

●32位CPU中大多数寄存器可以存储4个字节;
●64位CPU大多数寄存器可以存储8个字节。

2.2 CPU Cache(高速缓存)

CPU Cache用的是一种叫SRAM (Static Random-Access Memory,静态随机存储器)的芯片。
静态存储器:只要有电,数据就可以保持存在,而一旦断电,数据就会丢失
了。
CPU的高速缓存,通常可以分为L1、L2、 L3 这样的三层高速缓存,也称为一级缓存、二级缓存、 三级缓存。

L1高速缓存:分成指令缓存数据缓存,速度最快。

L2高速缓存:速度次之。

L3高速缓存:多核共用,速度最慢。

 2.3 内存

内存用的芯片和CPU Cache有所不同,它使用的是一种叫作DRAM (Dynamic Random Access
Memory,动态随机存取存储器)的芯片。
相比SRAM, DRAM的密度更高,功耗更低,有更大的容量,而且造价比SRAM芯片便宜很多。
DRAM存储一个bit数据,只需要一个晶体管和一 个电容就能存储,但是因为数据会被存储在电容里,电容会不断漏电,所以需要定时刷新电容,才能保证数据不会被丢失,这就是DRAM之所以被称为动态存储器的原因,只有不断刷新,数据才能被存储起来。
DRAM的数据访问电路和刷新电路都比SRAM复杂,所以访问的速度会更慢,内存速度大概在
200~300个时钟周期之间。

2.4 SSD/HDD 硬盘

SSD (Solid -state disk)就是我们常说的固体硬盘,结构和内存类似,但是它相比内存的优点是断电后数据还是存在的,而内存、寄存器、高速缓存断电后数据都会失。内存的读写速度比SSD大概快
10~1000倍。

2.5 存储器的层次关系

每个存储器只和相邻的一层存储器设备打交道,并且存储设备为了追求更快的速度,所需的材料成
本必然也是更高,也正因为成本太高,所以CPU内部的寄存器、L1\L2\L3 Cache只好用较小的容量,相反内存、硬盘则可用更大的容量,这就我们今天所说的存储器层次结构。

3. 影响代码在CPU运行速度因素

3.1 CPU访问cache方式:

CPU Cache的数据是从内存中读取过来的,它是以一小块一小块读取数据的,而不是按照单个数组元素来读取数据的,在CPU Cache中的,这样一小块-一小块的数据,称为Cache Line (缓存块)。

事实上,CPU 读取数据的时候,无论数据是否存放到Cache中, CPU都是先访问Cache,只有当Cache中找不到数据时,会访问内存,并把内存中的数据读入到Cache中,CPU 再从CPU Cache读取数据。


对于直接映射Cache采用的策略,就是把内存块的地址始终「映射」在一个CPU Cache Line (缓存块)的地址,至于映射关系实现方式,则是使用「取模运算」, 取模运算的结果就是内存块地址对应的CPUCache Line (缓存块)的地址。举个例子,内存共被划分为32个内存块,CPU Cache共有8个CPU Cache Line,假设CPU想要访问第15号内存块,如果15号内存块中的数据已经缓存在CPU Cache Line中的话,则是一定映射在7号CPUCacheLine中,因为15%8的值是7。
 

 为了区别不同的内存块,在对应的CPU Cache Line中我们还会存储一个组标记 (Tag) 。这个组标
记会记录当前CPU Cache Line中存储的数据对应的内存块,我们可以用这个组标记来区分不同的内存块。
除了组标记信息外,CPU Cache Line还有两个信息:
●一个是,从内存加载过来的实际存放数据(Data)。
●另一个是,有效位(Valid bit) ,它是用来标记对应的CPU Cache Line中的数据是否是有效的,如果有效位是0,无论CPU Cache Line中是否有数据,CPU都会直接访问内存,重新加载数据。
●偏移量:CPU在从CPU Cache读取数据的时候,并不是读取CPU Cache Line中的整个数据块,而是读取CPU所需要的一个数据片段,这样的数据统称为一个字(Word)。利用偏移量确定数据块中的字。
因此,一个内存的访问地址,包括组标记、CPU Cache Line索引、偏移量这三种信息,于是CPU就能通过这些信息,在CPU Cache中找到缓存的数据。而对于CPU Cache里的数据结构,则是由索引+有效位+组标记+数据块组成。

举例:

如果内存中的数据已经在CPU Cache中了,那CPU访问一个内存地址的时候,经历这4个步骤:
1.根据内存地址中索引信息,计算在CPU Cache中的索引,也就是找出对应的CPU Cache Line的地址;
2.找到对应CPU Cache Line后,判断CPU Cache Line中的有效位,确认CPU Cache Line中数据是否是有效的,如果是无效的,CPU就会直接访问内存,并重新加载数据,如果数据有效,则往下执行; 
3.对比内存地址中组标记和CPU Cache Line中的组标记,确认CPU Cache Line中的数据是我们要访问的内存数据,如果不是的话,CPU就会直接访问内存,并重新加载数据,如果是的话,则往下执行;
4.根据内存地址中偏移量信息,从CPU Cache Line的数据块中,读取对应的字。

3.2 提升数据缓存命中率

我们知道CPU访问内存的速度,比访问CPU Cache的速度慢了100 多倍,所以如果CPU所要操作的数据在CPU Cache中的话,这样将会带来很大的性能提升。访问的数据在CPU Cache中的话,意味着缓存命中,缓存命中率越高的话,代码的性能就会越好,CPU也就跑的越快。
于是,「如何写出让CPU跑得更快的代码?」这个问题, 可以改成「如何写出CPU缓存命中率高的代码?」。
在前面我也提到,L1 Cache通常分为「数据缓存」和「指令缓存」,这是因为CPU会分别处理数据和指令,比如1+1=2 这个运算,+ 就是指令,会被放在「指令缓存」中,而输入数字1则会被放在「数据缓存」里。因此,我们要分开来看「数据缓存」和「指令缓存的缓存命中率。

 顺序访问比跳跃访问的速度更快。

 3.3 如何提升指令缓存的命中率?

●第一个操作,循环遍历数组,把小于50的数组元素置为0;
●第二个操作,将数组排序;
那么问题来了,你觉得先遍历再排序速度快,还是先排序再遍历速度快呢?
在回答这个问题之前,我们先了解CPU的分支预测器。对于if条件语询,意味着此时至少可以选择跳转到两段不同的指令执行,也就是if还是else中的指令。那么,如果分支预测可以预测到接下来要执行if的指令,还是else的话,就可以提前把这些指令放在指令缓存中,这样CPU可以直接从
Cache读取到指令,是执行速度就会很快。

当数组中的元素是随机的,分支预测就无法有效工作,而当数组元素都是是顺序的,分支预测器会动态地根据历史命中数据对未来进行预测,这样命中率就会很高。
因此,先排序再遍历速度会更快,这是因为排序之后,数字是从小到大的,那么前几次循环命中if 
50的次数会比较多,于是分支预测就会缓存if 里的array[i] = 0指令到Cache中,后续CPU执行
该指令就只需要从Cache读取就好了。

核心:分支预测器会动态地根据历史命中数据对未来进行预测,这样命中率就会很高。

3.4 如何提升多核 CPU 的缓存命中率?

 在单核CPU,虽然只能执行一个线程,但是操作系统给每个线程分配了一个时间片,时间片用完了,就调度下一个线程,于各个线程就按时间片交替地占用CPU,从宏观上看起来各个线程同时在执行。而现代CPU都是多核心的,线程可能在不同CPU核心来回切换执行,这对CPU Cache不是有利的,虽然L3 Cache是多核心之间共享的,但是L1和L2 Cache都是每个核心独有的,如果-个线程在不同核心来切换,各个核心的缓存命中率就会受到影响,相反如果线程都在同一个核心上执行,那么其数据的L1和L2 Cache的缓存命中率可以得到有效提高,缓存命中率高就意味着CPU可以减少访问内存的频率。当有多个同时执行「计算密集型」的线程,为了防止因为切换到不同的核心,导致缓存命中率下降的问题,我们可以把线程绑定在某一个CPU核心上,这样性能可以得到非常可观的提升。
在Linux.上提供了sched setaffinity 方法,来实现将线程绑定到某个CPU核心这一功能。

4.  CPU缓存一致性

4.1 CPU Cache 的数据写入

写直达:保持内存与Cache一致性最简单的方式是,把数据同时写入内存和Cache中,这种方法称为写直达(Write Through)

在这个方法里,写入前会先判断数据是否已经在CPU Cache里面了:
●如果数据已经在Cache里面,先将数据更新到Cache里面,再写入到内存里面;
●如果数据没有在Cache里面,就直接把数据更新到内存里面。
写直达法很直观,也很简单,但是问题明显,无论数据在不在Cache里面,每次写操作都会写回到内存,这样写操作将会花费大量的时间,无疑性能会受到很大的影响。

写回:

既然写直达由于每次写操作都会把数据写回到内存,导致影响性能,于是为了要减少数据写回内存的频率,就出现了回(Write Back)的方法。在写回机制中,当发生写操作时,新的数据仅仅被写入Cache Block里,只有当修改过的Cache Block「被替换」时才需要写到内存中,减少了数据写回内存的频率,这样便可以提高系统的性能。

可以发现写回这个方法,在把数据写入到Cache的时候,只有在缓存不命中,同时数据对应的Cache中的Cache Block为脏标记的情况下,才会将数据写到内存中,而在缓存命中的情况下,则在写入后Cache后,只需把该数据对应的Cache Block标记为脏即可,而不用写到内存里。
这样的好处是,如果我们大量的操作都能够命中缓存,那么大部分时间里CPU都不需要读写内存,自然性能相比写直达会高很多。

4.2 缓存一致性问题

这时如果A号核心执行了i++语句的时候,为了考虑性能,使用了我们前面所说的写回策略,先把值为1的执行结果写入到L1/L2 Cache中,然后把L1/L2 Cache中对应的Block标记为脏的,这个时候数据其实没有被同步到内存中的,因为写回策略,只有在A号核心中的这个Cache Block要被替换的时候,数据才会写入到内存里。如果这时旁边的B号核心尝试从内存读取i变量的值,则读到的将会是错误的值,因为刚才A号核心更新i值还没写入到内存中,内存中的值还依然是0。这个就是所谓的缓存一致性问题, A号核心和B号核心的缓存,在这个时候是不一致, 从而会导致执行结果的错误。

那么,要解决这一问题,就需要一种机制, 来同步两个不同核心里面的缓存数据。实现的这个机制的
话,要保证做到下面这2点:
●第一点,某个CPU核心里的Cache数据更新时,必须要传播到其他核心的Cache,这个称为写传播
(Write Propagation) ;
●第二点,某个CPU核心里对数据的操作顺序,必须在其他核心看起来顺序是一样的, 这个称为事务的串行化(Transaction Serialization)
第一点写传播很容易就理解,当某个核心在Cache更新了数据,就需要同步到其他核心的Cache里。而对于第二点事务的串行化,我们举个例子来理解它。
假设我们有一个含有4个核心的CPU,这4个核心都操作共同的变量i (初始值为0)。A 号核心先把 i
值变为100,而此时同一时间,B号核心先把 i 值变为200,这里两个修改,都会「传播」到C和D号核
心。

那么问题就来了,C号核心先收到了A号核心更新数据的事件,再收到B号核心更新数据的事件,因此C号核心看到的变量i是先变成100,后变成200。
而如果D号核心收到的事件是反过来的,则D号核心看到的是变量i先矮成200,再变成100,虽然是做到了写传播,但各个Cache面的数据还是不一致的。所以,我们要保证C号核心和D号核心都能看到相同顺序的数据变化,比如变量i都是先变成100,变成200,这样的过程就是事务的串行化。
要实现事务串行化,要做到2点:
●CPU核心对于Cache中数据的操作,需要同步给其他CPU核心;
●引入「锁」的概念,如果两个CPU核心里有相同数据的Cache, 那么对于这个Cache数据的更新,
只有拿倒了[锁」,才能进行对应的数据更新。
那接下来我们看看,写传播和事务串行化具体是用什么技术实现的。

4.3 总线嗅探

写传播的原则就是当某个CPU核心更新了Cache中的数据,要把该事件广播通知到其他核心。最常见实现的方式是总线嗅探(Bus Snooping)。
总线嗅探方法很简单,CPU 需要每时每刻监听总线上的一切活动,但是不管别的核心的Cache是否缓存相同的数据,都需要发出一个广播事件,这无疑会加重总线的负载。

此外,总线嗅探只是保证了某个CPU核心的Cache更新数据这个事件能被其他CPU核心知道,但是并不能保证事务串行化。于是,有一个协议基于总线嗅探机制实现了事务串行化,也用状态机机制降低了总线带宽压力,这个协议就是MESI协议,这个协议就做到了CPU缓存一致性。

4.4 MESI协议

基于总线嗅探机制的MESI协议,是保障缓存一致性的协议。MESI协议,已修改、独占、共享已失效这四个状态的英文缩写的组合。整个MSI状态的变更,则是根据来自本地CPU核心的请求,或者来自其他CPU核心通过总线传输过来的请求,从而构成一个流动的状态机。外,对于在已修改或者独占状态的Cache Line,修改更新其数据不需要发送广播给其他CPU核心。

具体实现:
这四个状态来标记Cache Line四个不同的状态。
「已修改」状态就是我们前面提到的脏标记,代表该Cache Block. 上的数据E经被更新过,但是还没有写到内存里。而已失效」状态,示的这个Cache Block里的数据E经失效了,不可以读取该状态的数据。
「独占和[共享」状态都代表Cache Block的数据是干净的,也就是说,这个时候Cache Block的
数据和内存里面的数据是一致性的。
「独占和[共享」的差别在于,独占状态的时候,数据只存储在一个CPU核心的Cache里,其他CPU核心的Cache没有该数据。这个时候,如果要向独占的Cache写数据,就可以直接自由地写入,而不需要通知其他CPU核心,因为只有你这有这个数据,就不存在缓存一致性的问题了 ,于是就可以随便操作该数据。外,在「独占」状态下的数据,如果有其他核心从内存读取了相同的数据到各自的Cache,那么这个时候,独占状态下的数据就会变成共享状态。
那么,[共享」 状态代表着相同的数据在多个CPU核心的Cache里都有,所以当我们要更新Cache面的数据的时候,不能直接修改,而是要先向所有的其他CPU核心广播一个请求, 要求先把其他核心的Cache中对应的Cache Line标记为「无效」状态,然后再更新当前Cache里面的数据。

通过四种状态的变化来减少广播请求的发送。

 5. CPU执行任务的过程 

5.1 伪共享的定义

伪共享(False Sharing)的定义: 发现如果1号和2号CPU核心这样持续交替的分别修改变量A和B,就会重复这两个步骤,Cache 并没有起到缓存的效果,虽然变量A和B之间其实并没有任何的关系,但是因为同时归于一个Cache Line,这个Cache Line中的任意数据被修改后,都会相互影响,从而出现交替修改共享和已失效状态这两个步骤。因此,这种因为多个线程同时读写同一个Cache Line的不变时,导致CPU Cache失效的现象称为伪共享(False Sharing)。

5.2 避免共享的方法

所以,为了防止前Cache伪共享问题,可以使用宏定义,将b的地址设置为Cache Line 对齐地址,如下:

避免Cache伪共享实际上是用空间换时间的思想,浪费部分Cache空间,从而换来性能的提升

5.3 CUP选择线程的依据 

在Linux内核中,进程和线程都是用task struct结构体表示的,区别在于线程的task _struct结构体里
部分资源是共享了进程已创建的资源,比如内存地址空间、代码段、文件描述符等,所以Linux中的线程也被称为轻量级进程,因为线程的task_struct 相比进程的task_ struct 承载的资源比较少,因此以「轻」名。

一般来说,没有创建线程的进程,只有单个执行流,它被称为是主线程。如果想让进程处理更多的事情,可以创建多个线程分别去处理,但不管怎么样,它们对应到内核里都是task_ struct。

,Linux内核里的调度器,调度的对象就是task struct,接 下来我们就把这个数据结构统称为任
务。在Linux系统中,根据任务的优先级以及响应要求,主要分为两种,其中优先级的数值越小,优先级越高:
●实时任务,对系统的响应时间要求很高,也就是要尽可能快的执行实时任务,优先级在0~99 范围内
的就算实时任务;
●普通任务,响应时间没有很高的要求,优先级在100~139 范围内都是普通任务级别;

5.3.1 调度类

由于任务有优先级之分,Linux 系统为了保障高优先级的任务能够尽可能早的被执行,于是分为了这几种调度类,如下图:

5.3.2 完全公平调度

我们平日里遇到的基本都是普通任务,对于普通任务来说,公平性最重要,在Linux面,实现了一个基于CFS的调度算法,也就是完全公平调度(Completely Fair Scheduling)。
这个算法的理念是想让分配给每个任务的CPU时间是一样, 于是它为每 个任务安排一个虚拟运行时间vruntime,如果一个任务在运行,运行的越久,该任务的vruntime自然就会越大,而没有被运行的任务,vruntime 不会变化的。 

那么,在CFS算法调度的时候,会优先选择vruntime少的任务,以保证每个任务的公平性。这就好比,让你把-桶的奶茶平均分到10杯奶茶杯里,你看着哪杯奶茶少,就多倒一些;哪个多了,就先不倒,这样经过多轮操作,虽然不能保证每杯奶茶完全一样多, 但至少是公平的。当然,上面提到的例子没有考虑到优先级的问题, 虽然是普通任务,但是普通任务之间还是有优先级区分的,所以在计算虚拟运行时间vruntime还要考虑普通任务的权重值,注意权重值并不是优先级的值,内核中会有一个nice级别与权重值的转换表,nice 级别越低的权重值就越大,于nice值是什么,我们后面会提到。于是就有了以下这个公式:
 你可以不用管NICE_0_ LOAD是什么,你就认为它是一个常量,那么在「同样的实际运行时间」里,高权重任务的vruntime比低权重任务的vruntime少,你可能会奇怪为什么是少的?你还记得CFS调度吗,它是会优先选择vruntime少的任务进行调度,所以高权重的任务就会被优先调度了,于是高权重的获得的实际运行时间自然就多了。

5.3.3 CPU 运行队列

一个系统通常都会运行着很多任务,多任务的数量基本都是远超CPU核心数量,因此这时候就需要排队。事实上,每个CPU都有自己的运行队列(Run Queue, rq),用于描述在此CPU上所运行的所有进程,其队列包含三个运行队列,Deadline 运行队列dl_ rq、实时任务运行队列rt_ rq 和CFS运行队列cfs_ rq, 中cfs_ _rq是用红黑树来描述的,按vruntime大小来排序的,最左侧的叶子节点,就是下次会被调度的任务。

这几种调度类是有优先级的,优先级如下: Deadline > Realtime> Fair, 这意味着Linux 选择下一个务执行的时候,会按照此优先级顺序进行选择,也就是说先从d1_ rq里选择任务,然后从rt_ rq里选择
任务,后从cfs_ rq里选择任务。因此,实时任务总是会比普通任务优先被执行。


5.3.4 调整优先级

 如果我们启动任务的时候,没有特意去指定优先级的话,默认情况下都是普通任务,普通任务的调度类是Fair,由CFS调度器来进行管理。CFS 调度器的目的是实现任务运行的公平性,也就是保障每个任务的运行的时间是差不多的。如果你想让某个普通任务有更多的执行时间,可以调整任务的nice 值,从而让优先级高-些的任务执行更多时间。nice 的值能设置的范围是-20~19, 值越低, 表明优先级越高,因此-20是最高优先级,19则是最低优先级,默认优先级是0。

不是觉得nice 值的范围很诡异?事实上, nice 值并不是表示优先级,而是表示优先级的修正数值,它与优先级(priority) 的关系这样的: priority(new) = priority(old) + nice。内核中,priority 的范围是0~139,值越低,优先级越高,其中前面的0~99范围是提供给实时任务使用的,而nice值是映射到100~139,这个范围是提供给普通任务用的,因此nice值调整的是普通任务的优先级。

 6.软中断

6.1 软中断的定义

中断:是一种异步的事件处理机制, 可以提高系统的并发处理能力。
操作系统收到了中断请求,会打断其他进程的运行,所以中断请求的响应程序,也就是中断处理程序,要尽可能快的执行完,这样可以减少对正常进程运行调度地影响。

上部分要做的事情很少,先禁止网卡中断,避免频繁硬中断,而降低内核的工作效率。接着,内核会角发一个软中断,把一些处理比较耗时且复杂的事情,交给「软中断处理程序去做,也就是中断的下半部,其主要是需要从内存中找到网络数据,再按照网络协议栈,对网络数据进行逐层解析和处理,后
数据送给应用程序。所以,中断处理程序的上部分和下半部可以理解为: 


 还有一个区别,硬中断(上半部)是会打断CPU正在执行的任务,然后立即执行中断处理程序,而软中
断(下半部)是以内核线程的方式执行,并且每一个CPU都对应一个软中断内核线程,名字通常为
「ksoftirqd/CPU编号,比如0号CPU对应的软中断内核线程的名字是ksoftirqd/e
不过,软中断不只是包括硬件设备中断处理程序的下半部,一些内核自定义事件也属于软中断,比如内核调度等、RCU 锁(内核里常用的一种锁)等。

7. 计算机存储数据方式

7.1 负数的存储方式

7.2 小数的存储方式

现在绝大多数计算机使用的浮点数,一般采用的是 IEEE 制定的国际标准,这种标准形式如下图:


 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1965261.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【每日刷题】Day90

【每日刷题】Day90 🥕个人主页:开敲🍉 🔥所属专栏:每日刷题🍍 🌼文章目录🌼 1. 单词缩写_牛客题霸_牛客网 (nowcoder.com) 2. 面试题 01.03. URL化 - 力扣(LeetCode&am…

深度剖析Google黑科技RB-Modulation:告别繁琐训练,拥抱无限创意生成和风格迁移!

给定单个参考图像,RB-Modulation提供了一个无需训练的即插即用解决方案,用于(a)风格化和(b)具有各种提示的内容样式组合,同时保持样本多样性和提示对齐。例如,给定参考样式图像(例如“熔化的黄金3d渲染样式”)和内容图像(例如(a)“狗”),RB-Modulation方法可以坚持所需的提…

内存泄漏 与 内存溢出

1.内存溢出(Memory Overflow) 生活样例: 内存容量就像一个桶,内存就是水,水 溢出 就是水满了。定义: 内存溢出是指程序试图使用超过其可用内存限制的内存。这种情况通常会导致程序崩溃或异常。内存溢出一般是由于分配了过多…

Mixture of Experts with Attention论文解读

注意这篇论文没有代码,文章所谓的注意力是加性注意力,找scaled dot-product的伙计可以避坑了,但还是有值得学习的地方。 score是啥? 这个score标量怎么计算得到,请假设一下x和z的值,计算演示一下 expert是…

第十二章(重点 元数据管理)

语境关系图: 1. 元数据概念: 元数据从技术的角度叫元数据 从业务的角度叫数据资源管理目录 技术 元数据 业务 数据资源管理目录 但是并不是数据资产目录 如果没有可靠的原数据,组织就不知道它拥有什么数据,数据表示什么&#xff…

运行ruoyi

创建数据库 根据ry_20240629.sql创建ry-cloud数据库 根据ry_config_20231204.sql创建ry-config数据库 nacos 数据库配置 修改nacos/conf/application.properties 单机版运行 startup.cmd -m standalone redis 运行后端 运行gateway,auth,modules/system模块 可能遇到的问…

怎么给电脑选一款合适的固态硬盘?就看这个参数!

前言 前段时间有很多小伙伴找小白修电脑,在修电脑的过程中,小白也会稍微看一下硬件配置。 小白就发现一个事情:很多小伙伴其实都不太懂电脑硬件。 为啥这么说呢?简单来说就是主板上使用了“不合适”的固态硬盘作为主系统硬盘。…

VulnHub-Tomato靶机渗透教程 简单易懂 报错链接

Tomato靶机是一个用于渗透测试和漏洞研究的虚拟机。 环境准备 攻击机(Kali Linux)IP:192.168.252.134 目标机 IP:192.168.252.133 这里我两台虚拟机都是NAT模式 渗透步骤 1.端口扫描 这里我没用kali自带的 我用的物理机上…

【Python学习手册(第四版)】学习笔记12.1-语法规则拓展

个人总结难免疏漏,请多包涵。更多内容请查看原文。本文以及学习笔记系列仅用于个人学习、研究交流。 本文是对【学习笔记10】-语句编写的通用规则 介绍过的语法概念进行复习并扩展。非常简单,应该是我写过的最简单的文章,阅读时间&#xff1a…

学习Mybatis及其简单配置

目录 JDBC的弊端 为什么要有ORM模型? 什么是ORM模型? Mybatis和hibernate 区别: Mybatis解决了jdbc的问题 为什么选择myBatis(优势)? 什么是MyBatis 主配置文件(config文件) Mapper文件…

TwinCAT3 C++环境安装教程

文章目录 下载windos插件:下载地址,安装这个插件是为了能在 TwinACT 3 工程环境创建和编辑 C模块。 点击下载的文件,打开其中的KitSetup.exe 在打开的窗口中选择"Build Environment"后点击OK 弹出的窗口点击ok 选择“I agree”后点击…

从零逐步实现SVM(含公式推导)上

支持向量机(SVM)相关概念 支持向量:支持或支撑平面上把两类类别划分开的超平面的向量点线性可分支持向量机:通过硬间隔最大化,学习一个线性分类器线性支持向量机:通过软间隔最大,学习一个线性分…

掌握时间的秘密:pytz 库的神奇之旅

文章目录 掌握时间的秘密:pytz 库的神奇之旅背景:为何选择 pytz?pytz 库是什么?如何安装 pytz?函数的使用方法场景应用常见问题与解决方案总结 掌握时间的秘密:pytz 库的神奇之旅 背景:为何选择…

【Vue3】默认插槽

【Vue3】默认插槽 背景简介开发环境开发步骤及源码 背景 随着年龄的增长,很多曾经烂熟于心的技术原理已被岁月摩擦得愈发模糊起来,技术出身的人总是很难放下一些执念,遂将这些知识整理成文,以纪念曾经努力学习奋斗的日子。本文内…

学习c语言第18天(字符串和内存函数)

1.函数介绍 1.1 strlen size_t(就是无符号整形) strlen(const char * str); 字符串已经\0作为结束标志,strlen函数返回的是在字符串中\0前面出现的字符个数(不包 含\0) 参数指向的字符串必须要以\0结束。 注意函数的返回值为size_t,…

Java并发—Java内存模型以及线程安全

目录 一、Java内存模型 JMM的核心概念 二、什么是线程安全? 1、原子性 2、有序性 3、可见性 三、如何确保线程安全? 1、sychronized关键字 2、Lock接口和其实现 3、volatile关键字 4、Atomic原子类 5、ThreadLocal 6、不可变对象 7、并发集…

电商数据采集封装API的详细步骤分享(API测试实例)

在当今的电商行业中,数据采集已成为企业获取市场洞察、优化运营策略、提升用户体验的重要手段。而封装电商数据采集的API接口,则是将这一复杂过程标准化、模块化的有效方式。本文将详细分享电商数据采集封装API的步骤,并通过一个实际的API测试…

努力努力努力的第十四天(2024.7.31)

昨天日期写错了写成2020.7.30,应该是2024.7.31(手滑了哈哈哈) 1.行列转换 效果演示: 这是未经行列转换操作的t_score表: 这是经过行列转换后的t_score表: 第一步:确定初步的做法 使用分组查询(group by…

20240731在WIN10下数框框的方法【CPU】

20240731在WIN10下数框框的方法【CPU】 2024/7/31 20:14 百度:WINDOWs 10 多核CPU 数框框 win10怎么数框框 1、首先打开win10系统,进入任务管理器。 2、其次点击CPU使用率窗口,鼠标悬停在右边的窗口按右键。 3、最后将图形更改为:…

【计算机网络】http协议的原理与应用,https是如何保证安全传输的

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,…