当前位置: 首页 > news >正文

汇编---Nasm

文章目录

    • 比较流行的汇编语言有3种:
      • 不同风格的汇编语言在语法格式上会有不同:
    • 实战代码:
    • Intrinsic函数
    • 手写汇编(8086汇编)
      • 调用C的API库
      • 函数调用约定
        • 实际代码
      • C调用汇编函数进行计算
      • 纯C实现如下:
      • C+ASM实现:
      • 纯ASM实现:
      • ASM打印命令行参数
      • 数据段存储

融合了一些自己之前的笔记

比较流行的汇编语言有3种:

  • NASM风格的Intel汇编语言(x86\64)
  • AT&T风格的GAS汇编语言(特点是寄存器前面有%号)(Arm)
  • Windows风格的汇编语言

不同风格的汇编语言在语法格式上会有不同:

  • GAS(GNU Assembler)
  • NASM(Netwide Assembler)
  • MASM(Microsoft Macro Assembler)
descriptGASNASMMASM
寄存器push %eaxpush eax
立即数push $1push 1
给寄存器赋值1mov $1,%eaxmov eax,1

实战代码:

  • AT&T风格
000000000040056a <add>:40056a:	55                   	push   %rbp40056b:	48 89 e5             	mov    %rsp,%rbp40056e:	89 7d ec             	mov    %edi,-0x14(%rbp)400571:	89 75 e8             	mov    %esi,-0x18(%rbp)400574:	c7 45 fc 00 00 00 00 	movl   $0x0,-0x4(%rbp)40057b:	8b 55 ec             	mov    -0x14(%rbp),%edx40057e:	8b 45 e8             	mov    -0x18(%rbp),%eax400581:	01 d0                	add    %edx,%eax400583:	89 45 fc             	mov    %eax,-0x4(%rbp)400586:	8b 45 fc             	mov    -0x4(%rbp),%eax400589:	5d                   	pop    %rbp40058a:	c3                   	retq   40058b:	0f 1f 44 00 00       	nopl   0x0(%rax,%rax,1)
  • Intel风格
000000000040056a <add>:40056a:	55                   	push   rbp40056b:	48 89 e5             	mov    rbp,rsp40056e:	89 7d ec             	mov    DWORD PTR [rbp-0x14],edi400571:	89 75 e8             	mov    DWORD PTR [rbp-0x18],esi400574:	c7 45 fc 00 00 00 00 	mov    DWORD PTR [rbp-0x4],0x040057b:	8b 55 ec             	mov    edx,DWORD PTR [rbp-0x14]40057e:	8b 45 e8             	mov    eax,DWORD PTR [rbp-0x18]400581:	01 d0                	add    eax,edx400583:	89 45 fc             	mov    DWORD PTR [rbp-0x4],eax400586:	8b 45 fc             	mov    eax,DWORD PTR [rbp-0x4]400589:	5d                   	pop    rbp40058a:	c3                   	ret    40058b:	0f 1f 44 00 00       	nop    DWORD PTR [rax+rax*1+0x0]
  • 使用汇编编写功能函数(使用GNU-Gcc编译,因为clang会无法通过语法检查):
//gcc main.c -masm=intel
#include<stdio.h>//intel 64 中参数一为rdi \参数二为rsi \返回值为rax
//pop rbp为收栈准备返回main函数地址
int info(int x,int y){asm("add rdi,rsi;""mov rax,rdi;""pop rbp;""ret;");
}int main(){printf("%d\n",info(1,1));return 0;
}

Intrinsic函数

是编译器提供的函数接口,调用Intrinsic函数可以达到代替汇编的作用

TODO

手写汇编(8086汇编)

demo.asm 环境必须是Linux Intel 64系统

; nasm -felf64 demo.asm && ld demo.o && ./a.outglobal _start	section .text_start:;write(1,message,13)mov rax,1mov rdi,1mov rsi,messagemov rdx,13syscall;exit(0)mov eax,60xor rdi,rdisyscallmessage:db "Hi~",0xadb "Hi~~",0xadb "Hi~~~",0xamessage2:data1 db 'hello'data2 db ' world'

