【Linux庖丁解牛】— 庫的理解與加載!

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μm3高維邏輯門

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 視圖是數據庫中的一種虛擬表,它并不存儲實際的數據,而是基于…