RISC-V RVWMO 内存模型解释

RISC-V RVWMO 内存模型解释

引言

本文介绍 RISC-V RVWMO 内存模型。RVWMO 内存模型定义了什么样的全局内存顺序才是合法的。本引言部分将解释为什么会出现不合法的全局内存顺序,以及为什么需要内存模型。

首先引起乱序的全局内存顺序(指令重排序)有两种原因,一种是软件编译器带来的,另外一种是硬件执行上带来的。

软件带来指令重排序很好理解,如下面的例子:

x = 1;
while(x)x = memory[0];
y = 1;

在程序编译时期编译器无法预知多核执行的动态性,因此编译器可以认为语句 y = 1 和 x 的值没有必然联系,因此 y = 1 可能比 x = 1 先执行,从而跳过循环,在编译器看来这并不违反语义。

第二种由硬件执行产生的指令重排序,在单核单线程程序中,程序的执行的顺序看起来应该总是和程序设计的顺序一致,但是在硬件实现的视角中,情况却完全不同,首先我们定义对于内存操作,有两种内存操作原语,即读内存和写内存。

我们定义一个完成一个读操作是瞬时原子的,因为读操作不需要通知其他线程,因此读操作一旦完成,其他线程默认均可见。但是写操作的情况却完全不同,一个写操作可能不是原子,这有点反直觉,但确实如此,一个写操作需要通知所有其他线程修改了写地址上的内容之后,一个写操作才算完成,称为写可见性。

如果读取了一个未完成的写操作,即写操作还没有对所有的线程都可见,此时就会发生读非原子的写操作,此时读写会发生重排序,通常情况下,处理器内核通常具有写缓冲区,例如下面的例子:

写缓冲区

程序依次发生的顺序为:

  1. 首先 P1 将 Flag1 = 1 写入缓冲区,此时 Flag1 = 1 对线程 P2 不可见。
  2. P2 将 Flag2 = 1 写入缓冲区,此时 Flag2 = 1 对线程 P1 不可见。
  3. P1 读取 Flag2 的值为 0,进入临界区。
  4. P2 读取 Flag1 的值为 0,进入临界区。
  5. Flag1 = 1 从写缓冲区写入内存。
  6. Flag2 = 1 从写缓冲区写入内存。

在这种情况下,因为写的非原子性,产生了乱序的读写,导致两个线程同时进入临界区。但是写缓冲区带来了屏蔽等待写入时间的效果(将指令执行的时间重叠)。

除了写缓冲区,硬件还有其他的实现,例如 Overlapped writes, Non−blocking reads 这些都会产生读写的重排序。

不同的 Relaxed Memory 内存模型允许不同情况的重排序,RISC-V 采用了 RVWMO 内存模型,方便软件产生最大程度上的优化,以及最大程度上的硬件实现自由。

程序顺序、全局内存顺序以及保留程序顺序

在多线程内存模型中,存在三种程序执行顺序,即程序顺序、全局内存顺序以及保留程序顺序。最容易理解的是程序顺序,一个程序顺序定义为程序在指令流中的顺序,也是程序设计者所编写的顺序,例如对于一个 Hart 看到的指令流:

(a) li t1, 1
(b) sw t1, 0(s0)
(c) lw a0, 0(s0)

则称在程序顺序中指令 (b) 先于指令 (c)

上文已经解释过假设有两个线程 A 和线程 B ,为什么线程 B 看到 A 的执行顺序与线程 A 的程序顺序完全不同。而全局内存顺序定义了所有线程所看到的内存的全局性事件列表,例如对于两个 Hart 的指令流:

Hart 0:
(a) li t1, 1
(b) sw t1, 0(s0)
Hart 1:
(c) li t1, 2
(d) sw t1, 0(s0)
(e) lw a0, 0(s0)

则对于线程0来说,一种可能的全局内存顺序为: (c)(d)(a)(b)(e) ,即在事件 (c)(d) 结束后,线程0观测到了线程1的 (a)(b) 发出的全局事件,因此指令 (e) 读取到的值为1。

