【Linux】虚拟空间布局模型地址回填数据段合并(万字详解)
Ⅰ、虚拟空间布局模型
理论模型
包括上节的动态库与静态库,加上本节后面两个内容其实都是对gcc的扩展与补充知识,也是需要了解和掌握的知识。在开讲之前,我们先来说一下在32位x86的Linux系统中,虚拟地址空间布局模型:(系统编程阶段有重要意义)
①text段:用来存放程序执行代码的内存区域。这部分区域的大小在程序运行前确定,并且内存区域通常是只读(某些架构也允许代码段为可写,即允许修改程序)。
②rodata段:read only data segment,即只读数据段。存放一些只读的常数变量,例如字符串常量等。
③data段:用来存放程序中已初始化的全局变量的内存区域,属于静态内存分配。
④bss段:Block Started by Symbol的简称,用来存放程序中未初始化的全局变量的内存区域,属于静态内存分配。
⑤heap:堆区空间,向上增长;stack:栈区空间,向下增长。
⑥stack和heap中间的部分,用于存放我们的.so等文件
⑦stack上方与内核中间那段空间:是用来存放环境变量的。
tips:真正的段空间不止这几个,大概在30多个的样子吧。但那些对我们来说并没有很直接的关联,暂时不提。(例如,@plt就有一个与自己对应的段空间)
当关闭终端时,我们的虚拟内存就会“消失”,然后我们重新打开终端时,虚拟内存就会重新“出现”。所以我们使用export导入的环境变量,例如前文提到的LD_LIBRARY_PATH,所以这就是这个环境变量临时性的本质。(注意,这里并不是真的消失和出现,而是说:像我们运行C程序时一样,定义的变量并不会在下次重新启动时依旧保存上次运行的状态)
在初始化时,bss段部分将会清零。bss段属于静态内存分配,即程序一开始就将其清零了。比如,在C语言之类的程序编译完成后,已初始化的全局变量保存在.data段中,未初始化的全局变量保存在.bss段中。
text和data段都在可执行文件中,由系统从可执行文件中加载。
而bss段不在可执行文件中,由系统初始化。
让我们先写一段代码来进行探究:
实验一
使用vim test.c:完成下面的代码的编写。完事后 :wq保存并退出。
使用objdump(为object-dump的缩写) -t 反汇编查看变量的存储位置:
可以看到,变量在内存中的位置与注释一致。上图中,l代表local,局部的;g代表global,全局的。然后我们再来运行一下test程序看一下打印出来的内存与上图有什么关联:
我们再来对比一下两图,找到bss_1,在objdump -t查看的内存为:000000000000401c,而打印出来的是:0x61e88502401c,取末四位:401c,401c,对应上了吧。经过这个实验,我们可以发现,data段确实是位于bss段的下方的。
实验二
使用vim创建m1.c,然后再在末行模式输入::vsp m2.c分屏再建一个m2.c文件,然后分别编写程序如下:(左m2.c,右m1.c)。最后在末行模式输入:wqall,保存并退出所有打开的文件。
此时已经退出了vim模式,我们对这两个文件进行编译:
我们发现,m2编译之后比m1的文件要大很多。m1大小只有15800b,m2大小却达到了27816b。所以,此时我们打算继续使用objdump -t 反汇编来查看变量arr所在的存储位置
突然就发现不对劲了,m1没有初始化arr,所以m1中的arr处于bss段中,而m2相反,初始化为非零值,所以m2的arr处于data段中。因为text、bss、data段在编译时已经决定了进程将占用多少VM,所以我们使用size命令来查看一下两个文件的段的大小。
我们发现:data段和bss段的大小的差别很大。所以问题肯定是出现在了这个上面。
然后,我们使用objdump -s来查看两个文件中.data段中的数据,可以看到,有一长串的数据:
m2: 文件格式 elf64-x86-64
//省略
Contents of section .data:4000 00000000 00000000 08400000 00000000 .........@......//....此处为个人删除省略内容....//此处省略了大约六万的字符数6ef0 00000000 00000000 00000000 00000000 ................
//省略
m1: 文件格式 elf64-x86-64
//省略
Contents of section .data:4000 00000000 00000000 08400000 00000000 .........@......
//省略
.bss 是不占用.exe文件空间的,其内容由操作系统初始化(清零);
.data 却需要占用,其内容由程序初始化。因此造成了上述情况。
数据段合并和地址回填通常是操作系统和编译器在内存管理和程序加载时涉及的概念。
在 使用动态库 时就涉及到这两个操作:
Ⅱ、数据段合并
数据段合并主要是在程序链接的过程中进行的。当多个目标文件被链接时,它们可能包含多个相同类型的数据段(例如 .data
、.bss
)。链接器会将这些数据段合并成一个统一的数据段,减少内存使用和提高访问效率。
将上面的图拿过来进行说明:
提前声明:每个段的空间为1个页(page)的大小--4K。
在该内存中,每个段都会有访问权限,对于该图中的text段和rodata段都是ro权限,而data和bss段是rw权限。如果text段和rodata都单独占一个段空间,那么就会占用两个4K的空间,所以为了节省空间,在链接阶段,我们有一个数据段合并的过程,并不是说只合并数据段,很多段都会根据标准进行合并,这里给出四个段:
text段与rodata段说你也是只读我也是只读,咱俩凑合凑合过吧,然后就进行合并了,data和bss段也一样进行合并了,那么就节省了至少2个4K的空间。
Ⅲ、地址回填
地址回填是指在程序的加载或链接过程中,将目标文件中使用的符号地址转换为实际内存地址的过程。当程序被加载到内存中时,操作系统需要将符号(如函数或变量)的地址填充到代码和数据段中,以便程序能顺利运行。
在main函数中,里面有两个函数fuc1和fuc2,这两个函数在链接阶段进行地址回填。那么怎么进行地址回填呢?这通常涉及到以下几个步骤:
- 符号解析:将符号名称解析为实际的内存地址。
- 地址替换:在目标文件中占位符的地址被实际计算出的地址所替换。
- 重定位:处理程序在不同内存地址加载时,需要调整地址代码和数据。
完成链接后得到的a.out的地址是以main函数地址为依据的。
假设main的地址为1000,那么func1依据main而定义,假设间隔为100,那么func1的地址就为1000+100,假设func2与main间隔为200,那么func2的地址就为1000+200。
但是我们也说了,这是完成链接后的事情,在完成编译后的main.o文件中main的地址标记为0。但下面的关系依然存在,那也就是说:
当从hello.o执行到a.out的操作过程中,我们就完成了链接,在链接的过程中,进行了一个地址回填,看一下,填的是什么?其实填的就是main的地址,本来main的地址以main符代替,链接完然main具有地址后,就会将地址进行回填。
那么制作动态库呢?制作动态库时,func1、func2被制成了.o文件,那func1和func2还是以main作为依据吗?不是了。因为动态库内的函数调用方法与文件内的函数的调用时处理方法不同。
先创建一个test.c文件
然后我们使用objdump -dS 来查看反汇编:
test.shared: 文件格式 elf64-x86-64Disassembly of section .init:0000000000001000 <_init>:1000: f3 0f 1e fa endbr64 1004: 48 83 ec 08 sub $0x8,%rsp1008: 48 8b 05 d9 2f 00 00 mov 0x2fd9(%rip),%rax # 3fe8 <__gmon_start__@Base>100f: 48 85 c0 test %rax,%rax1012: 74 02 je 1016 <_init+0x16>1014: ff d0 call *%rax1016: 48 83 c4 08 add $0x8,%rsp101a: c3 ret Disassembly of section .plt:0000000000001020 <.plt>:1020: ff 35 8a 2f 00 00 push 0x2f8a(%rip) # 3fb0 <_GLOBAL_OFFSET_TABLE_+0x8>1026: f2 ff 25 8b 2f 00 00 bnd jmp *0x2f8b(%rip) # 3fb8 <_GLOBAL_OFFSET_TABLE_+0x10>102d: 0f 1f 00 nopl (%rax)1030: f3 0f 1e fa endbr64 1034: 68 00 00 00 00 push $0x01039: f2 e9 e1 ff ff ff bnd jmp 1020 <_init+0x20>103f: 90 nop1040: f3 0f 1e fa endbr64 1044: 68 01 00 00 00 push $0x11049: f2 e9 d1 ff ff ff bnd jmp 1020 <_init+0x20>104f: 90 nop1050: f3 0f 1e fa endbr64 1054: 68 02 00 00 00 push $0x21059: f2 e9 c1 ff ff ff bnd jmp 1020 <_init+0x20>105f: 90 nopDisassembly of section .plt.got:0000000000001060 <__cxa_finalize@plt>:1060: f3 0f 1e fa endbr64 1064: f2 ff 25 8d 2f 00 00 bnd jmp *0x2f8d(%rip) # 3ff8 <__cxa_finalize@GLIBC_2.2.5>106b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)Disassembly of section .plt.sec:0000000000001070 <add@plt>:1070: f3 0f 1e fa endbr64 1074: f2 ff 25 45 2f 00 00 bnd jmp *0x2f45(%rip) # 3fc0 <add@Base>107b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)0000000000001080 <printf@plt>:1080: f3 0f 1e fa endbr64 1084: f2 ff 25 3d 2f 00 00 bnd jmp *0x2f3d(%rip) # 3fc8 <printf@GLIBC_2.2.5>108b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)0000000000001090 <sub@plt>:1090: f3 0f 1e fa endbr64 1094: f2 ff 25 35 2f 00 00 bnd jmp *0x2f35(%rip) # 3fd0 <sub@Base>109b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)Disassembly of section .text:00000000000010a0 <_start>:10a0: f3 0f 1e fa endbr64 10a4: 31 ed xor %ebp,%ebp10a6: 49 89 d1 mov %rdx,%r910a9: 5e pop %rsi10aa: 48 89 e2 mov %rsp,%rdx10ad: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp10b1: 50 push %rax10b2: 54 push %rsp10b3: 45 31 c0 xor %r8d,%r8d10b6: 31 c9 xor %ecx,%ecx10b8: 48 8d 3d e1 00 00 00 lea 0xe1(%rip),%rdi # 11a0 <main>10bf: ff 15 13 2f 00 00 call *0x2f13(%rip) # 3fd8 <__libc_start_main@GLIBC_2.34>10c5: f4 hlt 10c6: 66 2e 0f 1f 84 00 00 cs nopw 0x0(%rax,%rax,1)10cd: 00 00 00 00000000000010d0 <deregister_tm_clones>:10d0: 48 8d 3d 39 2f 00 00 lea 0x2f39(%rip),%rdi # 4010 <__TMC_END__>10d7: 48 8d 05 32 2f 00 00 lea 0x2f32(%rip),%rax # 4010 <__TMC_END__>10de: 48 39 f8 cmp %rdi,%rax10e1: 74 15 je 10f8 <deregister_tm_clones+0x28>10e3: 48 8b 05 f6 2e 00 00 mov 0x2ef6(%rip),%rax # 3fe0 <_ITM_deregisterTMCloneTable@Base>10ea: 48 85 c0 test %rax,%rax10ed: 74 09 je 10f8 <deregister_tm_clones+0x28>10ef: ff e0 jmp *%rax10f1: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)10f8: c3 ret 10f9: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)0000000000001100 <register_tm_clones>:1100: 48 8d 3d 09 2f 00 00 lea 0x2f09(%rip),%rdi # 4010 <__TMC_END__>1107: 48 8d 35 02 2f 00 00 lea 0x2f02(%rip),%rsi # 4010 <__TMC_END__>110e: 48 29 fe sub %rdi,%rsi1111: 48 89 f0 mov %rsi,%rax1114: 48 c1 ee 3f shr $0x3f,%rsi1118: 48 c1 f8 03 sar $0x3,%rax111c: 48 01 c6 add %rax,%rsi111f: 48 d1 fe sar %rsi1122: 74 14 je 1138 <register_tm_clones+0x38>1124: 48 8b 05 c5 2e 00 00 mov 0x2ec5(%rip),%rax # 3ff0 <_ITM_registerTMCloneTable@Base>112b: 48 85 c0 test %rax,%rax112e: 74 08 je 1138 <register_tm_clones+0x38>1130: ff e0 jmp *%rax1132: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)1138: c3 ret 1139: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)0000000000001140 <__do_global_dtors_aux>:1140: f3 0f 1e fa endbr64 1144: 80 3d c5 2e 00 00 00 cmpb $0x0,0x2ec5(%rip) # 4010 <__TMC_END__>114b: 75 2b jne 1178 <__do_global_dtors_aux+0x38>114d: 55 push %rbp114e: 48 83 3d a2 2e 00 00 cmpq $0x0,0x2ea2(%rip) # 3ff8 <__cxa_finalize@GLIBC_2.2.5>1155: 00 1156: 48 89 e5 mov %rsp,%rbp1159: 74 0c je 1167 <__do_global_dtors_aux+0x27>115b: 48 8b 3d a6 2e 00 00 mov 0x2ea6(%rip),%rdi # 4008 <__dso_handle>1162: e8 f9 fe ff ff call 1060 <__cxa_finalize@plt>1167: e8 64 ff ff ff call 10d0 <deregister_tm_clones>116c: c6 05 9d 2e 00 00 01 movb $0x1,0x2e9d(%rip) # 4010 <__TMC_END__>1173: 5d pop %rbp1174: c3 ret 1175: 0f 1f 00 nopl (%rax)1178: c3 ret 1179: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)0000000000001180 <frame_dummy>:1180: f3 0f 1e fa endbr64 1184: e9 77 ff ff ff jmp 1100 <register_tm_clones>0000000000001189 <mul>:1189: f3 0f 1e fa endbr64 118d: 55 push %rbp118e: 48 89 e5 mov %rsp,%rbp1191: 89 7d fc mov %edi,-0x4(%rbp)1194: 89 75 f8 mov %esi,-0x8(%rbp)1197: 8b 45 fc mov -0x4(%rbp),%eax119a: 0f af 45 f8 imul -0x8(%rbp),%eax119e: 5d pop %rbp119f: c3 ret 00000000000011a0 <main>:11a0: f3 0f 1e fa endbr64 11a4: 55 push %rbp11a5: 48 89 e5 mov %rsp,%rbp11a8: 48 83 ec 20 sub $0x20,%rsp11ac: 89 7d ec mov %edi,-0x14(%rbp)11af: 48 89 75 e0 mov %rsi,-0x20(%rbp)11b3: c7 45 f8 0a 00 00 00 movl $0xa,-0x8(%rbp)11ba: c7 45 fc 05 00 00 00 movl $0x5,-0x4(%rbp)11c1: 8b 55 fc mov -0x4(%rbp),%edx11c4: 8b 45 f8 mov -0x8(%rbp),%eax11c7: 89 d6 mov %edx,%esi11c9: 89 c7 mov %eax,%edi11cb: e8 a0 fe ff ff call 1070 <add@plt>11d0: 89 c1 mov %eax,%ecx11d2: 8b 55 fc mov -0x4(%rbp),%edx11d5: 8b 45 f8 mov -0x8(%rbp),%eax11d8: 89 c6 mov %eax,%esi11da: 48 8d 05 23 0e 00 00 lea 0xe23(%rip),%rax # 2004 <_IO_stdin_used+0x4>11e1: 48 89 c7 mov %rax,%rdi11e4: b8 00 00 00 00 mov $0x0,%eax11e9: e8 92 fe ff ff call 1080 <printf@plt>11ee: 8b 55 fc mov -0x4(%rbp),%edx11f1: 8b 45 f8 mov -0x8(%rbp),%eax11f4: 89 d6 mov %edx,%esi11f6: 89 c7 mov %eax,%edi11f8: e8 93 fe ff ff call 1090 <sub@plt>11fd: 89 c1 mov %eax,%ecx11ff: 8b 55 fc mov -0x4(%rbp),%edx1202: 8b 45 f8 mov -0x8(%rbp),%eax1205: 89 c6 mov %eax,%esi1207: 48 8d 05 04 0e 00 00 lea 0xe04(%rip),%rax # 2012 <_IO_stdin_used+0x12>120e: 48 89 c7 mov %rax,%rdi1211: b8 00 00 00 00 mov $0x0,%eax1216: e8 65 fe ff ff call 1080 <printf@plt>121b: 8b 55 fc mov -0x4(%rbp),%edx121e: 8b 45 f8 mov -0x8(%rbp),%eax1221: 89 d6 mov %edx,%esi1223: 89 c7 mov %eax,%edi1225: e8 5f ff ff ff call 1189 <mul>122a: 89 c1 mov %eax,%ecx122c: 8b 55 fc mov -0x4(%rbp),%edx122f: 8b 45 f8 mov -0x8(%rbp),%eax1232: 89 c6 mov %eax,%esi1234: 48 8d 05 e5 0d 00 00 lea 0xde5(%rip),%rax # 2020 <_IO_stdin_used+0x20>123b: 48 89 c7 mov %rax,%rdi123e: b8 00 00 00 00 mov $0x0,%eax1243: e8 38 fe ff ff call 1080 <printf@plt>1248: b8 00 00 00 00 mov $0x0,%eax124d: c9 leave 124e: c3 ret Disassembly of section .fini:0000000000001250 <_fini>:1250: f3 0f 1e fa endbr64 1254: 48 83 ec 08 sub $0x8,%rsp1258: 48 83 c4 08 add $0x8,%rsp125c: c3 ret
看一下main函数中的调用语句:
#11cb: e8 a0 fe ff ff call 1070 <add@plt>11d0: 89 c1 mov %eax,%ecx11d2: 8b 55 fc mov -0x4(%rbp),%edx11d5: 8b 45 f8 mov -0x8(%rbp),%eax11d8: 89 c6 mov %eax,%esi#1225: e8 5f ff ff ff call 1189 <mul>122a: 89 c1 mov %eax,%ecx122c: 8b 55 fc mov -0x4(%rbp),%edx122f: 8b 45 f8 mov -0x8(%rbp),%eax1232: 89 c6 mov %eax,%esi
所以说,我们的调用方式是看不出任何问题的,因为我们说了区别在于调用函数之后的处理方式。
我们将mul和add(或sub)拉出来对比
我们发现动态库中的函数后面会有一个@plt后缀,而且执行的语句也不一样。调用mul函数后,利用push压栈,mov赋值,pop出栈等常规操作处理该函数,但调用add函数后,利用的时bnd jmp跳转到动态库中进行操作。@plt是一个与程序链接和运行时符号解析相关的概念
PLT(Procedure linkage Table--过程链接表)是动态链接的一个结构,主要用于解决动态函数调用(尤其是未解析的外部函数)。工作原理:
动态链接:当一个程序调用一个动态链接库中的函数时,这个函数的地址在编译时并不确定,因此在编译时并没有直接的地址。相反,编译器写入指向 PLT 中相应入口的调用。
PLT 表项:每个外部函数(例如,一个来自共享库的函数)都有一个 PLT 表项。当一个函数被调用时,程序首先跳转到 PLT 表项。
首次调用:在首次调用该函数时,PLT 将控制权传递给动态链接器(通常是
ld-linux.so
),该链接器负责解析该函数的实际地址。这时动态链接器会更新 PLT 中的表项,将实际地址填入,以便后续调用可以直接跳转到该地址。性能优势:使用 PLT,可以在不需要提前链接所有外部符号的情况下启动和运行程序,简化了链接过程并支持共享库的动态更新
当程序第一次调用 动态库函数add
时,它会跳转到 add 的 PLT 条目,这样动态链接器会处理真正的地址解析。
可以使用 objdump
或 gdb
等工具查看编译后的程序的 PLT 表。例如,使用以下命令:
objdump -D test.shared | grep add
这是运行结果截出来的两段内容,这就是程序跟add有关的PLT表,显示了add在PLT中的地址
因为调用动态库的函数比程序文件自己的函数慢,所以有些地方会描述动态库为延迟绑定。
结论:生成.o文件时要求生成与位置无关的代码,也就是加-fPIC选项的目的。
假设我们有多个目标文件,每个文件都有一个全局变量count
。链接器会将这些变量合并,只保留一份,并分配一个内存地址,之后所有对count
的引用将在运行时替换为这个地址。
感谢大家!!!
相关文章:

