1. 目标文件

编译和链接这两个步骤,在Windows下被我们的IDE封装的很完美,我们⼀般都是⼀键构建⾮常⽅便, 但⼀旦遇到错误的时候呢,尤其是链接相关的错误,很多⼈就束⼿⽆策了。在Linux下,我们之前也学 习过如何通过gcc编译器来完成这⼀系列操作。

接下来我们深⼊探讨⼀下编译和链接的整个过程,来更好的理解动静态库的使⽤原理。
先来回顾下什么是编译呢?编译的过程其实就是将我们程序的源代码翻译成CPU能够直接运⾏的机器
代码。
⽐如:在⼀个源⽂件 hello.c ⾥便简单输出"hello world!",并且调⽤⼀个run函数,⽽这个函数被
定义在另⼀个原⽂件 code.c 中。这⾥我们就可以调⽤ gcc -c 来分别编译这两个原⽂件。

// hello.c
#include<stdio.h>
void run();
int main() {printf("hello world!\n");run();return 0;
}
// code.c
#include<stdio.h>
void run() {printf("running...\n");
}
// 编译两个源⽂件 
$ gcc -c hello.c
$ gcc -c code.c
$ ls
code.c code.o hello.c hello.o

可以看到,在编译之后会⽣成两个扩展名为 .o 的⽂件,它们被称作⽬标⽂件。要注意的是如果我们 修改了⼀个原⽂件,那么只需要单独编译它这⼀个,⽽不需要浪费时间重新编译整个⼯程。⽬标⽂件 是⼀个⼆进制的⽂件,⽂件的格式是 ELF ,是对⼆进制代码的⼀种封装。

$ file hello.o 
hello.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped
## file命令⽤于辨识⽂件类型。

2. ELF文件

要理解编译链链接的细节,我们不得不了解⼀下ELF⽂件。其实有以下四种⽂件其实都是ELF⽂件:

• 可重定位⽂件(Relocatable File) :即xxx.o⽂件。包含适合于与其他⽬标⽂件链接来创 建可执⾏⽂件或者共享⽬标⽂件的代码和数据。

• 可执⾏⽂件(Executable File) :即可执⾏程序。

• 共享⽬标⽂件(Shared Object File) :即xxx.so⽂件。

• 内核转储(core dumps) ,存放当前进程的执⾏上下⽂,⽤于dump信号触发。

⼀个ELF⽂件由以下四部分组成:

• ELF头(ELF header) :描述⽂件的主要特性。其位于⽂件的开始位置,它的主要⽬的是定位⽂ 件的其他部分。

• 程序头表(Program header table) :列举了所有有效的段(segments)和他们的属性。表⾥ 记着每个段的开始的位置和位移(offset)、⻓度,毕竟这些段,都是紧密的放在⼆进制⽂件中, 需要段表的描述信息,才能把他们每个段分割开。

• 节头表(Section header table) :包含对节(sections)的描述。

• 节(Section ):ELF⽂件中的基本组成单位,包含了特定类型的数据。ELF⽂件的各种信息和 数据都存储在不同的节中,如代码节存储了可执⾏代码,数据节存储了全局变量和静态数据等。

最常⻅的节:

• 代码节(.text):⽤于保存机器指令,是程序的主要执⾏部分。

• 数据节(.data):保存已初始化的全局变量和局部静态变量。

 3.  ELF从形成到加载轮廓

3.1 ELF形成可执行

• step-1:将多份 C/C++ 源代码,翻译成为⽬标 .o ⽂

• step-2:将多份 .o ⽂件section进⾏合并

注意: 实际合并是在链接时进⾏的,但是并不是这么简单的合并,也会涉及对库合并,此处不做
过多追究 

3.2 ELF可执行文件加载

• ⼀个ELF会有多种不同的Section,在加载到内存的时候,也会进⾏Section合并,形成segment

• 合并原则:相同属性,⽐如:可读,可写,可执⾏,需要加载时申请空间等.

