一种路径敏感的数据依赖分析算法

news2024/11/17 9:26:17

Falcon

  • 1.方法
    • 1.1.Basic Rule
    • 1.2.改进算法
    • 1.3.跨函数分析
  • 2.Evaluation
    • 2.1.设置
    • 2.2.value-flow分析
    • 2.3.Thin Slicing
    • 2.4.Bug Detection
  • 参考文献

这篇工作发表于PLDI 24,提出了一种context- 以semi-path-sensitive的数据依赖分析算法,解决path-sensitive的内存模型中存在的aliasing-path-explosion问题。

1.方法

现有sparse flow-sensitive pointer analysis缺点:

  • 1.auxiliary pre-analysis的精度缺失会导致后续分析大量冗余错误pointer-information的传播。

  • 2.auxiliary pre-analysis只用到了一个point-to集合,而随后的flow-sensitive分析在处理path-sensitive问题时后point-to集合会出现路径爆炸。

为了解决这些问题作者提出了Falcon,Falcon处理的程序语法如下:

请添加图片描述

1.1.Basic Rule

作者定义了两个集合 E E E S S S,还定义了对应的查询方式 ∏ φ \prod_{\varphi} φ

  • E [ p → { . . . } ] E[p \rightarrow \{...\}] E[p{...}] 表示top-level variable p p p 的指向环境集合,每一个环境为 ( φ , o ) (\varphi, o) (φ,o) 表示一个address-taken object及其对应的路径条件。

  • S [ o → { . . . } ] S[o \rightarrow \{...\}] S[o{...}],每一个元素 ( π , l , q ) (\pi, l, q) (π,l,q) 表示top-level variable q q q 在语句 l l l 处路径条件为 π \pi π 时赋值给了 o o ostore 指令)。比如语句 l : ∗ x = q l: *x = q l:x=q,该路径条件 π \pi π x x x 只指向 o o o,那么有 S ( o ) = S ( o )    ∪    ( π , l , q ) S(o) = S(o) \; \cup \; (\pi, l, q) S(o)=S(o)(π,l,q)

  • ∏ φ ( S ( o ) ) = { ( π ∧ φ , l , v )    ∣    ( π , l , v ) ∈ S ( o ) } \prod_{\varphi}(S(o)) = \{(\pi \land \varphi, l, v) \; | \; (\pi, l, v) \in S(o)\} φ(S(o))={(πφ,l,v)(π,l,v)S(o)} 表示address-taken variable o o o 在路径条件 φ \varphi φ 下可能的值。这里值用top-level variable表示,也就是存在value-flow v → φ o v \stackrel{\varphi}{\rightarrow} o vφo

  • ∏ φ ( E ( v ) ) = { ( π ∧ φ , o )      ∣    ( π , o ) ∈ E ( v ) } \prod_{\varphi}(E(v)) = \{(\pi \land \varphi, o)\ \; | \; (\pi, o) \in E(v)\} φ(E(v))={(πφ,o) (π,o)E(v)} 表示top-level variable 在路径条件 π \pi π 下的指向集合。

  • E ( v ) ⨄ E ′ ( v ′ ) = { ( π    ∨    π ′ , o )    ∣    ∀ ( π , o ) ∈ E ( v ) , ∀ ( π ′ , o ) ∈ E ′ ( v ′ ) } E(v) \biguplus E^{'}(v^{'}) = \{(\pi \; \vee \; \pi^{'}, o) \; | \; \forall (\pi, o) \in E(v), \forall(\pi^{'}, o) \in E^{'}(v^{'})\} E(v)E(v)={(ππ,o)(π,o)E(v),(π,o)E(v)},表示两个 E E E 集合合并,一个应用的地方就是 Φ \Phi Φ 指令处合并不同的value时,不过关于具体操作paper里说的不是很明确,个人理解是如果 o o o 在两个 E E E 集合处都出现了,那么合并路径条件 π    ∨    π ′ \pi \; \vee \; \pi^{'} ππ,反之则相当于 π    ∨    f a l s e \pi \; \vee \; false πfalse 或者 f a l s e ∨    π ′ false \vee \; \pi^{'} falseπ