保留程序顺序在原文中的定义为:

Preserved program order represents the subset of program order that must be respected within the global memory order.

简单的理解为,因为全局内存顺序无法保证和程序顺序相同,但是为了程序语义的正确,至少必须保证程序顺序的一个子集,称为保留程序顺序,在全局内存顺序中和程序顺序相同。

RISC-V RVWMO 模型定义了13条保留程序顺序的规则和3条公理,本文剩下的部分将讲解这13条规则和3条公理。

3条公理

加载值公理(Load Value Axiom)

加载值公理定义了一个具有 load 语义的指令可以返回值,原文如下:

Load Value Axiom: Each byte of each load i returns the value written to that byte by the store that is the latest in global memory order among the following stores:

  1. Stores that write that byte and that precede i in the global memory order
  2. Stores that write that byte and that precede i in program order

也就是说 load 指令可以返回的值必须是在全局内存顺序以及在当前线程程序顺序中的最后一条 store 指令所写入的值。

该公理引入 RVWMO 内存模型中对写原子性的规定,即一个线程可以读取自己一条还未完成的写入指令所写入的值,即 RVWMO 中的 store 指令是非原子写的(此时写可能对其他线程不可见)。这个规则允许硬件优化写入时间(非阻塞写),通常由一个写缓冲区(store buffer)实现,对于具体的写缓冲区的实现,读者可以参考计算机体系结构相关的书籍。

在 RISC-V 规范中给出了第一个示例:

示例1
上图的结果按时间顺序的解释为:

  • (a) 执行并将1写入自己的私有写缓冲区。
  • (b) 读取自己写缓冲区的值(store buffer forwarding)(之前 (a) 写入的)。
  • (c) 确保之前的读取已完成。
  • (d) 读取当前内存y的值为0。
  • (e) 执行并将1写入自己的私有写缓冲区。
  • (f) 读取自己写缓冲区的值(之前 (e) 写入的)。
  • (g) 确保之前的读取已完成。
  • (h) 读取当前内存x的值为0。
  • (a) 写缓冲区将1写入内存x。
  • (e) 写缓冲区将1写入内存x。

接下来规范中给出的一个更加极端的例子:

示例2
对于结果的解释为:

  • 线程1认为控制性依赖 (d) 循环不会对 (e)(f)(g) 产生影响,因为地址不同(即使其他线程写入了相同的地址,但是线程1无法预测是否真的有其他线程在写入),因此 (e)(f)(g) 允许先比 (d) 执行,也不会改变当前线程的程序顺序的语义,此时 (e) 写入自己的私有写缓冲区z=1。
  • (f) 读取自己写缓冲区的值(之前 (e) 写入的)。
  • (g) 读取内存中的x值为0。
  • (a) 执行写入私有写缓冲区x=1。
  • (b) 确保私有写缓冲区的任务已经全部写入,将x=1写入内存。
  • (c) 执行写入私有写缓冲区y=1,之后将y=1写入内存。
  • (d) 执行成功,读取到1,跳出循环。
  • (e) 预测成功,将z=1从缓冲区写入内存。

原子性公理(Atomicity Axiom)

Atomicity Axiom (for Aligned Atomics): If r and w are paired load and store operations generated by aligned LR and SC instructions in a hart h, s is a store to byte x, and r returns a value written by s, then s must precede w in the global memory order, and there can be no store from a hart other than h to byte x following s and preceding w in the global memory order.

RISC-V 提供了两种原子性指令,一种是 AMO ,AMO 把读和写两种操作打包为一种原子性操作,因此该指令本身就是原子的,而另外一种 LR/SC 指令则不同,该公理说明了什么是成对的 LR/SC (原子性),只有成对的 LR/SC , SC 操作才能成功。

该公理说若在线程 h 中 LRSC 是成对的,那么在两者之间除了线程 h 可以修改被保留的地址为,其他线程在全局内存序中均不允许修改。