编译并运行:

~ $ nasm -felf64 demo.asm && ld demo.o && ./a.out
Hi~
Hi~~
Hi~~~
  • -f elf64 将源文件编译为64位的elf文件
  • ld 将目标文件连接为可执行程序

代码解释:

  • ; : 代码注释的符号,表示这一行代码注释

  • global _start : 定义全局函数

  • section .text : 表示下面的代码段为.text段,在C语言中你编写的代码段最终会通过编译器变为汇编代码放在.text

  • _start: : 表示一个函数的开始,声明函数域,C语言中就是由_start函数开始初始化程序,然后再调用熟悉的main函数

  • mov rax,1 : 当rax为1时 执行下面syscall等同于执行write()函数,其中原理就是系统调用,比如你将rax为2时,则会调用open()函数

  •     mov rdi,1mov rsi,messagemov rdx,13
    

    这一块就是构造函数调用时的参数,上面我声明了必须是64bit的系统,因为rdi、rsi、rdx就是64bit ELF文件中的常用寄存器

    64bit ELF文件中调用函数时会将参数从左到右放入寄存器,寄存器分别为(顺序): rdi, rsi, rdx, rcx, r8, r9。

    可以注意到这里只有6个寄存器,一旦超过6个参数时就会按照32bit ELF文件的规则将参数“从右至左”依次压入栈空间,进行传递

​ 那么这里就很容易理解了其实就是构造了3个参数(1,message,13),这里的message就是一个字符串常量的地址

  • syscall : 执行系统调用。即调用函数write(1,"Hi~",13)
  • exit(0) : 也是一样的方式调用,(xor就是异或操作即:rdi = rdi ^ rdi = 0)
  • message : 表示数据标签段
  • db : 定义字节类型的数据,表示其后的数据都是字节型数据,后面的,0xa表示换行0xa就是\n的ASCLL码值

调用C的API库

;nasm -felf64 demo.asm && gcc demo.o -no-pie -static && ./a.outglobal  mainextern  putssection .text
main:mov     rdi, messagecall    putsret
message:db      "hello", 0

因为是使用gcc链接器那么就只需要写一个main函数入口,如果还有个_start函数的话就会有冲突错误

注意:这里的编译需要关闭PIE保护(动态地址)才能编译成功,同时需要开启静态编译才能防止报段错误,具体可以用gdb开查看实现

比如:我关闭了PIE保护和开启了静态编译,那么我的main函数地址地址每次都会是0x4016c0,并且puts函数的地址也被程序正确的找到

────────────────────────────────────────────────────── code:x86:64 ────0x4016b5 <frame_dummy+53> cs     nop WORD PTR [rax+rax*1+0x0]0x4016bf <frame_dummy+63> nop0x4016c0 <main+0>         movabs rdi, 0x4016d0→   0x4016ca <main+10>        call   0x40c080 <puts>↳    0x40c080 <puts+0>         endbr640x40c084 <puts+4>         push   r130x40c086 <puts+6>         push   r120x40c088 <puts+8>         mov    r12, rdi0x40c08b <puts+11>        push   rbp0x40c08c <puts+12>        push   rbx
────────────────────────────────────────────── arguments (guessed) ────
puts ($rdi = 0x000000004016d0 → 0x2e66006f6c6c6568 ("hello"?),$rsi = 0x007fff184836e8 → 0x007fff184856d5 → "/home/hi/a.out",$rdx = 0x007fff184836f8 → 0x007fff184856e4 → "SHELL=/bin/bash",$rcx = 0x00000040004000
)

函数调用约定

在操作函数跳转时栈无疑是一个非常重要的通道,而决定栈空间数据使用顺序声明的就是函数的调用约定声明了,存在如下常用调用约定:

TODO:还需要搞清楚其他的调用约定用法,做好是有代码演示

  stdcall (pascal) 主要用于Microsoft C++系列cdecl (默认C语言调用约定)fastcall thiscall naked call

在默认C语言调用约定中(64bit):

  • 传递参数时,按照从左到右的顺序,将尽可能多的参数依次保存在寄存器中。存放位置的寄存器顺序是确定的:
    • 对于整数和指针,rdirsirdxrcxr8r9
    • 对于浮点数(float 和 double 类型),xmm0xmm1xmm2xmm3xmm4xmm5xmm6xmm7
  • 剩下的参数将按照从右到左的顺序压入栈中,并在调用之后 由调用函数推出栈

在进入到另一个函数地址时,会首先进行一个开栈操作,至于开多少取决于你的函数代码块有多少和编译器

如果手写汇编的话就需要自己手动计算需要开辟的栈空间大小

为了更好看清楚栈空间的结构,这里使用32bit程序来看效果:

  • 比如在main函数调用子函数时:
    • 处理完成调用参数后会执行call 0xxxxxx的指令

    • call会在调用函数时将eip压入栈,也就是将下一条指令的地址赋值给esp,这样就可以通过ret指令进行返回原函数继续执行了

  • 然后到了子函数的地址处时:
    • 会执行先保存当前的栈底(ebp)值,然后分别将ebp、esp压入栈,通过sub esp,0xxxxx的方式来扩展栈空间

    • 之后就是取main函数在调用子函数时的压入的参数了

  • 子函数返回时:
    • 会通过执行leave指令其实就是:
      • mov esp , ebp 来关闭开辟的栈空间,然后pop ebp 移动到上个函数的位置

      • 这时ebp就是函数入口时push上个函数的ebp的值,最后根据这个值进行恢复

    • 再执行ret指令进行地址跳转,而ret指令就是:
      • pop eip将堆栈段中当前SS:SP所指的字内容弹出到某个寄存器,也就是将sp的值赋值给eip,eip就是下一条指令地址

      • 对应arm的pc寄存器,这就对应了call指令时压入栈的ip地址

实际代码

执行leave指令前

00:0000│ esp 0xffffd508 —▸ 0x56558fdc (_GLOBAL_OFFSET_TABLE_) ◂— 0x3ee4
01:00040xffffd50c —▸ 0x56556273 (__libc_csu_init+83) ◂— add    esi, 1
02:00080xffffd510 ◂— 0x1
03:000c│     0xffffd514 ◂— 0x1
04:0010│ ebp 0xffffd518 —▸ 0xffffd538 ◂— 0x0
05:00140xffffd51c —▸ 0x56556208 (main+45) ◂— add    esp, 8
06:00180xffffd520 ◂— 0x4
07:001c│     0xffffd524 ◂— 0x2

执行leave指令后

00:0000│ esp 0xffffd51c —▸ 0x56556208 (main+45) ◂— add    esp, 8
01:00040xffffd520 ◂— 0x4
02:00080xffffd524 ◂— 0x2
03:000c│     0xffffd528 ◂— 0x0
04:00100xffffd52c ◂— 0x0
05:00140xffffd530 ◂— 0x1
06:00180xffffd534 ◂— 0x2
07:001c│ ebp 0xffffd538 ◂— 0x0

intel架构默认调用约定组成结构:

  • 32位无参数:
payload = b'a'* 0x88 + b'b' * 0x4  + p32(backdoor) +p32(main_addr)
  • 32位有参数:"函数地址+返回地址+参数
payload = b'a'* 0x88 + b'b' * 0x4  + p32(backdoor) + p32(main_addr) + p32(bin_sh)//完整的rop链返回地址main
  • 64无参数:
payload = b'a'* 0x88 + b'b' * 0x8  + p64(backdoor) 
  • 64有参数:"函数地址+参数+返回地址”
paypyload = b'a'* 0x88 + b'b' * 0x8 + p64(pop_edi) + p64(bin_sh) + p64(backdoor)
  • 64无System泄露:
#第一次加载payload进行libc真实地址获取
payload = b'a' * 0x50 + b'b' * 0x8 + p64(prdi) + p64(e.got['puts']) + p64(e.plt['puts']) + p64(返回函数地址)
#重新构造payload
payload = b'a' * 0x50 + b'b' * 8
for i in range(1):payload += p64(rtn_addr)
payload += p64(prdi) + p64(libc_addr + lic.dump("str_bin_sh")) + p64(libc_addr + libc.dump("system"))

C调用汇编函数进行计算

特点环境下说使用simd的一些优化操作,就需要使用simd指令集来操作数据,从而实现快速计算,那么此时的汇编代码块就起到了一个处理数据集功能的作用

纯C实现如下:

#include<stdio.h>
#include<time.h>int Max(int a,int b,int c){int ret = a;if(ret<b) ret=b;if(ret<c) ret=c;return ret;
}int main(){srand(0x100); //为了方便ASM 和 C代码分别实现的demo运行结果对比,这里使用一样的随机种子for (int i = 0; i < 10; ++i) {int a = rand() , b = rand(), c = rand();printf("{%d,%d,%d} Max=%d \n",a,b,c,Max(a,b,c));}return 0;
}

结果:

~ $ gcc main.c && ./a.out
{1557381903,485784087,974190345} Max=1557381903
{909832560,185226890,4869305} Max=909832560
{842916993,1066023196,370971114} Max=1066023196
{1378714000,834802215,875669745} Max=1378714000
{419994512,459245563,1733189616} Max=1733189616
{259238441,2032537841,1291879760} Max=2032537841
{1977168301,1893959658,2072065736} Max=2072065736
{1802926432,786500781,937118081} Max=1802926432
{1567600346,303276252,249486295} Max=1567600346
{2134477068,1322435152,1593906562} Max=2134477068

C+ASM实现:

demo.asm

    global  Maxsection .textMax:mov rax,rdicmp rax,rsicmovl rax,rsicmp rax,rdxcmovl rax,rdxret

main.c

#include<stdio.h>
#include<time.h>int Max(int a,int b,int c);int main(){srand(0x100);for (int i = 0; i < 10; ++i) {int a = rand() , b = rand(), c = rand();printf("{%d,%d,%d} Max=%d \n",a,b,c,Max(a,b,c));}return 0;
}

结果:

~ $ nasm -f elf64 demo.asm && gcc main.c demo.o && ./a.out
{1557381903,485784087,974190345} Max=1557381903
{909832560,185226890,4869305} Max=909832560
{842916993,1066023196,370971114} Max=1066023196
{1378714000,834802215,875669745} Max=1378714000
{419994512,459245563,1733189616} Max=1733189616
{259238441,2032537841,1291879760} Max=2032537841
{1977168301,1893959658,2072065736} Max=2072065736
{1802926432,786500781,937118081} Max=1802926432
{1567600346,303276252,249486295} Max=1567600346
{2134477068,1322435152,1593906562} Max=2134477068

纯ASM实现:


ASM打印命令行参数

    global  mainextern putssection .textmain:push rdi ;保存参数一push rsi ;保存参数二;sub rsp,8	;实际在操作的时候在进入main函数时,因为使用的程序是64bit,可能需要手动的修复rbp的值,不然会导致栈空间数据错乱mov rdi ,[rsi]call puts;add rsp,8 ;回收栈pop rsi ;取出原参数值pop rdi ;取出原参数值add rsi,8dec rdijnz mainret

运行:

~ $ nasm -f elf64 demo.asm && gcc demo.o -static && ./a.out 1 2 3
./a.out
1
2
3

调试:

  1. 首先来看看进入main函数时的寄存器值