基础的transfer function如下, E , S ⊢ l , φ : s t m t : E ′ , S ′ E, S \vdash l, \varphi : stmt : E^{′}, S^{′} E,Sl,φ:stmt:E,S 表示 E , S E, S E,S 集合经过路径条件 φ \varphi φ 下的语句 l l l 后更新为 E ′ , S ′ E^{′}, S^{′} E,S,其中:

  • alloca 指令 KaTeX parse error: Expected 'EOF', got '&' at position 5: p = &̲a p p p E E E 集合添加 { φ , a l l o c a a } \{\varphi, alloca_a\} {φ,allocaa}

  • store 指令 ∗ x = q *x = q x=q 更新了 p t s ( x ) pts(x) pts(x) 每个address-taken variable的 S S S 集合。往已有的 S ( o ) S(o) S(o) 集合中添加 ( π , l , q ) (\pi, l, q) (π,l,q) 表示value-flow关系 q → π o q \stackrel{\pi}{\rightarrow} o qπo。同时只有 store 指令会更新 S S S 集合的值,其它只更新 E E E 集合。

  • SequencingBranching 规则表明Falcon可能通过遍历CFG实现(paper没有明说),由于CFG遍历很费时,为了跳过不必要的CFG node,作者后面提出了一个优化方案。

  • load, phi, copy 指令则负责传播指针集合 E E E 的值。其中 load 的最为复杂,个人理解是对于 l , φ : p = ∗ y l, \varphi: p = *y l,φ:p=y,首先 ∏ φ ( E ( y ) ) = { ( π , o ) , . . . } \prod_\varphi(E(y)) = \{(\pi, o),...\} φ(E(y))={(π,o),...} 查询出满足路径条件的所有指向address-taken variable ( π , o ) (\pi, o) (π,o)(此时存在value-flow o → π p o \stackrel{\pi}{\rightarrow} p oπp),由于 load 指令主要是更新 p p p 的指向集 E ( p ) E(p) E(p),因此需要先查询每个 ( π , o ) (\pi, o) (π,o) 的所有值 ∏ π ( S ( o ) ) \prod_\pi(S(o)) π(S(o))(找出所有的 v → φ o v \stackrel{\varphi}{\rightarrow} o vφo),这样找到了value-flow v → φ p v \stackrel{\varphi}{\rightarrow} p vφp,随后合并所有 v v v 的指向集合更新 E ( p ) E(p) E(p)

请添加图片描述

value flow的构建规则如下图所示(value-flow只存在于 store --> load),对于语句 l 1 l_1 l1 处路径条件为 φ 1 \varphi_1 φ1 store 指令 ∗ x = q *x = q x=q,以及语句 l 2 l_2 l2 处路径条件 φ 2 \varphi_2 φ2load 指令 p = ∗ y p = *y p=y,查询路径条件 φ 2 \varphi_2 φ2 y y y 指向的所有address-taken variable ( π i , o i ) (\pi_i, o_i) (πi,oi),通过 ∏ φ i ( S ( o i ) ) \prod_{\varphi_i}(S(o_i)) φi(S(oi)) 查找 φ i \varphi_i φi o i o_i oi 所有可能的值 ( ( φ i , l 1 , q ) (\varphi_i, l_1, q) (φi,l1,q)),也就是value-flow q → φ i o i q \stackrel{\varphi_i}{\rightarrow} o_i qφioi,最后合并所有同source value-flow的路径条件,也就是构造value-flow边 q → ∨ φ i p q \stackrel{\vee \varphi_i}{\rightarrow} p qφip