• 这样,即便是不同的Section,在加载到内存中,可能会以segment的形式,加载到⼀起

• 很显然,这个合并⼯作也已经在形成ELF的时候,合并⽅式已经确定了,具体合并原则被记录在了 ELF的 程序头表(Program header table) 中

这里认识一个查看可执行程序的section命令:readelf -S

查看section合并的segment命令:readelf -l

为什么要将section合并成为segment??

• Section合并的主要原因是为了减少⻚⾯碎⽚,提⾼内存使⽤效率。如果不进⾏合并, 假设⻚⾯⼤⼩为4096字节(内存块基本⼤⼩,加载,管理的基本单位),如果.text部分 为4097字节,.init部分为512字节,那么它们将占⽤3个⻚⾯,⽽合并后,它们只需2个 ⻚⾯。

• 此外,操作系统在加载程序时,会将具有相同属性的section合并成⼀个⼤的 segment,这样就可以实现不同的访问权限,从⽽优化内存管理和权限访问控制。

对于 程序头表 和 节头表 ⼜有什么⽤呢??其实ELF ⽂件提供2个不同的视图/视⻆来让我们理解这两个部分:
• 链接视图(Linking view) -对应节头表 Section header table 
◦ ⽂件结构的粒度更细,将⽂件按功能模块的差异进⾏划分,静态链接分析的时候⼀般关注的是链接视图,能够理解ELF⽂件中包含的各个部分的信息。
◦ 为了空间布局上的效率,将来在链接⽬标⽂件时,链接器会把很多节(section)合并,规整成可执⾏的段(segment)、可读写的段、只读段等。合并了后,空间利⽤率就⾼了,否
则,很⼩的很⼩的⼀段,未来物理内存⻚浪费太⼤(物理内存⻚分配⼀般都是整数倍⼀块给
你,⽐如4k),所以,链接器趁着链接就把⼩块们都合并了。
• 执⾏视图(execution view) -对应程序头表 Program header table

告诉操作系统,如何加载可执⾏⽂件,完成进程内存的初始化。⼀个可执⾏程序的格式中,⼀定有 program header table 。
• 说⽩了就是:⼀个在链接时作⽤,⼀个在运⾏加载时作⽤。 

从链接视图来看:
• 命令 readelf -S hello.o 可以帮助查看ELF⽂件的节头表。

• .text节 :是保存了程序代码指令的代码节。

• .data节 :保存了初始化的全局变量和局部静态变量等数据。

• .rodata节 :保存了只读的数据,如⼀⾏C语⾔代码中的字符串。由于.rodata节是只读的,所以只能存在于⼀个可执⾏⽂件的只读段中。因此,只能是在text段(不是data段)中找到.rodata节。

• .BSS节 :为未初始化的全局变量和局部静态变量预留位置

• .symtab节 :Symbol Table符号表,就是源码⾥⾯那些函数名、变量名和代码的对应关系。

• .got.plt节 (全局偏移表-过程链接表):.got节保存了全局偏移表。.got节和.plt节⼀起提供了对导⼊的共享库函数的访问⼊⼝,由动态链接器在运⾏时进⾏修改。对于GOT的理解,我们后⾯说。

◦ 使⽤ readelf 命令查看.so⽂件可以看到该节。

从执⾏视图来看:
• 告诉操作系统哪些模块可以被加载进内存。
• 加载进内存之后哪些分段是可读可写,哪些分段是只读,哪些分段是可执⾏的。 