$rax   : 0x000000004016c0  →  <main+0> push rdi
$rbx   : 0x007ffeccff0100  →  0x007ffeccff06e4  →  "SHELL=/bin/bash"
$rcx   : 0x154000
$rdx   : 0x007ffeccff0100  →  0x007ffeccff06e4  →  "SHELL=/bin/bash"
$rsp   : 0x007ffeccfefef8  →  0x00000000401b0a  →  <__libc_start_call_main+106> mov edi, eax
$rbp   : 0x1
$rsi   : 0x007ffeccff00d8  →  0x007ffeccff06cf  →  "/home/hi/a.out"
$rdi   : 0x4
$rip   : 0x000000004016c0  →  <main+0> push rdi

可以看到rdi为4表示main的第一个参数的值为4,即命令行参数有4个

  1. 然后看看此时的rsi:
gef➤  telescope $rsi
0x007ffeccff00d8│+0x0000: 0x007ffeccff06cf  →  "/home/hi/a.out"	 ← $rsi, $r13
0x007ffeccff00e0│+0x0008: 0x007ffeccff06de  →  0x4853003300320031 ("1"?)
0x007ffeccff00e8│+0x0010: 0x007ffeccff06e0  →  0x4c45485300330032 ("2"?)
0x007ffeccff00f0│+0x0018: 0x007ffeccff06e2  →  0x3d4c4c4548530033 ("3"?)
0x007ffeccff00f8│+0x0020: 0x0000000000000000

刚好对应着我们传进来的参数。并且最后一个参数后面的地址被置为0

  1. 可以看到在main函数开始时,进行了2个push操作,这是为了保存main函数的2个参数地址,因为在调用call puts的时候必须要保证rdi为参数二的字符串值,在进入puts函数的子函数栈空间后,同时也会操作rdi的值,这个时候如果没有在调用puts函数之前保存地址的话,那么就会发生指针丢失的问题,从而产生各种可能的错误
  2. 调用puts函数后进行了一个恢复参数值的操作,从而方便再次进入到main函数,进行一个相同的操作
  3. dec指令表示自减1,执行结果影响AF、OF、PF、SF、ZF标志位, jne指令 ZF寄存器!=0则跳转,这里主要关注ZF标志位,来看调试:
────────────────────────────────────────────────────── code:x86:64 ────0x4016ca <main+10>        pop    rsi0x4016cb <main+11>        pop    rdi0x4016cc <main+12>        add    rsi, 0x8
●→   0x4016d0 <main+16>        dec    rdi0x4016d3 <main+19>        jne    0x4016c0 <main>0x4016d5 <main+21>        ret0x4016d6 <main+22>        cs     nop WORD PTR [rax+rax*1+0x0]0x4016e0 <handle_zhaoxin+0> push   rbx0x4016e1 <handle_zhaoxin+1> mov    eax, 0x4

在执行最后一个参数时,执行dec指令前的flags寄存器如下:

gef➤  p $eflags
$8 = [ AF IF ]
//AF IF表示只有这两个寄存器的值为1
gef➤  x $eflags
0x212
gef➤  p $eflags
$7 = [ PF ZF IF ]
//ZF 可以看到这里的ZF寄存器值为1了,即表示dec指令执行后的值为0,那么就会在jne指令后满足不为zero寄存器不为0的条件进行
gef➤  x $eflags
0x246

具体的flag寄存器标记为如下:

value溢出方向中断跟踪符号辅进位奇偶进位
markOFDFIFTFSFZFAFPFCF
0x212001000010010
0x246001001000110

数据段存储