请添加图片描述
以下图为例,(b)为传统value-flow构建方法, (c)为Falcon,以处理从 store ∗ x = a *x = a x=aload d = ∗ x d = *x d=x 为例,存在 E ( x ) = { ( φ 1 , o 1 ) , ( ¬ φ 1 , o 2 ) } E(x) = \{(\varphi_1, o_1), (\lnot \varphi_1, o_2)\} E(x)={(φ1,o1),(¬φ1,o2)} S ( o 1 ) = { φ 1 , l 1 , a } S(o_1) = \{\varphi_1, l_1, a\} S(o1)={φ1,l1,a} S ( o 2 ) = { ¬ φ 1 , l 2 , a } S(o_2) = \{\lnot \varphi_1, l_2, a\} S(o2)={¬φ1,l2,a} l 1 , l 2 l_1, l_2 l1,l2是占位符)。处理 d = ∗ x d = *x d=x 时query ∏ ¬ φ 2 ( E ( x ) ) \prod_{\lnot \varphi_2}(E(x)) ¬φ2(E(x)),发现 φ 1 ∧ ¬ φ 2 \varphi_1 \land \lnot \varphi_2 φ1¬φ2 ¬ φ 1 ∧ ¬ φ 2 \lnot\varphi_1 \land \lnot \varphi_2 ¬φ1¬φ2 都能满足,因此得到 ∏ ¬ φ 2 ( E ( x ) ) = { ( φ 1 , o 1 ) , ( ¬ φ 1 , o 2 ) } \prod_{\lnot \varphi_2}(E(x)) = \{(\varphi_1, o_1), (\lnot \varphi_1, o_2)\} ¬φ2(E(x))={(φ1,o1),(¬φ1,o2)},接着分别query ∏ φ 1 ( S ( o 1 ) ) \prod_{\varphi_1}(S(o_1)) φ1(S(o1)) ∏ ¬ φ 1 ( S ( o 2 ) ) \prod_{\lnot \varphi_1}(S(o_2)) ¬φ1(S(o2)) 得到 { φ 1 ∧ ¬ φ 2 , l 1 , a } \{\varphi_1 \land \lnot \varphi_2, l_1, a\} {φ1¬φ2,l1,a} { ¬ φ 1 ∧ ¬ φ 2 , l 2 , a } \{\lnot \varphi_1 \land \lnot \varphi_2, l_2, a\} {¬φ1¬φ2,l2,a}。最后得到value-flow a ⟶ ¬ φ 2 d a \stackrel{\lnot \varphi_2}{\longrightarrow} d a¬φ2d(路径条件简化)。与之相比传统方法的value-flow边就多多了。

请添加图片描述

1.2.改进算法

不过目前上图的算法还有优化空间,主要原因包括:

  • 1.直接在CFG上按上面规则进行传播开销过大。而Sparse分析的pre-analysis构建的SVFG包括太多false def-use。因此也会降低性能。

  • 2.大量guard的路径条件需要更新,可能会引起路径爆炸问题,如果严格的对路径条件求解开销过大。

针对问题1作者提出了一个CFG优化方案,主要针对 storeload 的遍历。算法如下图所示(Algo1为 store,Algo2为 load)。优化的重点是 S S S 集合的访问,首先,store 会修改 S S S 的值而 load 会读取其值。

针对问题1,作者优化了 storeload 的遍历规则,其中将 S ( o ) S(o) S(o) 替换为一系列 S l ( o ) S_l(o) Sl(o),表示每个指令 l l l 处address-taken variable o o o 的值。改进后的算法如下图所示,红框为改变处,主要是处理 store 指令时会顺便处理其所有的支配边界,处理 load 时会沿着其直接支配节点回溯。这里需要先进行控制依赖分析。

请添加图片描述
一个示例如下图所示:

  • 在处理 store 语句 l 4 : ∗ x = d l_4: *x = d l4:x=d 时,更新完 S l 4 ( a l l o c m ) S_{l_4}(alloc_m) Sl4(allocm) { ( φ , l 4 , d ) } \{(\varphi, l_4, d)\} {(φ,l4,d)} 后,接着找到 l 4 l_4 l4 的支配边界 l 6 l_6 l6,更新 S l 6 ( a l l o c m ) = { φ , l 4 , d } S_{l_6}(alloc_m) = \{\varphi, l_4, d\} Sl6(allocm)={φ,l4,d}。同理,访问 l 5 l_5 l5 也会做对应的更新。

  • 在处理 load 语句 l 6 : f = ∗ x l_6: f = *x l6:f=x 时, x x x 指向 a l l o c m alloc_m allocm,首先读取 S l 6 ( a l l o c m ) S_{l_6}(alloc_m) Sl6(allocm) 的值,获的 ( φ , l 4 , d ) (\varphi, l_4, d) (φ,l4,d),表示value-flow d ⟶ φ f d \stackrel{\varphi}{\longrightarrow} f dφf;随后,追溯到 l 6 l_6 l6 的直接支配节点 l 3 l_3 l3,读取 ( t r u e , l 3 , c ) (true, l_3, c) (true,l3,c) 的值后,根据Algo2第9行 φ = π ∧ σ ∧ β \varphi = \pi \land \sigma \land \beta φ=πσβ 和第14行 σ = ¬ π ∧ β \sigma = \lnot \pi \land \beta σ=¬πβ 的规则更新为 ( ¬ φ , l 3 , c ) (\lnot \varphi, l_3, c) (¬φ,l3,c),表示value-flow c ⟶ ¬ φ f c \stackrel{\lnot \varphi}{\longrightarrow} f c¬φf c c c 会在 φ \varphi φ 下被kill掉)。根据这两个value-flow更新 E ( f ) E(f) E(f)