// 查看⽬标⽂件 
$ readelf -h hello.o 
ELF Header:Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 Class: ELF64 # ⽂件类型Data: 2's complement, little endian # 指定的编码⽅式Version: 1 (current)OS/ABI: UNIX - System VABI Version: 0Type: REL (Relocatable file) # 指出ELF⽂件的类型Machine: Advanced Micro Devices X86-64 # 该程序需要的体系结构Version: 0x1Entry point address: 0x0 # 系统第⼀个传输控制的虚拟地址,在那启动进程。假如⽂件没有如何关联的⼊⼝点,该成员就保持为0。Start of program headers: 0 (bytes into file)Start of section headers: 728 (bytes into file)Flags: 0x0Size of this header: 64 (bytes) # 保存着ELF头⼤⼩(以字节计数)Size of program headers: 0 (bytes) # 保存着在⽂件的程序头表(program header table)中⼀个⼊⼝的⼤⼩Number of program headers: 0 # 保存着在程序头表中⼊⼝的个数。因此,e_phentsize和e_phnum的乘机就是表的⼤⼩(以字节计数).假如没有程序头表,变量为0。
Size of section headers: 64 (bytes) # 保存着section头的⼤⼩(以字节计数)。⼀个section头是在section头表的⼀个⼊⼝ Number of section headers: 13 # 保存着在section header 
table中的⼊⼝数⽬。因此,e_shentsize和e_shnum的乘积就是section头表的⼤⼩(以字节计数)。假如⽂件没有section头表,值为0。 Section header string table index: 12 # 保存着跟section名字字符表相关⼊⼝的section头表(section header table)索引。

对于 ELF HEADER 这部分来说,我们只⽤知道其作⽤即可,它的主要⽬的是定位⽂件的其他部分。         

4. 静态链接

> 无论是自己的.o还是静态库中的.o,本质都是把.o文件进行合并的过程!

> 所以,研究静态链接,本质是研究.o是如何链接的。

下面我们先来做一个小实验:

以下我们创建了两个.c文件:

 我们知道这俩个文件要编译形成可执行就必须先各自形成.o文件,在链接形成可执行。

 好,接下来我们来认识一个命令->objdump -d,该命令可以将代码段进行反汇编查看!        

 我们可以看到,这两个文件call指令对应调用printf和run函数,但是,我们会发现,它们对应的跳转地址都被设成了0。这是为什么呢?

其实,在编译hell.c和code.c时,编译器完全不知道printf和run函数的存在,比如它们位于内存的哪个区块,代码长什么样都是不知道的!因此,编译器只能暂时将它们的跳转地址设置为0。

而这些地址事实上会在链接的时候被修正!    

我们也可以通过readelf -s命令来读取文件的符号表:

printf底层调用的就是puts,我们看到,UND就是undefined,表示在本.o文件中找不到该函数。

我们再看形成的可执行文件的符号表:

 16:就是run函数所在的section被合并最终的那⼀个section中了,16就是下标。

因此,我们可以得出结论:两个.O文件的代码段合并到了一起,并同一进行编址,链接的时候,会修正没有确定的函数地址,在合并完成之后,进行相关call地址,完成函数的调用。

静态链接就是把库中的.o进行合并,和上述过程⼀样。

所以链接其实就是将编译之后的所有目标文件连同用到的⼀些静态库运行时库组合,拼装成⼀个独立的可执行文件。其中就包括我们之前提到的地址修正,当所有模块组合在⼀起之后,链接器会根据我们的.o文件或者静态库中的重定位表找到那些需要被重定位的函数全局变量,从而修正它们的地址。这其实就是静态链接的过程。

所以,链接过程中会涉及到对.o中外部符号进行地址重定位。 【因此,.o文件也叫重定位文件

5. ELF加载与进程地址空间

5.1 虚拟地址/逻辑地址

> 问:一个ELF程序没有被加载到内存时,有没有地址呢?

答:⼀个ELF程序,在没有被加载到内存的时候,本来就有地址,当代计算机⼯作的时候,都采用"平坦模式"进行⼯作。所以也要求ELF对自己的代码和数据进行统⼀编址,下⾯是 objdump -S 反汇编之后的代码

最左侧的就是ELF的虚拟地址,其实,严格意义上应该叫做逻辑地址(起始地址+偏移量),但是我们 认为起始地址是0.也就是说,其实虚拟地址在我们的程序还没有加载到内存的时候,就已经把可执执行程序进行统⼀编址了.