;nasm -f elf64 demo.asm && gcc demo.o -static && ./a.out 1 2global  main;C APIextern atoiextern printfdefault relsection .text
main:dec      rdijz       nothingToAveragemov      [count], rdi; 保存浮点数参数的个数到bss变量
accumulate:push     rdipush     rsimov      rdi, [rsi+rdi*8]; argv[rdi]call     atoipop      rsipop      rdiadd      [sum], raxdec      rdijnz      accumulate; loop遍历参数average:cvtsi2sd xmm0, [sum]cvtsi2sd xmm1, [count]divsd    xmm0, xmm1; xmm0 现在值为 sum/countmov      rdi, format; printf输出格式mov      rax, 1; printf 是多参数的, 含有一个不是整数的参数sub      rsp, 8; 对齐栈指针call     printfadd      rsp, 8retnothingToAverage:mov      rdi, errorxor      rax, raxcall     printfret;=============.data段===========================section  .data
count:  dq       0
sum:    dq       0
format: db       "%g", 10, 0
error:  db       "There are no command line arguments to average", 10,0
~ $ nasm -f elf64 demo.asm && gcc demo.o -static && ./a.out 1 2
1.5

相关文章:

汇编---Nasm

文章目录 比较流行的汇编语言有3种:不同风格的汇编语言在语法格式上会有不同: 实战代码&#xff1a;Intrinsic函数手写汇编&#xff08;8086汇编&#xff09;调用C的API库函数调用约定实际代码 C调用汇编函数进行计算纯C实现如下&#xff1a;CASM实现:纯ASM实现:ASM打印命令行参…...

NDK OpenGL渲染画面效果

NDK系列之OpenGL渲染画面效果技术实战&#xff0c;本节主要是通过OpenGL Java库&#xff08;谷歌对OpenGL C库做了JIN封装&#xff0c;核心实现还是在Native层&#xff09;&#xff0c;实现页面渲染&#xff0c;自定义渲染特效。 实现效果&#xff1a; 实现逻辑&#xff1a; 1…...

常见的深度学习框架

框架优点缺点TensorFlow- 由Google开发和维护&#xff0c;社区庞大&#xff0c;学习资源丰富- 具备优秀的性能表现&#xff0c;支持大规模分布式计算- 支持多种编程语言接口&#xff0c;易于使用- 提供了可视化工具TensorBoard&#xff0c;可用于调试和可视化模型- 底层架构复杂…...

【设计模式】七大设计原则--------单一职责原则

文章目录 1.案例1.1 原始案例1.2 改进一&#xff1a;类上遵循单一职责原则1.3 改进二&#xff1a;方法上遵循单一职责原则 2.小结 1.案例 1.1 原始案例 package com.sdnu.principle.singleresponsibility; //客户端 public class singleResponsibility {public static void m…...

MySQL-中间件mycat(一)

目录 &#x1f341;mycat基础概念 &#x1f341;Mycat安装部署 &#x1f343;初始环境 &#x1f343;测试环境 &#x1f343;下载安装 &#x1f343;修改配置文件 &#x1f343;启动mycat &#x1f343;测试连接 &#x1f990;博客主页&#xff1a;大虾好吃吗的博客 &#x1f9…...

ARM寄存器组织

ARM有37个32位长的寄存器&#xff1a; 1个用做PC&#xff08;Program Counter&#xff09;&#xff1b; 1个用做CPSR(Current Program Status Register)&#xff1b; 5个用做SPSR&#xff08;Saved Program Status Registers&#xff09;&#xff1b; 30个通用寄存器。 AR…...

记录一次webdav协议磁盘挂载经验总结

记录一次磁盘挂载经验总结 文章目录 记录一次磁盘挂载经验总结适配环境服务器协议适配方案脚本与详细说明 适配环境 windows 11windows 10windows 7 x86 and x64linuxuos统信国产化linux系统 服务器协议 webdav 适配方案 一、通用 winfsprclone 已验证通过&#xff0c;版…...

安装Django

1. 在物理环境安装Django Python官方的PyPi仓库为我们提供了一个统一的代码托管仓库&#xff0c;所有的第三方库&#xff0c;甚至你自己写的开源模块&#xff0c;都可以发布到这里&#xff0c;让全世界的人分享下载 pip是最有名的Python包管理工具 。提供了对Python包的查找、…...

【前端面经】JS-如何使用 JavaScript 来判断用户设备类型?