请添加图片描述

针对问题2,作者将程序的表达式都抽象为bool skeleton,比如将 x < 2, x >=2, y = 100/2 抽象为布尔谓词 p p p, ¬ p \lnot p ¬p, q q q。其次,Falcon并不使用功能齐全的SAT求解器,而是采用几种线性时间的半决策程序,如unit-propagation,用于识别“简单”的不可满足约束,并执行轻量级的逻辑简化,如消除重言式。在实验中,作者发现约70%的路径条件是可满足的。对于其余的路径条件,其中80%是简单约束,可以通过半决策程序解决。value-flow边的修剪和合并例子如下图所示。

请添加图片描述

1.3.跨函数分析

与SVF不同,作者依然采用采用一个按照call-graph的拓扑序自底向上先构造每个函数的Value-Flow Graph然后以summary-based方法先构造整体value-flow graph,也就是先独立分析每个函数生成summary,然后按需获取summary进行进一步分析。

通常summary需要注意的是side-effect,对于下图(a)所示代码片段,在对 foo 函数生成摘要时,通常会假设参数 y 指向一个address-taken variable o。按照作者的设定,foo 初始处有 E ( y ) = { ( t r u e , o ) } E(y) = \{(true, o)\} E(y)={(true,o)},分析完整个程序后, S ( o ) = { ( φ , l 2 , c ) , ( ¬ φ , l 3 , a ) } S(o) = \{(\varphi, l_2, c), (\lnot \varphi, l_3, a)\} S(o)={(φ,l2,c),(¬φ,l3,a)}。由于 y y yfoo 和其 caller 的接口,因此 foo 的side-effect只涉及到 y y y,因此 E ( y ) E(y) E(y) S ( o ) S(o) S(o) 就是需要的side-effect。