> 问:进程mm_struct、vm_area_struct在进程刚刚创建的时候,初始化数据从哪⾥来的?

答:从ELF各个 segment来,每个segment有自己的起始地址和自己的长度,用来初始化内核结构中的[start,end] 等范围数据,另外在用详细地址,填充页表.

所以:虚拟地址机制,不光光OS要⽀持,编译器也要⽀持.

5.2 重新理解进程虚拟地址空间

可执行程序在加载到物理内存的同时,系统自动创建进程【对应的PCB task_struckt】,其中系统会把可执行程序中的各个segment的逻辑地址来初始化mm_struct中的各区域地址,并且同时初始化页表中的虚拟地址。在物理内存中,每条代码和数据都有其物理地址,而这些地址也被用来初始化页表中的物理地址,从而建立页表中虚拟地址和物理地址的映射关系

> 那cpu是如何知道可执行程序的入口呢?

ELF在被编译好之后,会把自己未来程序的入口地址记录在ELF header的Entry字段中,cup通过系统自动读取到程序的入口地址【注意,该字段在ELF header中,是被编译器处理过的逻辑地址,也就是虚拟地址】,所以cpu还要通过页表来查找到实际的物理地址,最后再开始执行程序!【所以,cup寄存器中拿到的地址全部都是虚拟地址】。

 结合文件系统部分的知识,先找到磁盘中的文件的整体程序执行逻辑。入下图所示:

5.3 进程如何看待动态库

我们之前就知道了,可执行程序在合并时,如果需要用到动态库,动态库是不会被一起合并的!所以,如果进程需要找到动态库,那么动态库也需要加载到物理内存,库依旧是ELF文件,其加载过程和上面所述一样。不同之处在于:进程地址空间中,程序在调用库函数时,需要从代码区跳转到共享区,然后查找页表找到库函数。调用结束后,调转回代码区!

> 一个进程看待动态库如此,那2个多个进程又如何呢?

其实也非常简单,一张图就能明白。在实际物理内存中,只有一个动态库被加载进来,但每个进程都可以找到这个动态库。如此一来就实现了动态库在物理内存中只有一份,这也就节省了物理内存空间!【这也是动态库也称为共享库的原因】        

6. 动态链接 

6.1 动态链接到底是如何工作的

⾸先要交代⼀个结论,动态链接实际上将链接的整个过程推迟到了程序加载的时候。比如我们去运行⼀个程序,操作系统会⾸先将程序的数据代码连同它⽤到的⼀系列动态库先加载到内存,其中每个动态库的加载地址都是不固定的,操作系统会根据当前地址空间的使⽤情况为它们动态分配⼀段内存。 当动态库被加载到内存以后,⼀旦它的内存地址被确定,我们就可以去修正动态库中的哪些函数跳转地址了。

> 我们的可执行程序实际上是被编译器动了手脚的!!

在C/C++程序中,当程序开始运行时,首先并不会直接跳转到main函数。实际上,程序的入口点是_start,这是⼀个由C运行时库(通常是glibc)或链接器(如ld)提供的特殊函数。 

在 _start 函数中,会执⾏⼀系列初始化操作,这些操作包括:

1. 设置堆栈:为程序创建⼀个初始的堆栈环境。

2. 初始化数据段:将程序的数据段(如全局变量和静态变量)从初始化数据段复制到相应的内存位 置,并清零未初始化的数据段。

3. 动态链接:这是关键的⼀步, _start 函数会调⽤动态链接器的代码来解析和加载程序所依赖的 动态库(shared libraries)。动态链接器会处理所有的符号解析和重定位,确保程序中的函数调 ⽤和变量访问能够正确地映射到动态库中的实际地址。

动态链接器:

◦ 动态链接器(如ld-linux.so)负责在程序运⾏时加载动态库。

◦ 当程序启动时,动态链接器会解析程序中的动态库依赖,并加载这些库到内存中。

环境变量和配置⽂件:

◦ Linux系统通过环境变量(如LD_LIBRARY_PATH)和配置⽂件(如/etc/ld.so.conf及其⼦配置 ⽂件)来指定动态库的搜索路径

◦ 这些路径会被动态链接器在加载动态库时搜索。

缓存⽂件:

◦ 为了提⾼动态库的加载效率,Linux系统会维护⼀个名为/etc/ld.so.cache的缓存⽂件。

◦ 该⽂件包含了系统中所有已知动态库的路径和相关信息,动态链接器在加载动态库时会⾸先 搜索这个缓存⽂件。

4. 调⽤ __libc_start_main :⼀旦动态链接完成, _start 函数会调⽤ __libc_start_main (这是glibc提供的⼀个函数)。 __libc_start_main 函数负责执⾏ ⼀些额外的初始化⼯作,⽐如设置信号处理函数、初始化线程库(如果使⽤了线程)等。

5. 调⽤ main 函数:最后, __libc_start_main 函数会调⽤程序的 main 函数,此时程序的执 ⾏控制权才正式交给⽤⼾编写的代码。

6. 处理 main 函数的返回值:当 main 函数返回时, __libc_start_main 会负责处理这个返回 值,并最终调⽤ _exit 函数来终⽌程序。

6.2 程序怎么进行库函数调用

• 库已经被我们映射到了当前进程的地址空间中

• 库的虚拟起始地址我们也已经知道了

• 库中每⼀个方法的偏移量地址我们也知道

• 所有:访问库中任意方法,只需要知道库的起始虚拟地址+方法偏移量即可定位库中的方法

• 而且:整个调用过程,是从代码区跳转到共享区,调用完毕在返回到代码区,整个过程完全在进程地址空间中进行的.

程序运行之前,所有的库都被加载到内存中,并建立映射关系,所有库的虚拟地址都是提前知道的【这在编译时就已经完成,动态库和ELF文件一样采用相对编址的方式】,然后,我们对加载到内存中的程序的库函数调用进行地址修改,在内存中完成二次地址修改【这个过程叫做加载地址重定位】。

 > 但是,代码区不是只读的吗?这又是如何做到修改呢?

所以,动态链接采用的方法是:在.data区中,专门预留一块区域来存放函数的跳转地址,他也被叫全局偏移表GOT,表中每⼀项都是本运行模块要引用的⼀个全局变量或函数的地址。

至此,程序在进行库函数调用时,不再关心加载时被修改的函数地址只需要查表即可。而GOT表在数据区,可以方便二次编址时修改。【GOT表中开始有每个库函数的和其对应的动态库,当动态库加载时,这些库函数的地址就会被修正】。

> 这种方式实现的动态链接就被叫做 PIC 地址无关代码 。换句话说,我们的动态库不需要做任何修 改,被加载到任意内存地址都能够正常运行,并且能够被所有进程共享,这也是为什么之前我们给编译器指定-fPIC参数的原因,PIC=相对编址+GOT

7. 总结

• 静态链接的出现,提高了程序的模块化水平。对于⼀个大的项目,不同的人可以独立地测试和开发自己的模块。通过静态链接,生成最终的可执行文件。

• 我们知道静态链接会将编译产生的所有目标文件,和用到的各种库合并成⼀个独立的可执行文件, 其中我们会去修正模块间函数的跳转地址,也被叫做编译重定位(也叫做静态重定位)。

• 而动态链接实际上将链接的整个过程推迟到了程序加载的时候。比如我们去运行⼀个程序,操作系统会首先将程序的数据代码连同它用到的⼀系列动态库先加载到内存,其中每个动态库的加载地址都是不固定的,但是无论加载到什么地方,都要映射到进程对应的地址空间,然后通过.GOT方式进行调用(运行重定位,也叫做动态地址重定位)。

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

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

相关文章

QML事件处理:鼠标、拖拽与键盘事件