但是,该公理允许在全局内存序上有下面的操作:

  1. 其他线程或线程 h 读取或修改了非 LR/SC 重叠的地址上的内存。
  2. 允许线程 h 读取或修改 LR/SC 重叠的地址上的内存。
  3. LR 和 SC 不必成对出现,可以嵌套。

第一条比较好理解,对于第二条,下面的例子中的 LR/SC 可能成对:

(a) lr.d a0, 0(s0)
(b) sd t1, 0(s0)
(c) sc.d t2, 0(s0)

对于第三条,下面的例子中的 LR/SC 可能成对:

(a) lr.w a0, 0(s0)
(b) sw t1, 4(s0)
(c) sc.w t2, 8(s0)

其中 (c) 可能和之前的 LR 成对, (a) 不影响 (c) 是否成功。

渐近性公理(Progress Axiom)

原文如下:

Progress Axiom: No memory operation may be preceded in the global memory order by an infinite sequence of other memory operations.

渐近性公理较好理解,它确保了一个内存操作应该在有限的时间内完成(被所有的线程可见)。如果渐近性公理不成立,那么用户将无法实现自旋锁等一些同步操作,因为可能一次写入永远都不会对其他线程可见。

如果一个具有缓存的系统,应该通过缓存一致性协议来确保在有限的时间内,内存操作对所有线程可见。

13条规则

在介绍13条规则之前,我们首先制定一个通用约定:

  1. 指令 a 和指令 b 都是内存操作指令(而不是IO区域)
  2. 在程序顺序中, a 先于 b
  3. ab 满足下面任意一条规则,则必须保证在保留程序顺序(当然也在全局内存顺序)中, a 也先于 b

重叠地址顺序(Overlapping-Address Orderings)(1-3)

Rule 1: b is a store, and a and b access overlapping memory addresses

规则1说若b是一个 store 指令,并且 ab 有重叠的地址,那么 a 就不能排到 b 后面。这是显然的,因为若调换两个指令的执行顺序,则连程序顺序语义都得不到保证。但是若 a 是一个 store 指令 b 是一个 load 指令,则不受该规则约束, load 可以先于 store 出现在全局内存序中,因为硬件通常需要实现写缓冲区。

Rule 2: a and b are loads, x is a byte read by both a and b, there is no store to x between a and b in program order, and a and b return values for x written by different memory operations

规则2说若 ab 都是 load 指令,并且 ab 有重叠的地址,并且在程序顺序中 ab 之间没有地址重叠的 store 指令,并且 ab 的值是由不同 store 指令写入的。这条规则十分抽象,但至少应遵守的一条规则是一个新的 load 指令不应该比一个旧的 load 指令返回一个更旧的值,这条规则被称为 CoRR (Coherence for Read-Read pairs)规则。

规范中给出了两个具体的例子分别解释了规则2的第二句话和第三句话,首先看例子1:

例子1
给出的结果解释是:

  • (d) 指令由于某种原因暂停了执行。
  • (e) 执行,并放到写缓冲区。
  • (f) 执行并读取写缓冲区的值。
  • (g)(h)(i) 分别按顺序执行。
  • (a)(b)(c) 按顺序执行,并将 x=1 和 y=1 依次从写缓冲区写入内存。
  • (d) 指令跳过写缓冲区,读取内存中y=1。
  • (e) 将写缓冲区的值写回内存。

对应的全局内存顺序为 (f)(i)(a)(c)(d)(e) ,注意到虽然 (f) 排在 (d) 之前,但是 (f) 却比 (d) 读取的值要新,因此不违反 CoRR 规则,在 (f)(d) 之间夹入了一条在程序顺序上和两者地址重合的 load 指令,该重排序是合法的。

读者可能疑惑,在全局内存顺序中 load 指令的返回值并不遵循全局内存顺序的读写顺序,毕竟 load 的返回值和全局内存顺序并无直接关系,全局内存顺序无法唯一的决定 load 的返回值,具体详见上文加载值公理。