请添加图片描述
summary最终会在分析call语句的时候用到,这里 quxbar 函数都调用了 foo,同时 quxbar 的参数和 bar 一致,假设在 qux 的调用处, E ( x ) = { ( φ 1 ′ , o 1 ) , ( φ 2 ′ , o 2 ) , ( φ 3 ′ , o 3 ) } E(x) = \{(\varphi^{'}_1, o_1), (\varphi^{'}_2, o_2), (\varphi^{'}_3, o_3)\} E(x)={(φ1,o1),(φ2,o2),(φ3,o3)},那么将summary展开后就有了 S ( o 1 ) = { ( φ ∧ φ 1 ′ , l 2 , c ) , ( ¬ φ ∧ φ 1 ′ , l 3 , a ) } S(o_1) = \{(\varphi \land \varphi^{'}_1, l_2, c), (\lnot \varphi \land \varphi^{'}_1, l_3, a)\} S(o1)={(φφ1,l2,c),(¬φφ1,l3,a)}, S ( o 2 ) = { ( φ ∧ φ 2 ′ , l 2 , c ) , ( ¬ φ ∧ φ 2 ′ , l 3 , a ) } S(o_2) = \{(\varphi \land \varphi^{'}_2, l_2, c), (\lnot \varphi \land \varphi^{'}_2, l_3, a)\} S(o2)={(φφ2,l2,c),(¬φφ2,l3,a)}, S ( o 3 ) = { ( φ ∧ φ 3 ′ , l 2 , c ) , ( ¬ φ ∧ φ 3 ′ , l 3 , a ) } S(o_3) = \{(\varphi \land \varphi^{'}_3, l_2, c), (\lnot \varphi \land \varphi^{'}_3, l_3, a)\} S(o3)={(φφ3,l2,c),(¬φφ3,l3,a)}。如果调用有多层,那么摘要数量可能指数爆炸。

针对这个问题,作者引入了一个辅助变量 R R R,上面 foo 的摘要变成 E ( y ) = { ( t r u e , o ) } E(y) = \{(true, o)\} E(y)={(true,o)}, S ( o ) = { ( t r u e , l 4 , R ) } S(o) = \{(true, l_4, R)\} S(o)={(true,l4,R)}, R = { ( φ , l 2 , c ) , ( ¬ φ , l 3 , a ) } R = \{(\varphi, l_2, c), (\lnot \varphi, l_3, a)\} R={(φ,l2,c),(¬φ,l3,a)}。具体的构造方法如下图所示,在添加辅助变量的同时,对于 void 返回类型,算法还会添加一个额外返回值,示例如上图(b)所示

请添加图片描述
假设分析 foo

  • 独立分析完 foo 后有: E ( y ) = { ( t r u e , o ) } E(y) = \{(true, o)\} E(y)={(true,o)}, S ( o ) = { ( φ , l 2 , c ) , ( ¬ φ , l 3 , a ) } S(o) = \{(\varphi, l_2, c), (\lnot \varphi, l_3, a)\} S(o)={(φ,l2,c),(¬φ,l3,a)}, E ( c ) = { t r u e , j } E(c) = \{true, j\} E(c)={true,j}, E ( a ) = { t r u e , k } E(a) = \{true, k\} E(a)={true,k}。(此时 a a a, c c c 均为一阶指针,对其指向集进行指针分析意义不大,故忽略 k , j k, j k,j 的指向集)。value-flow edge包括: ( c = & j ) → ( ∗ y = c ) (c = \&j) \rightarrow (*y = c) (c=&j)(y=c) ( a = & k ) → ( ∗ y = a ) (a = \&k) \rightarrow (*y = a) (a=&k)(y=a) ( ∗ y = c ) ⟶ φ ( R = ∗ y ) (*y = c) \stackrel{\varphi}{\longrightarrow} (R = *y) (y=c)φ(R=y) ( ∗ y = a ) ⟶ ¬ φ ( R = ∗ y ) (*y = a) \stackrel{\lnot \varphi}{\longrightarrow} (R = *y) (y=a)¬φ(R=y)

  • 用Algo3分析后得到 E ( y ) = { t r u e , o y } E(y) = \{true, o_y\} E(y)={true,oy}( o y o_y oy 为新建object), S ( o y ) = { ( t r u e ,    , R ) } S(o_y) = \{(true, \; , R)\} S(oy)={(true,,R)} E ( R ) = { ( φ , j ) , ( ¬ φ , k ) } E(R) = \{(\varphi, j), (\lnot \varphi, k)\} E(R)={(φ,j),(¬φ,k)}

利用 f f f 的summary分析caller语句 l , φ : r = f ( u ) l, \varphi: r = f(u) l,φ:r=f(u) 的规则如下,以caller qux 中的 foo(x) 为例,假设 E ( x ) = { ( φ 1 , o 1 ) } E(x) = \{(\varphi_1, o_1)\} E(x)={(φ1,o1)}

  • (1).在callee的summary中替换形参( y y y)和返回值( R R R),构造中间变量 E f ′ ( y ) = { ( φ 1 , o 1 ) } E^{'}_f(y) = \{(\varphi_1, o_1)\} Ef(y)={(φ1,o1)}, S f ′ ( o 1 ) = { ( φ 1 , , R ) } S^{'}_f(o_1) = \{(\varphi_1, , R)\} Sf(o1)={(φ1,,R)} E ( R ) = { ( φ 1 ∧ φ , j ) , ( φ 1 ∧ ¬ φ , k ) } E(R) = \{(\varphi_1 \land \varphi, j), (\varphi_1 \land \lnot \varphi, k)\} E(R)={(φ1φ,j),(φ1¬φ,k)}

  • (2).合并后为 E ′ ( x ) = { ( φ 1 , o 1 ) } E^{'}(x) = \{(\varphi_1, o_1)\} E(x)={(φ1,o1)}, S f ′ ( o 1 ) = { ( φ 1 , , L 1 ) } S^{'}_f(o_1) = \{(\varphi_1, , L_1)\} Sf(o1)={(φ1,,L1)}, E ( L 1 ) = { ( φ 1 ∧ φ , j ) , ( φ 1 ∧ ¬ φ , k ) } E(L_1) = \{(\varphi_1 \land \varphi, j), (\varphi_1 \land \lnot \varphi, k)\} E(L1)={(φ1φ,j),(φ1¬φ,k)}

请添加图片描述在上述示例中,qux 的call语句后面还添加了 store 语句 *x = L1,接着 foo(x) 分析,那么更新了 S ( o 1 ) S(o_1) S(o1) 的值为 { ( φ 1 , l ′ , L 1 ) } \{(\varphi_1, l^{'}, L_1)\} {(φ1,l,L1)}。这里 l ′ l^{'} lstore 语句。

在call分析完后,value-flow中会添加summary edge,主要从callee的return value对应的 store(可能是已有的也可能是新添加的)连接到caller后面的 load 处,在上上张图示例(b)中,foo 结尾插入 R = *y 以及 return R,以及在 quxbar 的caller处添加返回值 L1(还有 L2)以及后面跟着 store 语句 *x = L1 (还有 *z = L2),那么会在新添加的callee的 load 和caller的 store 中间添加一个summary edge。(这里感觉首先需要对IR进行语义等价转换,可能方法和Pinpoint一样)。

2.Evaluation

2.1.设置

下游任务包括:(1).Thin Slicing for Program Understanding、(2).Value-flow Bug Finding: 主要是use-after-free。

baseline包括:

  • (a).指针分析的比较对比:(1).SVF (主要是SVF实现的Andersen算法)、(2).SFS(稀疏值流分析)、(3).DSA (unionfication-based, flow-insensitive, context-sensitive算法,实现采用sea-dsa)

  • (b).slicing与SUPA-FSCS进行对比

  • ©.bug finding与CRED、clang-static-analyzer (CSA) 进行对比。

benchmark采用了6个SPEC INT 2010程序以及10个开源程序。

2.2.value-flow分析

value-flow分析主要对比时间开销,结果如下表和下图所示,SVF、SFS、DSA为baseline,Falcon(PI)和Falcon(SAT)为消融实验,分别表示采用path-insensitive和采用全量SAT进行约束求解的开销。可以看到Falcon的时间开销相比其它方法有着巨大优势。

请添加图片描述

请添加图片描述

2.3.Thin Slicing

为了生成真实的slicing query,作者使用typestate分析提得到的bug report。从对应程序位置的问题变量开始backward分析,结果可以帮助开发人员理解这些报告。主要比较每个slicing query的处理时间(排除value-flow graph的时间),precison和recall。后两者有两位作者进行了人工验证。

时间开销:Falcon对每个slicing query的处理时间少于240ms。总的来说,它比SUPA-FSCS快302倍,平均加速54倍。这一性能提升归因于Falcon生成的value-flow graph比SUPA-FSCS更紧凑。

Precision:Falcon生成的slicing平均大小比SVF、SFS、DSA和SUPA-FSCS分别小5.5倍、1.9倍、2.6倍和1.3倍。表示过滤了很多错误的语句。

Recall:Falcon假设函数参数之间不存在别名,这一不sound的假设对超过90%的query没有影响,经过手动检查结果得到了验证。两项先前的研究也表明,真实世界的C/C++程序中的函数参数往往具有很少的别名关系。

2.4.Bug Detection

use-after-free bug检测的结果如下图所示,分别对比了时间开销和误报率。可以看出,Falcon在大多数大规模程序中超越了CRED和CSA,平均速度提升分别达到10.3倍和1620.8倍(以工具完成的项目为基准)。尽管表中未显示,但作者观察到,如果允许10个线程并发分析,Falcon可以在两小时内完成每个程序的检查。CRED、CSA和Falcon的虚假正例率分别为40.0%、33.3%和27.8%。我们注意到,CSA报告的警告明显少于CRED和Falcon,部分原因是频繁超时和在跨编译单元分析路径时能力有限。Falcon符合工业界对30%误报率的常见要求。

请添加图片描述

参考文献

[1].Yao P, Zhou J, Xiao X, et al. Falcon: A Fused Approach to Path-Sensitive Sparse Data Dependence Analysis[J]. Proceedings of the ACM on Programming Languages, 2024, 8(PLDI): 567-592.

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

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

相关文章

如何使用ssm实现基于web的山东红色旅游信息管理系统的设计与实现

TOC ssm716基于web的山东红色旅游信息管理系统的设计与实现jsp 绪论 1.1研究背景 从古到今&#xff0c;信息的录入&#xff0c;存储&#xff0c;检索都受制于社会生产力的发展&#xff0c;不仅仅浪费大量的人力资源还需要浪费大量的社会物资&#xff0c;并且不能长时间的保…

信息安全工程师(24)网络安全体系建设原则与安全策略

一、网络安全体系建设原则 网络空间主权原则&#xff1a;维护网络空间主权是网络安全的首要原则。这要求国家在网络空间的管理、运营、建设和使用等方面具有完全自主的权利和地位&#xff0c;不受任何外部势力的干涉和侵犯。网络安全与信息化发展并重原则&#xff1a;网络安全与…

Midjourney 使用教程——入门篇

目录标题 一、前提二、Midjourney 使用文档三、如何注册使用Midjourney四、结合GPT快速生成Midjourney 构图指令五、其他 一、前提 先连接国外代理服务器。没有的可以退下了。 二、Midjourney 使用文档 Discord 快速入门 注意&#xff1a;如图所示&#xff0c;需要10美刀一…

【HTML5】html5开篇基础(4)

1.❤️❤️前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; Hello, Hello~ 亲爱的朋友们&#x1f44b;&#x1f44b;&#xff0c;这里是E绵绵呀✍️✍️。 如果你喜欢这篇文章&#xff0c;请别吝啬你的点赞❤️❤️和收藏&#x1f4d6;&#x1f4d6;。如果你对我的…

已解决:“ModuleNotFoundError:No module named apex”

首先遇到这个问题不可以直接简单粗暴的使用&#xff1a;“pip install apex”直接安装模块来解决&#xff0c;这样的话程序还是会继续报错“ModuleNotFoundError&#xff1a;No module named apex”&#xff0c;别问我怎么知道&#xff0c;问就是深受其害&#xff01; 去网上查…

Android实现图片滚动和页签控件功能的实现代码

首先题外话&#xff0c;今天早上起床的时候&#xff0c;手滑一下把我的手机甩了出去&#xff0c;结果陪伴我两年半的摩托罗拉里程碑一代就这么安息了&#xff0c;于是我今天决定怒更一记&#xff0c;纪念我死去的爱机。 如果你是网购达人&#xff0c;你的手机上一定少不了淘宝…

热题系列章节21

补充题14. 阿拉伯数字转中文数字 补充题9. 36进制加法 85. 最大矩形 class Solution:def maximalRectangle(self, matrix: List[List[str]]) -> int:if not matrix:return 0m,nlen(matrix),len(matrix[0])# 记录当前位置上方连续“1”的个数pre[0]*(n1)res0for i in range…

带你0到1之QT编程:二十、QT与MySQL喜结连理,构建数据库应用开发

此为QT编程的第二十谈&#xff01;关注我&#xff0c;带你快速学习QT编程的学习路线&#xff01; 每一篇的技术点都是很很重要&#xff01;很重要&#xff01;很重要&#xff01;但不冗余&#xff01; 我们通常采取总-分-总和生活化的讲解方式来阐述一个知识点&#xff01; …

基于elasticsearch存储船舶历史轨迹

文章目录 引言轨迹文档定时创建索引手动添加索引并为索引添加别名POST请求批量插入文档数据查询文档数据引言 需求: 存储轨迹,提供站点查询显示 实现:每天创建索引,使用POST请求往Elasticsearch批量插入文档数据 依赖 <dependency><groupId>org.springframe…

JAVA红娘婚恋相亲交友系统源码全面解析

在数字化时代&#xff0c;红娘婚恋相亲交友系统成为了连接单身男女的重要桥梁。JAVA作为一种流行的编程语言&#xff0c;为开发这样的系统提供了强大的支持。编辑h17711347205以下是对JAVA红娘婚恋相亲交友系统源码的全面解析&#xff0c;以及三段示例代码的展示。 系统概述 …

Stable Diffusion绘画 | 插件-Deforum:动态视频生成

Deforum 与 AnimateDiff 不太一样&#xff0c; AnimateDiff 是生成丝滑变化视频的&#xff0c;而 Deforum 的丝滑程度远远没有 AnimateDiff 好。 它是根据对比前面一帧的画面&#xff0c;然后不断生成新的相似图片&#xff0c;来组合成一个完整的视频。 Deforum 的优点在于可…

DevExpress WPF中文教程:如何解决编辑单元格值的常见问题?

DevExpress WPF拥有120个控件和库&#xff0c;将帮助您交付满足甚至超出企业需求的高性能业务应用程序。通过DevExpress WPF能创建有着强大互动功能的XAML基础应用程序&#xff0c;这些应用程序专注于当代客户的需求和构建未来新一代支持触摸的解决方案。 无论是Office办公软件…

明达技术工业级边缘计算网关:智能制造的智慧纽带

在工业互联网的浪潮中&#xff0c;智能制造正引领着传统制造业的深刻变革&#xff0c;显著提升着生产效能与运营效率。在这场转型中&#xff0c;数据处理与分析能力成为了企业核心竞争力的重要一环。工业级边缘计算网关&#xff0c;作为连接工业设备与云端智能的桥梁&#xff0…

华为-单臂路由

1、什么是单臂路由 单臂路由&#xff08;Single-Arm Routing&#xff09;是一种网络架构和配置技术&#xff0c;它允许路由器通过一个物理接口来管理多个虚拟局域网&#xff08;VLAN&#xff09;之间的通信。 这个物理接口被配置为Trunk模式&#xff0c;以便能够传输来自不同VL…

完数因子输出-C语言

1.问题&#xff1a; 找出1000之内的所有完数&#xff0c;并输出其因子。 2.解答&#xff1a; 一个数如果恰好等于它的因子之和&#xff0c;这个数就称为完数。1不用判断&#xff0c;直接从2开始&#xff0c;因为1的因子只有1。 3.代码&#xff1a; #include<stdio.h>…

【文件增量备份系统】MySQL百万量级数据量分页查询性能优化

&#x1f3af; 导读&#xff1a;本文针对大数据量下的分页查询性能问题进行了深入探讨与优化&#xff0c;最初查询耗时长达12秒&#xff0c;通过避免全表计数及利用缓存保存总数的方式显著提升了浅分页查询速度。面对深分页时依然存在的延迟&#xff0c;采用先查询倒数第N条记录…

从信号量开始的里牛渴死生活

讲讲信号量 POSIX信号量 这个曾经在进程间通信提过一嘴但是没怎么细说&#xff0c;POSIX信号量和SystemV信号量都可用于同步达到无冲突的访问共享资源的目的&#xff0c;POSIX还可以用于线程间同步 初始化 #include <semaphore.h> int sem_init(sem_t *sem, int psh…

C++ 9.27

作业&#xff1a; 将之前实现的顺序表、栈、队列都更改成模板类 Stack #include <iostream> using namespace std; template <typename T> class Stack { private: T* arr; // 存储栈元素的数组 int top; // 栈顶索引 int capacity; // 栈的…

工程师 - Windows下使用WSL来访问本地的Linux文件系统

Access Linux filesystems in Windows and WSL 2 从 Windows Insiders 预览版构建 20211 开始&#xff0c;WSL 2 将提供一项新功能&#xff1a;wsl --mount。这一新参数允许在 WSL 2 中连接并挂载物理磁盘&#xff0c;从而使您能够访问 Windows 本身不支持的文件系统&#xff0…

【中医智慧解糖忧】血糖高?中医调理有妙招,自然平衡血糖不是梦!

在快节奏的现代生活中&#xff0c;高血糖已成为困扰许多人的健康难题。面对这一挑战&#xff0c;许多人第一时间想到的是西医的药物治疗&#xff0c;却往往忽略了中医这一博大精深的宝库。事实上&#xff0c;中医以其独特的理论体系和丰富的实践经验&#xff0c;在调理血糖方面…