在QML应用开发中&#xff0c;用户交互是构建动态界面的核心。本文将全面解析QML中的三大交互事件&#xff1a;鼠标事件、拖拽事件和键盘事件&#xff0c;通过实际代码示例展示如何实现丰富的用户交互体验。一、鼠标事件处理1. MouseArea基础MouseArea是QML中处理鼠标交互的核心…

MySQL 8.0 OCP 1Z0-908 题目解析(20)

题目77 Choose the best answer. Which step or set of steps can be used to rotate the error log? ○ A) Execute SET GLOBAL max_error_count . ○ B) Rename the error log file on disk, and then execute FLUSH ERROR LOGS. ○ C) Execute SET GLOBAL log_error ‘’…

八股学习(四)---MySQL

一、MySQL如何进行SQL调优&#xff1f;我的回答&#xff1a;面试官好&#xff01;我想从SQL语句本身和数据库结构两方面来做MySQL的SQL调优。首先会优化SQL写法&#xff0c;比如避免用SELECT *、减少子查询嵌套&#xff0c;用JOIN代替&#xff0c;还有合理使用索引&#xff0c;…

华中科大首创DNN衍射量子芯片登《Science Advances》:3D打印实现160μm³高维逻辑门

01 前言华中科技大学王健/刘骏团队在《Science Advances》发表突破性研究&#xff0c;利用飞秒激光三维打印技术&#xff0c;制造出全球首个聚合物基超紧凑高维量子光芯片。该芯片仅160微米见方&#xff08;约头发丝直径的1.5倍&#xff09;&#xff0c;却实现了光子空间模式的…

【排序】插入排序

如果你已经对排序略知一二&#xff0c;现在正在复习排序的一些重点知识 ------------------------------------------------------------------------------------------------------------------------- 点赞收藏&#x1f308;&#xff0c;每天更新总结文章&#xff08;多以图…

扣子Coze怎么模仿人类输出(分段输出)?

效果&#xff1a; 让AI回复的更像人类 教程&#xff1a; 工作流&#xff1a; 假设大模型节点就是需要的回复&#xff0c;并且已经按句号&#xff08;。&#xff09;区别开每句话 后面连接一个 文本处理 节点&#xff0c;选择“字符串分隔”&#xff0c;按“。”进行分割 分…

Android 应用开发 | 一种限制拷贝速率解决因 IO 过高导致系统卡顿的方法

文章目录一、问题背景二、代码实现一、问题背景 经常做 Android 应用的小伙伴应该会有经验&#xff0c;就是如果应用在写入文件的时候&#xff0c;即使写文件的动作是在子线程&#xff0c;也会出现 UI 上的卡顿&#xff0c;这是因为文件的 IO 是由内核去完成的&#xff0c;此时…

力扣面试150(19/150)

7.7 12. 整数转罗马数字 七个不同的符号代表罗马数字&#xff0c;其值如下&#xff1a; 符号值I1V5X10L50C100D500M1000 罗马数字是通过添加从最高到最低的小数位值的转换而形成的。将小数位值转换为罗马数字有以下规则&#xff1a; 如果该值不是以 4 或 9 开头&#xff0c;…

数据结构与算法——从递归入手一维动态规划【1】

前言&#xff1a; 简单记录对左程云系列算法课程--算法讲解066【必备】的学习&#xff0c;这是第一篇。主要提供C代码和一些简单的个人理解&#xff0c;如需要细致讲解请移步原视频。 涉及内容&#xff1a; 斐波那契数列、动态规划 参考视频&#xff1a; 左程云--算法讲解…

搭建个人博客系列--Nacos 注册中心

基础项目已完成&#xff0c;接下来就是SpringCloud的各种组件了。 那你又要问&#xff1a;既然有Nacos为什么之前还装了Apollo&#xff1f; 那你别管&#xff0c;那不得什么都会点&#xff0c;不然怎么找工作。干就完了。 一、安装Nacos 管他三七二十一&#xff0c;先在doc…

前端实习总结——案例与大纲