第二个例子:

例子2
假设 z 的值已经被其他线程写入v,若全局内存序为 (h)(k)(a)(c)(d)(g) ,注意虽然 (h)(g) 之前,但是 (h) 并没有比 (g) 读到更旧的值,因此也不违反 CoRR 规则。

但是,假设 z 的值已经被其他线程写入v不成立, (g)(h) 可能读到不同的值,此时必须要求 (g)(h) 前执行,即值是由不同 store 指令写入的。

Rule 3: a is generated by an AMO or SC instruction, b is a load, and b returns a value written by a

规则3定义了 AMO 和 SC 指令的原子性,若 a 是 AMO 或者 SC 指令, bload 指令,并且 b 返回由 a 写入的值。上文说过 RVWMO 允许普通的 store 非原子性写,但是 AMO 和 SC 指令必须具有原子性写,在 a 对所有线程可见之前,b 都不应该读取 a 写入的值。

最后需要提醒读者的一点是,重叠地址指的是部分重叠就算是重叠,因为存在不同位宽的内存操作指令。

内存栅栏(Fences)(4)

Rule 4: There is a FENCE instruction that orders a before b

该规则比显然,一个 FENCE 指令指定了全局内存序的要求,该指令具有八个可以设置的位,指定了在内存栅栏前的需要定序的指令和之后需要定序的指令,具体请参考 FENCE 指令说明。

显式同步(Explicit Synchronization)(5-8)

Rule 5: a has an acquire annotation

规则5规定了一个“获取”语义,正如字面意思“获取”经常是获取锁进入临界区(Critical section),因此不能把获取锁之后的操作拿到获取锁之前来执行,否则就会违反临界区原则。

RISC-V 提供了两种实现“获取”语义的方法,一种是原子指令的 aq 位,另外一种由 FENCE R,RW 提供。

例子1
上图中的例子使用 aq 位实现“获取”语义,注意 aq 位规定临界区内的指令不能在临界区外(之前)执行,并没有规定临界区外(之前)的指令能不能在临界区内执行,因此两个无关的 storeload 指令在临界区内执行是允许的。

例子2
上图中使用 FENCE R,RW 定序临界区,和 aq 位不同的是,该指令具有额外的要求,无关的 ld 指令不能再临界区内执行,相比来看 aq 位具有更贴近的“获取”语义,允许硬件有额外的优化。

Rule 6: b has a release annotation

规则6和规则5相似,只是位置不同,一个“释放”语义,通常是释放锁,不允许临界区内的操作在临界区外执行(之后),同样也提供了2中方案,一是设置原子指令的 rl 位,另外是使用 FENCE RW,W 定序。

Rule 7: a and b both have RCsc annotations

一个 RCsc 记号指的是一个 AMO 、LR或是SC指令具有 aq 或是 rl 标记。具有该记号的指令不能被重排序,规则5、6、7共同组成了临界区规则。

因为 RVWMO 目前还没有 RCpc 记号的指令,因此关于 RCpc 和 RCsc 的细节请见后文参考资料。

Rule 8: a is paired with b

规则8目前成对的指令只有 LR 和 SC 指令,规定 LR 和 SC 不能重排序。

句法依赖(Syntactic Dependencies)(9-11)

Rule 9: b has a syntactic address dependency on a
Rule 10: b has a syntactic data dependency on a
Rule 11: b is a store, and b has a syntactic control dependency on a

规则9和10从直觉上来讲是合理的。 a 指令确定了 b 指令的地址,或者执行 b 需要 a 的结果,又或者是 bstore 指令, a 控制 b 的执行,那么 a 指令就需要在 b 指令执行之前执行。

需要强调的是,这里的依赖指的是句法依赖,而不是语义依赖,换句话说,即使重排序从结果上是正确的,但是如果是句法依赖,该规则也不允许重排序,例如:

例子
上图中,尽管 xor a2, a1, a1 的值永远是0,重排序两个 load 指令从语义上看起来没问题,但是句法依赖却不允许这种重排序。该规则提供一个轻量级的定序语义,相反若使用 FENCE R,R 则会定序所有的 load 指令。

对于控制依赖也具有相同的句法依赖性,例如:

例子1
尽管 next 标签总是执行,但也存在对于第一条指令的依赖,同理:

例子2
尽管 bne 指令从语义上讲没有实际的作用效果,但是 next 标签的指令也存在对于第一条指令的依赖。

SC 指令的成功和失败也会产生依赖:

例子

假设不规定SC 指令的依赖,则一种可能的执行结果是,线程为了保证 (b)(c) 成对执行成功,先让 (d)(e)(f)(a) 执行,此时 (c) 可以提前返回 0,然后执行 (b)(c) 其中 (c) 执行成功。规则11不允许这样的执行。

其他例如 CSR 指令、浮点指令也存在依赖情况,具体请参考规范说明。

流水线依赖(Pipeline Dependencies)(12-13)

Rule 12: b is a load, and there exists some store m between a and b in program order such that m has an address or data dependency on a, and b returns a value written by m

流水线依赖描述了一种传递性依赖,规则12指明了 load 只有当之前的 store 指令的地址和数据都确定了, load 才可以提前读 store 指令写入的值。

例子1
这里例子中 (d) 一定是比 (f) 先执行的,只有执行了 (d) 那么 (e) 的数据才准备好, (f) 才能执行,这一般受限于处理器设计中的流水线依赖,因此叫做流水线依赖。

下面的例子情况则完完全不同:

例子2

虽然上一个例子 (d)(e)(g) 之间有流水线依赖,但是这里例子中额外的 (f) 打破了这种依赖, (f) 的地址和数据可以先准备好, (g) 允许读 (f) 写入的数据。

Rule 13: b is a store, and there exists some instruction m between a and b in program order such that m has an address dependency on a

规则13说若 b 是一个 store 指令,并且 ab 在程序顺序中存在其他指令的地址依赖于 a 的结果,那么 ab 不能重排序,看下面的例子:

例子
(f)(d) 先执行,存在一种情况当 a1 = s0 的时候,此时 (e) 无法读取到在 (f) 之前写入的值了,因为这样违反加载值公理。因此 (d) 一定比 (f) 先执行。

IO内存

对于内存和IO内存,RISC-V 定义了物理内存属性(Physical Memory Attributes,PMAs)来描述一段内存区域,具体可参考 RISC-V 特权规范。

对于IO内存,加载值公理和原子性公理不再适用,在IO内存中,读操作可能有明显的副作用(side effects),并且读操作的值可能不是由 store 指令写入的,这取决于IO设备。但是IO内存依然遵循下面的保留程序顺序:

  1. ab 依旧遵循13条保留程序顺序规则,但是 ab 要么都是内存操作,要么都是 IO 操作,不能一个是内存操作一个是 IO 操作。
  2. ab 对重叠的 IO 地址进行操作。
  3. ab 访问相同的强顺序IO区域(strongly-ordered)。
  4. ab 访问的IO区域,有一个通道是通道1。
  5. ab 访问相同的IO区域通过相同的通道,除了通道0。

RISC-V 的 FENCE 指令区别 IO 操作和内存操作,处理器实现应该总是假设,当设备收到操作指令之后,设备可能立刻访问之前的内存操作,此时就需要 FENCE 指令进行定序:

实例

假设 0(a1) 是一个IO地址,而 0(a0) 是一个内存地址,为了确保设备能够正确读取第一条指令存入的值,需要使用 FENCE W, O 定序。

参考资料

  1. S. V. Adve and K. Gharachorloo. Shared memory consistency models: A tutorial.
    Computer, 29(12):66–76, 1996.
  2. A. Waterman and K. Asanović, editors. The RISC-V Instruction Set Manual,
    Volume I: Unprivileged ISA, Version 20191213. December 13, 2019.

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

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