在 Web 开发中&#xff0c;有时需要针对不同的设备类型进行不同的处理。例如&#xff0c;对于移动设备&#xff0c;我们可能需要采用不同的布局或者交互方式&#xff0c;以提供更好的用户体验。因此&#xff0c;如何判断用户设备类型成为了一个重要的问题。 1. 使用 navigator…...

压缩HTML引用字体

内容简介 有些网站为了凸显某部分字体&#xff0c;而引入自定义字体&#xff0c;但由于自定义字体相对都比较大&#xff08;几M&#xff09;,导致页面加载缓慢&#xff1b;所以本文介绍三种压缩字体的方法&#xff0c;可根据项目情况自行选择。 压缩方法 1、利用Fontmin程序&a…...

大厂高频面试:底层的源码逻辑知多少?

你好&#xff0c;我是何辉。今天我们来聊一聊Dubbo的大厂高频面试题。 大厂面试&#xff0c;一般重点考察对技术理解的深度&#xff0c;和中小厂的区别在于&#xff0c;不仅要你精于实战&#xff0c;还要你深懂原理&#xff0c;勤于思考并针对功能进行合理的设计。 网上一直流…...

【学习笔记】CF607E Cross Sum

最后一道数据结构&#xff0c;不能再多了。 而且需要一点计算几何的知识&#xff0c;有点难搞。 分为两个部分求解。 首先考虑找到距离 ≤ r \le r ≤r的交点数量。发现这等价于圆上两段圆弧相交&#xff0c;因此将圆上的点离散化后排序&#xff0c;用一个主席树来求就做完了…...

Python 一元线性回归模型预测实验完整版

一元线性回归预测模型 实验目的 通过一元线性回归预测模型&#xff0c;掌握预测模型的建立和应用方法&#xff0c;了解线性回归模型的基本原理 实验内容 一元线性回归预测模型 实验步骤和过程 (1)第一步&#xff1a;学习一元线性回归预测模型相关知识。 线性回归模型属于…...

GStreamer第一阶段的简单总结

这里写目录标题 前言个人的总结v4l2src插件的简单使用 前言 因为涉及很多细节的GStreamer官方论坛有详细解链接: GStreamer官网&#xff0c;这里不做说明&#xff0c;以下只是涉及到个人的理解和认知&#xff0c;方便后续的查阅。 个人的总结 1)了解pipeline的使用&#xff0…...

【网络进阶】服务器模型Reactor与Proactor

文章目录 1. Reactor模型2. Proactor模型3. 同步IO模拟Proactor模型 在高并发编程和网络连接的消息处理中&#xff0c;通常可分为两个阶段&#xff1a;等待消息就绪和消息处理。当使用默认的阻塞套接字时&#xff08;例如每个线程专门处理一个连接&#xff09;&#xff0c;这两…...

使用div替代<frameset><frame>的问题以及解决办法

首先是原版三层框架的html&#xff1a; <html> <head> <title>THPWP</title> </head> <!-- 切记frameset不能写在body里面&#xff0c;以下代表首页由三层模块组成&#xff0c;其中第一层我是用来放菜单高度占比14%&#xff0c;中间的用作主…...

Verilog中的`define与`if的使用

一部分代码可能有时候用&#xff0c;有时候不用&#xff0c;为了避免全部编译占用资源&#xff0c;可以使用条件编译语句。 语法 // Style #1: Only single ifdef ifdef <FLAG>// Statements endif// Style #2: ifdef with else part ifdef <FLAG>// Statements …...

沃尔玛、亚马逊影响listing的转化率4大因素,测评补单自养号解析

1、listing的相关性&#xff1a;前期我们在找词&#xff0c;收集词的时候&#xff0c;我们通过插件来协助我们去筛选词。我们把流量高&#xff0c;中&#xff0c;低的关键词都一一收集&#xff0c;然后我们再进行对收集得来的关键词进行分析&#xff0c;再进行挑词&#xff0c;…...

静态分析和动态分析