以下是一个结合真实场景的前端面试案例&#xff0c;包含面试流程、核心问题、候选人回答思路及面试官考察点&#xff0c;可直观感受如何在面试中展现实习/项目经历&#xff1a; 案例背景 候选人&#xff1a;应届生&#xff0c;有6个月前端实习经历&#xff0c;参与过“企业内部…

Web前端开发: :where(伪类函数选择器)

:where(伪类函数选择器)&#xff1a;:where() 是 CSS Selectors Level 4 规范中引入的一个强大的伪类函数选择器&#xff0c;它允许开发者以简洁的方式编写复杂的选择器&#xff0c;同时具有独特的优先级特性。核心概念&#xff1a;:where() 伪类函数选择器与 :is() 非常相似&a…

EfficientVMamba: Atrous Selective Scan for Light Weight Visual Mamba论文精读(逐段解析)

EfficientVMamba: Atrous Selective Scan for Light Weight Visual Mamba论文精读&#xff08;逐段解析&#xff09; 论文地址&#xff1a;https://arxiv.org/abs/2403.09977 CVPR 2024 Abstract. Prior efforts in light-weight model development mainly centered on CNN an…

Integer缓冲区

文章目录常见面试题&#xff1a;总结Integer缓冲区是Java预先创建的一个固定范围的Integer对象缓存池&#xff08;默认-128到127&#xff09;&#xff0c;用于自动复用频繁使用的整数值&#xff0c;减少内存开销和对象创建。当通过自动装箱或Integer.valueOf()生成该范围内的整…

[国家电网备考]计算机网络

计算机网络的概述 概念: 用通信设备与线路将地理位置不同,功能独立的计算机系统互连起来,以功能完善的网络软件实现网络中资源共享和信息传递的系统 自治计算机: 能够自我管理,配置,维护的计算机(目前我们使用的电脑) 以前的终端只有显示器,不能叫做自治计算机 计算机网络向用户…

在 Linux(openEuler 24.03 LTS-SP1)上安装 Kubernetes + KubeSphere 的防火墙放行全攻略

目录 在 Linux&#xff08;openEuler 24.03 LTS-SP1&#xff09;上安装 Kubernetes KubeSphere 的防火墙放行全攻略 一、为什么要先搞定防火墙&#xff1f; 二、目标环境 三、需放行的端口和协议列表 四、核心工具说明 1. 修正后的 exec.sh 脚本&#xff08;支持管道/重…

HTTP 响应头信息详解

HTTP 响应头信息详解 引言 HTTP(超文本传输协议)是互联网上应用最为广泛的网络协议之一。在HTTP协议中,响应头信息是服务器向客户端发送的重要信息之一。响应头信息包含了关于响应的元数据,如状态码、内容类型、缓存策略等。本文将详细介绍HTTP响应头信息的概念、类型、作…

去掉长按遥控器power键后提示关机、飞行模式的弹窗

首先找到对应长短按power键的位置&#xff1a;frameworks\base\policy\src\com\android\internal\policy\impl\PhoneWindowManager.javaprivate final Runnable mPowerLongPress new Runnable() {Overridepublic void run() {// The context isnt readif (mLongPressOnPowerBe…

Redis-哨兵机制Sentinel

redis的主从复制模式下,一旦主节点出现了故障无法提供服务了,需要人工进行主从切换,同时大量的客户端需要被通知切换到新的主节点上,对于有了一定规模的应用来说,这种方案的延迟是无法接受的,于是redis2.8提供了Redis-Sentinel(哨兵)来解决这个问题. 目录 1.啥是哨兵节点: 2.r…

SQL 视图

SQL 视图 引言 SQL 视图是数据库管理系统中的一种重要概念,它允许用户以不同的方式查看数据库中的数据。本文将详细介绍 SQL 视图的概念、作用、创建方法以及在实际应用中的注意事项。 一、SQL 视图的概念 SQL 视图是数据库中的一种虚拟表,它并不存储实际的数据,而是基于…