相关文章

10.多柱状图(MuliBarChart)

愿你出走半生,归来仍是少年&#xff01; 环境&#xff1a;.NET 7、MAUI 话接上回&#xff08;9.单柱状图&#xff08;SingleBarChart&#xff09;&#xff09;&#xff0c;从单柱拓展到多柱状图。 1.数据设置 private void InitValue(List<BasicSerieDto> dtos){Serie…

14.5 Flash查询和添加数据库数据

14.5 Flash查询和添加数据库数据 在Flash与数据库通讯的实际应用中&#xff0c;如何实现用户的登录与注册是经常遇到的一个问题。登录实际上就是ASP根据Flash提供的数据查询数据库的过程&#xff0c;而注册则是ASP将Flash提供的数据写入数据库的过程。 1.启动Access2003&…

C#,获取与设置Windows背景图片的源代码

为了满足孩子们个性化桌面的需求。 这里发布获取与设置Windows背景图片的源代码。 1 文本格式 using System; using System.IO; using System.Data; using System.Linq; using System.Text; using System.Drawing; using System.Collections; using System.Collections.Gene…

JeecgBoot集成TiDB,打造高效可靠的数据存储解决方案

TiDB简介 TiDB是PingCAP公司自主设计、研发的开源分布式关系型数据库&#xff0c;同时支持在线事务处理与在线分析处理 (Hybrid Transactional and Analytical Processing, HTAP) 的融合型分布式数据库产品&#xff0c;具备水平扩容或者缩容、金融级高可用、实时 HTAP、云原生…

基数排序算法

1. 排序算法分类 十种常见排序算法可以分为两大类&#xff1a; 比较类排序&#xff1a; 通过比较来决定元素间的相对次序&#xff0c;由于其时间复杂度不能突破O(nlogn)&#xff0c;因此也称为非线性时间比较类排序。比较类排序算法包括&#xff1a;插入排序、希尔排序、选择…

第139期 做大还是做小-Oracle名称哪些事(20240125)

数据库管理139期 2024-01-25 第139期 做大还是做小-Oracle名称哪些事&#xff08;20240125&#xff09;1 问题2 排查3 扩展总结 第139期 做大还是做小-Oracle名称哪些事&#xff08;20240125&#xff09; 作者&#xff1a;胖头鱼的鱼缸&#xff08;尹海文&#xff09; Oracle A…

SQL - 事务控制

SQL - 事务控制 文章目录 SQL - 事务控制TCL - 事务事务的边界事务的特性事务的应用 事务隔离等级MySQL支持四种隔离级别 TCL - 事务 **模拟场景&#xff1a;**生活当中转账是转账方账户扣钱&#xff0c;收账方账户加钱。用数据库操作来模拟现实转账。 数据库模拟&#xff1a…

CI/CD

介绍一下CI/CD CI/CD的出现改变了开发人员和测试人员发布软件的方式,从最初的瀑布模型,到最后的敏捷开发(Agile Development),再到今天的DevOps,这是现代开发人员构建出色产品的技术路线 随着DevOps的兴起,出现了持续集成,持续交付和持续部署的新方法,传统的软件开发和交付方…

软件设计师——软件工程(五)

&#x1f4d1;前言 本文主要是【软件工程】——软件设计师——软件工程的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是听风与他&#x1f947; ☁️博客首页&#xff1a;CSDN主页听风与他 &#x1f304…

安全防御综合组网实验

题目 要求 生产区在工作时间可以访问服务器区&#xff0c;仅可以访问http服务器。办公区全天可以访问服务器区&#xff0c;其中10.0.2.20 可以访问FTP服务器和http服务器。10.0.2.10仅可以ping通10.0.3.10。办公区在访问服务器区时采用匿名认证的方式进行上网行为管理。办公区…

【RabbitMQ】死信(延迟队列)的使用