在开发早期&#xff0c;发现并修复bug在许多方面都有好处。它可以减少开发时间&#xff0c;降低成本&#xff0c;并且防止数据泄露或其他安全漏洞。特别是对于DevOps&#xff0c;尽早持续地将测试纳入SDLC软件开发生命周期是非常有帮助的。 这就是动态和静态分析测试的用武之地…...

代码随想录_贪心_leetcode 1005 134

leetcode 1005. K 次取反后最大化的数组和 1005. K 次取反后最大化的数组和 给你一个整数数组 nums 和一个整数 k &#xff0c;按以下方法修改该数组&#xff1a; 选择某个下标 i 并将 nums[i] 替换为 -nums[i] 。 重复这个过程恰好 k 次。可以多次选择同一个下标 i 。 以…...

R语言AI模型部署方案:精准离线运行详解

R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...

【力扣数据库知识手册笔记】索引

索引 索引的优缺点 优点1. 通过创建唯一性索引&#xff0c;可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度&#xff08;创建索引的主要原因&#xff09;。3. 可以加速表和表之间的连接&#xff0c;实现数据的参考完整性。4. 可以在查询过程中&#xff0c;…...

【磁盘】每天掌握一个Linux命令 - iostat

目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat&#xff08;I/O Statistics&#xff09;是Linux系统下用于监视系统输入输出设备和CPU使…...

质量体系的重要

质量体系是为确保产品、服务或过程质量满足规定要求&#xff0c;由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面&#xff1a; &#x1f3db;️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限&#xff0c;形成层级清晰的管理网络&#xf…...

让AI看见世界:MCP协议与服务器的工作原理

让AI看见世界&#xff1a;MCP协议与服务器的工作原理 MCP&#xff08;Model Context Protocol&#xff09;是一种创新的通信协议&#xff0c;旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天&#xff0c;MCP正成为连接AI与现实世界的重要桥梁。…...

ABAP设计模式之---“简单设计原则(Simple Design)”

“Simple Design”&#xff08;简单设计&#xff09;是软件开发中的一个重要理念&#xff0c;倡导以最简单的方式实现软件功能&#xff0c;以确保代码清晰易懂、易维护&#xff0c;并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计&#xff0c;遵循“让事情保…...

MyBatis中关于缓存的理解

MyBatis缓存 MyBatis系统当中默认定义两级缓存&#xff1a;一级缓存、二级缓存 默认情况下&#xff0c;只有一级缓存开启&#xff08;sqlSession级别的缓存&#xff09;二级缓存需要手动开启配置&#xff0c;需要局域namespace级别的缓存 一级缓存&#xff08;本地缓存&#…...

一些实用的chrome扩展0x01

简介 浏览器扩展程序有助于自动化任务、查找隐藏的漏洞、隐藏自身痕迹。以下列出了一些必备扩展程序&#xff0c;无论是测试应用程序、搜寻漏洞还是收集情报&#xff0c;它们都能提升工作流程。 FoxyProxy 代理管理工具&#xff0c;此扩展简化了使用代理&#xff08;如 Burp…...

深入浅出WebGL:在浏览器中解锁3D世界的魔法钥匙

WebGL&#xff1a;在浏览器中解锁3D世界的魔法钥匙 引言&#xff1a;网页的边界正在消失 在数字化浪潮的推动下&#xff0c;网页早已不再是静态信息的展示窗口。如今&#xff0c;我们可以在浏览器中体验逼真的3D游戏、交互式数据可视化、虚拟实验室&#xff0c;甚至沉浸式的V…...

32位寻址与64位寻址

32位寻址与64位寻址 32位寻址是什么&#xff1f; 32位寻址是指计算机的CPU、内存或总线系统使用32位二进制数来标识和访问内存中的存储单元&#xff08;地址&#xff09;&#xff0c;其核心含义与能力如下&#xff1a; 1. 核心定义 地址位宽&#xff1a;CPU或内存控制器用32位…...