【Linux】虚拟空间布局模型地址回填数据段合并(万字详解)
Ⅰ、虚拟空间布局模型 理论模型 包括上节的动态库与静态库,加上本节后面两个内容其实都是对gcc的扩展与补充知识,也是需要了解和掌握的知识。在开讲之前,我们先来说一下在32位x86的Linux系统中,虚拟地址空间布局模型:…...

const和修饰指针的几种用法
昨天闲着没事去面试了一个C岗位,问了很多基础的东西都没答上来。主要原因是这些知识在硬件资源丰富的pc端用的不多,二来确实很久没温习之前的C相关的知识了。在面试官问了几次类似的问题没有答好的情况下(还喜欢问你确不确定)&…...

mybatis事务的自动提交与手动提交
MyBatis支持自动提交和手动提交两种事务管理方式。 自动提交事务 MyBatis默认使用自动提交模式,即每个SQL操作都会自动提交到数据库中。这意味着在执行完一条SQL语句后,MyBatis会自动调用commit()方法将更改持久化到数据库。 手动提交事务 可以通过Sq…...

网络安全协议之比较(SSH、PKI、SET、SSL)
一、SSH介绍 什么是SSH? 传统的网络服务程序,如:ftp、pop和telnet在本质上都是不安全的,因为它们在网络上用明文传送口令和数据, 别有用心的人非常容易就可以截获这些口令和数据。而且,这些服务程序的…...