目录 一、介绍 1、什么是死信队列(延迟队列) 2、应用场景 3、死信队列(延迟队列)的使用 4、死信消息来源 二、案例实践 1、案例一 2、案例二&#xff08;消息接收确认 &#xff09; 3、总结 一、介绍 1、什么是死信队列(延迟队列) 死信&#xff0c;在官网中对应的单词…

Unity学习之坦克游戏制作(2)游戏场景的制作

文章目录 1. 基础场景的搭建2. 游戏主面板2.1 拼出面板2.2 创建新面板2.3 设置面板复用2.4 退出界面 3. 坦克基类3.1 创建基类脚本3.1.1 基类基本属性3.1.2 抽象开火函数3.1.3 受伤虚函数3.1.4 死亡虚函数 4 玩家——基础移动旋转摄像机跟随4.1 玩家对象脚本4.2 控制坦克移动4.…

移动端打包成功后禁止生成 report.html 文件,并不自动打开该文件

目录 【问题】移动端 npm run build 打包后生成并打开 report.html 文件package.json 文件vue.config.js 代码 【解决】打包后去除 report.html 文件vue.config.js 代码 参考 【问题】移动端 npm run build 打包后生成并打开 report.html 文件 package.json 文件 {"name&…

蓝牙----蓝牙协议栈L2CAP

蓝牙协议栈----L2CAP L2CAP的功能术语介绍L2CAP信道L2CAP的工作模式经典蓝牙的分段和分解过程 L2CAP—逻辑链路控制和适配层协议 L2CAP的功能 经典蓝牙的L2CAP层实现了协议复用、数据分段与重组、封装调度等操作。BLE的L2CAP层是经典蓝牙L2CAP层的简化版本&#xff1a; 在基…

爬虫js逆向分析——x平台(实现)

爬虫js逆向分析——x平台&#xff08;实现&#xff09; &#xff08;仅供学习&#xff0c;本案例只是分析流程没有账号&#xff09;网址&#xff1a;https://xuexi.chinabett.com/ 1.分析请求包格式 打开控制台&#xff0c;并勾选保存日志&#xff0c;然后点击登录看发送了什…

ICSpector:一款功能强大的微软开源工业PLC安全取证框架

关于ICSpector ICSpector是一款功能强大的开源工业PLC安全取证框架&#xff0c;该工具由微软的研究人员负责开发和维护&#xff0c;可以帮助广大研究人员轻松分析工业PLC元数据和项目文件。 ICSpector提供了方便的方式来扫描PLC并识别ICS环境中的可疑痕迹&#xff0c;可以用于…

Unity配置表xlsx/xls打包后读取错误问题

前言 代码如下&#xff1a; //文本解析private void ParseText(){//打开文本 读FileStream stream File.Open(Application.streamingAssetsPath excelname, FileMode.Open, FileAccess.Read, FileShare.Read);//读取文件流IExcelDataReader excelRead ExcelReaderFactory…

vue实现甘特图

目录 实现效果 一、安装依赖 二、使用 二、绕过license 实现效果 一、安装依赖 npm i --save vue-gantt-schedule-timeline-calendar 实现甘特图需先安装上述依赖&#xff0c;安装依赖实际上是通过gantt-schedule-timeline-calendar来实现的。所以node_module中因包含以下…

JQuery下载和一些语法

最近学了六种jQuery选择器&#xff0c;我想把学过案例和知识结合起来&#xff0c;给大家分享下&#xff01; 那么既然学jQuery选择器&#xff0c;肯定要先了解下jQuery是什么吧&#xff01;jQuery是一个快速、简洁的JavaScript框架&#xff0c;相当于用jQuery能更加高效的创建…

【快影】怎么制作卡拉OK字幕

您好&#xff0c;您添加了字幕之后可以添加动画&#xff0c;选择卡拉OK&#xff0c;其中 卡拉OK1是支持修改颜色的&#xff0c;卡拉OK2只支持修改文字的底色。