Vue的生命周期方法
Vue 生命周期方法详解 beforeCreate 执行时机:在实例初始化之后,数据观测(data observer)和事件配置(event/watcher setup)之前被调用。内部状态:此时,组件的选项对象(例…...

ISP和IQ调试(一)
系列文章目录 文章目录 系列文章目录前言一、ISP(image signal process)二、ISP位置三、IQ总结 前言 一、ISP(image signal process) image signal process 图像处理技术 image signal processor 图像信号处理器 设备 什么是图像信号? 代表…...

c# TaskScheduler
这里记录下 TaskScheduler 的简单用法。 使用场景: 使用 Task 的时候,大家知道用 TaskFactory.StartNew 可以用来创建一个 Task 。这里如果创建了 3 个,那么这3个 Task 就各自放飞直接运行了。 class Program {private static TaskFactory…...

可视化数据
数据科学家会直观呈现数据,以更好地理解数据。 他们可以扫描原始数据、检查摘要度量值(如平均值)或绘制数据图表。 图表是一种可视化数据的强有力方式,数据科学家经常使用图表快速了解适度复杂的模式。 直观地表示数据 绘制图表…...

【Redis】Redis缓存击穿
1. 概述 缓存击穿:缓存击穿问题也叫热点key问题,一个高并发的key或重建缓存耗时长(复杂)的key失效了,此时大量的请求给数据库造成巨大的压力。如下图,线程1还在构建缓存时,线程2,3&…...

厦门凯酷全科技有限公司深耕抖音电商运营
在数字经济飞速发展的今天,抖音电商平台以其独特的社交属性和庞大的用户基础,迅速成为众多品牌和商家的新战场。在这个充满机遇与挑战的市场中,厦门凯酷全科技有限公司凭借其专业的服务、创新的理念和卓越的执行力,成为了抖音电商…...

六西格玛DMAIC在企业得项目管理中有什么作用
六西格玛(Six Sigma)是一种以数据为基础的管理方法,旨在通过减少缺陷和变异来提高过程质量和效率。DMAIC 是六西格玛中一种常用的改进方法论,适用于现有过程的改进。DMAIC 代表五个阶段:定义(Define&#x…...

vscode借助插件调试OpenFoam的正确的.vscode配置文件
正确的备份文件位置: /home/jie/桌面/理解openfoam/正确的调试爆轰单进程案例/mydebugblastFoam 调试爆轰案例流体 并且工作区和用户区都是openfoam-7版本 问题:F5以debug模式启动后不停在断点 解决方法: 这里备份一下.vsode正确的配置&…...

SpringBoot整合JWT(JSON Web Token)生成token与验证
目录 JWT 什么是JWT JWT使用流程 确定要传递的信息: 生成JWT: JWT传输: 客户端保存JWT: 客户端发送JWT: 服务器验证JWT: 服务器响应: Token的使用示例: 工具类 R结果集 返回一个生成的token 创建拦截器 JWT 什么是JWT JWT(JSON Web Token)是是目前最…...

把帕拉丁需要的.rom文件转成.bin
# 输入文件名 input_file_name = fw_payload.bin.rom # 输出文件名 output_file_name = fw_payload.bin.rom2 # 打开输出文件,准备写入翻转后的十六进制字符串 with open(output_file_name, w) as output_file: # 打开输入文件读取十六进制字符串 with open(input_f…...

Nginx 缓存那些事儿:原理、配置和最佳实践
Nginx 缓存那些事儿:原理、配置和最佳实践 在当今的互联网世界,网站的访问量和数据处理量不断攀升,如何确保用户能够快速、稳定地访问我们的网站,已经成为每个运维工程师面临的挑战。幸运的是,Nginx 作为一款高性能的…...

vue发展史
Vue.js发展史 Vue.js是一个渐进式JavaScript框架,自发布以来受到了广泛的关注和喜爱。以下是Vue.js的发展史: 1. 起源(2013年) Vue.js的创始人尤雨溪(Evan You)在2013年开始构思这个项目。当时࿰…...

基于Java和Vue开发的校园跑腿软件校园跑腿小程序系统源码
市场前景 学生需求多样化: 随着校园生活节奏的加快和学生需求的多样化,跑腿服务逐渐成为一种新兴的商业模式。学生群体对于便捷、高效的日常服务需求不断增加,如外卖送餐、快递代取、文件传递等。市场规模持续增长: 大学校园作为…...

MySQL(五)--- 事务
1、CURD操作不加控制时,可能会出现什么问题 即:类似于线程安全问题,可能会导致数据不一致问题。 因为,MySQL内部本身就是多线程服务。 1.1、CURD满足什么属性时,才能避免上述问题 1、买票的过程得是原子的吧。 2、买票互相应该不能影响吧。 3、买完票应该要永久有效吧。…...

llm chat场景下的数据同步
背景 正常的chat/im通常是有单点登录或者利用类似广播的机制做多设备间内容同步的。而且由于长连接的存在,数据同步(想起来)相对简单。而llm的chat在缺失这两个机制的情况下,没见到特别好的做到了数据同步的产品。 llm chat主要两…...

机器学习经典算法
机器学习经典算法学习和分享。 k近邻算法 线性回归 梯度下降法 PCA主成分分析法 多项式回归 逻辑回归 支撑向量机SVM 决策树 随机森林 评价分类指标...

Scala中的泛型
类型参数 ---- 泛型(数据类型是变化的) (1) 可以有多个 (2) 名称合法就行,没有固定的,一般用T(Type) 在Scala中,用[]表示。在Java中用<>表示 1. 与数据类型的区别 List是数据类型,表示一个列表。[Int]表示泛型,它…...

数据分析特征标准化方法及其Python实现
数据分析特征标准化方法及其Python实现 1、概述 在数据分析中,对特征进行标准化主要是: 1、消除量纲影响 不同特征可能具有不同的量纲和数量级。 例如,一个特征可能是以米为单位的长度,而另一个特征可能是以秒为单位的时间。直接使用这些具有不同量纲的原始数据进行分析…...

UnityShaderLab 实现程序化形状(一)
1.实现一个长宽可变的矩形: 代码: fixed4 frag (v2f i) : SV_Target{return saturate(length(saturate(abs(i.uv - 0.5)-0.13)))/0.03;} 2.实现一个半径可变的圆形: 代码: fixed4 frag (v2f i) : SV_Target{return (distance(a…...

前端数据安全防护(控制台)
目录 前言 禁用右键菜单 禁用快捷键 监控控制台 完整逻辑 前言 前端的数据在浏览器中一直处于一个裸奔的状态,只要是稍微懂一点计算机的人,都可以在浏览器的控制台中拿到前端页面的所有数据,包括和后端的交互数据。为了…...

自己玩虚拟机:vagrant,virtual box,centos
vagrant 访问Vagrant官网 https://www.vagrantup.com/ 点击Download Windows,MacOS,Linux等 选择对应的版本 AMD64 (x86_64) I686 (x86) 傻瓜式安装 命令行输入vagrant,测试是否安装成功 vagrant -v 可以查看当前版本 virtual box 访…...

Frida框架HOOK RegisterNatives函数
使用Frida框架HOOK RegisterNatives函数,获取动态注册的函数地址、名称、签名、class名称、所属的so文件名称、so文件加载基址、函数在so文件中的地址。 废话不多说,上代码: 运行命令:frida -U -f in.****** -l RegisterNatives…...

[创业之路-189]:《华为战略管理法-DSTE实战体系》-2- 生存与发展的双重旋律:短期与长期、战术与战略的交响乐章
目录 生存与发展的双重旋律:短期与长期、战术与战略的交响乐章 一、生存:短期视角下的战术布局 二、发展:长期视角下的战略规划 三、短期与长期、战术与战略的融合与平衡 四、结语:在生存与发展的交响曲中奏响辉煌 生存与发展…...

TDengine 部署
TDengine是一款开源高性能的时序数据库,其部署过程可以根据不同的环境和需求进行灵活配置。以下将详细介绍TDengine的部署步骤,包括单节点部署和集群部署。 一、单节点部署 下载安装包: 访问TDengine的官方网站或GitHub仓库,下载…...

【前端】20种 Button 样式
20种 Button 样式 在前端开发中,Button 按钮的样式设计是提升用户交互体验的重要一环。以下是20种常见的Button样式,这些样式主要基于CSS实现,可以根据具体需求进行调整和组合。 1. 默认样式 CSS 样式:.button { background-co…...

机器人构建详解:售前售后服务客服机器人与广告生成机器人的微调数据处理方法
引言 大模型(如BERT、GPT等)在自然语言处理任务中展现了强大的能力,但为了使其更贴合特定应用场景,通常需要进行微调。本文将详细讲解如何为售前售后服务的客服机器人和广告生成机器人准备高质量的微调数据,并通过具体…...