【Linux】庫制作與原理

前言

本篇博客我們來認識下庫方面的知識

💓 個人主頁:zkf

? 文章專欄:Linux

若有問題 評論區見📝

🎉歡迎大家點贊👍收藏?文章

目錄

1.什么是庫

2.靜態庫

2.1靜態庫的生成

2.2靜態庫的使用

3.動態庫

3.1動態庫?成

3.2動態庫的使用

3.3庫運?搜索路徑

4.目標文件

5.ELF文件

6.ELF從形成到加載輪廓

6.1ELF形成可執行文件

6.2ELF可執??件加載

7.理解連接與加載

7.1靜態鏈接

7.2ELF加載與進程地址空間

7.2.1虛擬地址/邏輯地址

7.2.2重新理解進程虛擬地址空間

7.3動態鏈接與動態庫加載

7.3.1進程如何看到動態庫

7.3.2進程間如何共享庫的

7.3.3動態鏈接

7.3.4全局偏移量表GOT(global offset table)

8.總結


1.什么是庫

庫是寫好的現有的,成熟的,可以復?的代碼。現實中每個程序都要依賴很多基礎的底層庫,不可能每個?的代碼都從零開始,因此庫的存在意義?同尋常。
本質上來說庫是?種可執?代碼的?進制形式,可以被操作系統載?內存執?。庫有兩種:
靜態庫 .a[Linux]、.lib[windows]
動態庫 .so[Linux]、.dll[windows]

2.靜態庫

靜態庫(.a):程序在編譯鏈接的時候把庫的代碼鏈接到可執??件中,程序運?的時候將不再
需要靜態庫。
?個可執?程序可能?到許多的庫,這些庫運?有的是靜態庫,有的是動態庫,?我們的編譯默
認為動態鏈接庫,只有在該庫下找不到動態.so的時候才會采?同名靜態庫。我們也可以使? gcc
-static 強轉設置鏈接靜態庫。

2.1靜態庫的生成

// Makefile
libmystdio.a:my_stdio.o my_string.o
@ar -rc $@ $^
@echo "build $^ to $@ ... done"
%.o:%.c
@gcc -c $<
@echo "compling $< to $@ ... done"
.PHONY:clean
clean:
@rm -rf *.a *.o stdc*
@echo "clean ... done"
.PHONY:output
output:
@mkdir -p stdc/include
@mkdir -p stdc/lib
@cp -f *.h stdc/include
@cp -f *.a stdc/lib
@tar -czf stdc.tgz stdc
@echo "output stdc ... done"
ar gnu 歸檔?具, rc 表? (replace and create)
$ ar -tv libmystdio.a
rw-rw-r-- 1000/1000 2848 Oct 29 14:35 2024 my_stdio.o
rw-rw-r-- 1000/1000 1272 Oct 29 14:35 2024 my_string.o
t: 列出靜態庫中的?件
v:verbose 詳細信息

2.2靜態庫的使用

// 任意?錄下,新建
// main.c,引?庫頭?件
#include "my_stdio.h"
#include "my_string.h"
#include <stdio.h>
int main()
{
const char *s = "abcdefg";
printf("%s: %d\n", s, my_strlen(s));
mFILE *fp = mfopen("./log.txt", "a");
if(fp == NULL) return 1;
mfwrite(s, my_strlen(s), fp);
mfwrite(s, my_strlen(s), fp);
mfwrite(s, my_strlen(s), fp);
mfclose(fp);
return 0;
}
// 場景1:頭?件和庫?件安裝到系統路徑下
$ gcc main.c -lmystdio
// 場景2:頭?件和庫?件和我們??的源?件在同?個路徑下
$ gcc main.c -L. -lmymath
// 場景3:頭?件和庫?件有??的獨?路徑
$ gcc main.c -I頭?件路徑 -L庫?件路徑 -lmymath
-L: 指定庫路徑
-I: 指定頭?件搜索路徑
-l: 指定庫名
測試?標?件?成后,靜態庫刪掉,程序照樣可以運?
關于 -static 選項,稍后介紹
庫?件名稱和引?庫的名稱:去掉前綴 lib ,去掉后綴 .so , .a ,如: libc.so -> c

3.動態庫

動態庫(.so):程序在運?的時候才去鏈接動態庫的代碼,多個程序共享使?庫的代碼。
?個與動態庫鏈接的可執??件僅僅包含它?到的函數??地址的?個表,?不是外部函數所在?
標?件的整個機器碼
在可執??件開始運?以前,外部函數的機器碼由操作系統從磁盤上的該動態庫中復制到內存中,
這個過程稱為動態鏈接(dynamic linking)
動態庫可以在多個程序間共享,所以動態鏈接使得可執??件更?,節省了磁盤空間。操作系統采
?虛擬內存機制允許物理內存中的?份動態庫被要?到該庫的所有進程共?,節省了內存和磁盤空
間。

3.1動態庫?成

// Makefile
libmystdio.so:my_stdio.o my_string.o
gcc -o $@ $^ -shared
%.o:%.c
gcc -fPIC -c $<
.PHONY:clean
clean:
@rm -rf *.so *.o stdc*
@echo "clean ... done"
.PHONY:output
output:
@mkdir -p stdc/include
@mkdir -p stdc/lib
@cp -f *.h stdc/include
@cp -f *.so stdc/lib
@tar -czf stdc.tgz stdc
@echo "output stdc ... done"
shared: 表??成共享庫格式
fPIC:產?位置?關碼(position independent code) ?
庫名規則:libxxx.so

3.2動態庫的使用

// 場景1:頭?件和庫?件安裝到系統路徑下
$ gcc main.c -lmystdio
// 場景2:頭?件和庫?件和我們??的源?件在同?個路徑下
$ gcc main.c -L. -lmymath // 從左到右搜索-L指定的?錄
// 場景3:頭?件和庫?件有??的獨?路徑
$ gcc main.c -I頭?件路徑 -L庫?件路徑 -lmymath
$ ldd libmystdio.so // 查看庫或者可執?程序的依賴
linux-vdso.so.1 => (0x00007fffacbbf000)
libc.so.6 => /lib64/libc.so.6 (0x00007f8917335000)
/lib64/ld-linux-x86-64.so.2 (0x00007f8917905000)

3.3庫運?搜索路徑

問題

$ ldd a.out
linux-vdso.so.1 => (0x00007fff4d396000)
libmystdio.so => not found
libc.so.6 => /lib64/libc.so.6 (0x00007fa2aef30000)
/lib64/ld-linux-x86-64.so.2 (0x00007fa2af2fe000)

為什么此刻mystdio動態庫找不到

解決方法

拷? .so ?件到系統共享庫路徑下, ?般指 /usr/lib/usr/local/lib/lib64 或者開
篇指明的庫路徑等
向系統共享庫路徑下建?同名軟連接
更改環境變量: LD_LIBRARY_PATH

4.目標文件

編譯和鏈接這兩個步驟,在Windows下被我們的IDE封裝的很完美,我們?般都是?鍵構建?常?便, 但?旦遇到錯誤的時候呢,尤其是鏈接相關的錯誤,很多?就束??策了。在Linux下,我們之前也了解過如何通過gcc編譯器來完成這?系列操作。

接下來我們深?探討?下編譯和鏈接的整個過程,來更好的理解動靜態庫的使?原理。
先來回顧下什么是編譯呢?編譯的過程其實就是將我們程序的源代碼翻譯成CPU能夠直接運?的機器
代碼。
?如:在?個源?件 hello.c ?便簡單輸出"hello world!",并且調??個run函數,?這個函數被
定義在另?個原?件 code.c 中。這?我們就可以調? gcc -c 來分別編譯這兩個原?件。
在編譯之后會?成兩個擴展名為 .o 的?件,它們被稱作?標?件。要注意的是如果我們
修改了?個原?件,那么只需要單獨編譯它這?個,?不需要浪費時間重新編譯整個?程。?標?件是?個?進制的?件,?件的格式是 ELF ,是對?進制代碼的?種封裝。

5.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):保存已初始化的全局變量和局部靜態變量。

6.ELF從形成到加載輪廓

6.1ELF形成可執行文件

step-1:將多份 C/C++ 源代碼,翻譯成為?標 .o ?件 + 動靜態庫(ELF)
step-2:將多份 .o ?件section進?合并

📌 注意:
實際合并是在鏈接時進?的,但是并不是這么簡單的合并,也會涉及對庫合并,此處不做
過多追究

6.2ELF可執??件加載

?個ELF會有多種不同的Section,在加載到內存的時候,也會進?Section合并,形成segment
合并原則:相同屬性,?如:可讀,可寫,可執?,需要加載時申請空間等.
這樣,即便是不同的Section,在加載到內存中,可能會以segment的形式,加載到?起
很顯然,這個合并?作也已經在形成 ELF 的時候,合并?式已經確定了,具體合并原則被記錄在
ELF 的 程序頭表(Program header table)
📌 為什么要將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 ?件可以看到該節。
從 執?視圖 來看:
告訴操作系統哪些模塊可以被加載進內存。
加載進內存之后哪些分段是可讀可寫,哪些分段是只讀,哪些分段是可執?的
對于 ELF HEADER 這部分來說,我們只?知道其作?即可,它的主要?的是定位?件的其他部分。

7.理解連接與加載

7.1靜態鏈接

?論是??的 .o , 還是靜態庫中的 .o ,本質都是把.o?件進?連接的過程
所以:研究靜態鏈接,本質就是研究 .o 是如何鏈接的
靜態鏈接就是把庫中的.o進?合并,和上述過程?樣
所以鏈接其實就是將編譯之后的所有?標?件連同?到的?些靜態庫運?時庫組合,拼裝成?個獨?的可執??件。其中就包括我們之前提到的地址修正,當所有模塊組合在?起之后,鏈接器會根據我們的.o?件或者靜態庫中的重定位表找到那些需要被重定位的函數全局變量,從?修正它們的地址。這其實就是靜態鏈接的過程。

所以,鏈接過程中會涉及到對.o中外部符號進?地址重定位。

7.2ELF加載與進程地址空間

7.2.1虛擬地址/邏輯地址

?個ELF程序,在沒有被加載到內存的時候,有沒有地址呢?
進程mm_struct、vm_area_struct在進程剛剛創建的時候,初始化數據從哪?來的?
答案:
?個ELF程序,在沒有被加載到內存的時候,本來就有地址,當代計算機?作的時候,都采?"平坦
模式"進??作。所以也要求ELF對??的代碼和數據進?統?編址,下?是 objdump -S 反匯編
之后的代碼
最左側的就是ELF的虛擬地址,其實,嚴格意義上應該叫做邏輯地址(起始地址+偏移量), 但是我們
認為起始地址是0.也就是說,其實虛擬地址在我們的程序還沒有加載到內存的時候,就已經把可執
?程序進?統?編址了.
進程mm_struct、vm_area_struct在進程剛剛創建的時候,初始化數據從哪?來的?從ELF各個
segment來,每個segment有??的起始地址和??的?度,?來初始化內核結構中的[start, end]
等范圍數據,另外在?詳細地址,填充?表.
所以:虛擬地址機制,不光光OS要?持,編譯器也要?持.

7.2.2重新理解進程虛擬地址空間

ELF 在被編譯好之后,會把??未來程序的??地址記錄在ELF header的Entry字段中:

7.3動態鏈接與動態庫加載

7.3.1進程如何看到動態庫

7.3.2進程間如何共享庫的

7.3.3動態鏈接

動態鏈接其實遠?靜態鏈接要常?得多。?如我們查看下 hello 這個可執?程序依賴的動態庫,會發現它就?到了?個c動態鏈接庫:
ldd main.exe
linux-vdso.so.1 => (0x00007ffefd43f000)
libc.so.6 => /lib64/libc.so.6 (0x00007f533380b000)
/lib64/ld-linux-x86-64.so.2 (0x00007f5333bd9000)
這?的 libc.so 是C語?的運?時庫,??提供了常?的標準輸?輸出?件字符串處理等等這些功
能。
那為什么編譯器默認不使?靜態鏈接呢?靜態鏈接會將編譯產?的所有?標?件,連同?到的各種
庫,合并形成?個獨?的可執??件,它不需要額外的依賴就可以運?。照理來說應該更加?便才對是吧?
靜態鏈接最?的問題在于?成的?件體積?,并且相當耗費內存資源。隨著軟件復雜度的提升,我們的操作系統也越來越臃腫,不同的軟件就有可能都包含了相同的功能和代碼,顯然會浪費?量的硬盤空間。
這個時候,動態鏈接的優勢就體現出來了,我們可以將需要共享的代碼單獨提取出來,保存成?個獨?的動態鏈接庫,等到程序運?的時候再將它們加載到內存,這樣不但可以節省空間,因為同?個模塊在內存中只需要保留?份副本,可以被不同的進程所共享。
動態鏈接到底是如何?作的??
?先要交代?個結論,動態鏈接實際上將鏈接的整個過程推遲到了程序加載的時候。?如我們去運??個程序,操作系統會?先將程序的數據代碼連同它?到的?系列動態庫先加載到內存,其中每個動態庫的加載地址都是不固定的,操作系統會根據當前地址空間的使?情況為它們動態分配?段內存。 當動態庫被加載到內存以后,?旦它的內存地址被確定,我們就可以去修正動態庫中的那些函數跳轉地址了。
我們的可執?程序被編譯器動了?腳
在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 函數來終?程序。
上述過程描述了C/C++程序在 main 函數之前執?的?系列操作,但這些操作對于?多數程序員來說是透明的。程序員通常只需要關注 main 函數中的代碼,?不需要關?底層的初始化過程。然?,了解這些底層細節有助于更好地理解程序的執?流程和調試問題
動態庫為了隨時進?加載,為了?持并映射到任意進程的任意位置,對動態庫中的?法,統?編址, 采?相對編址的?案進?編制的(其實可執?程序也?樣,都要遵守平坦模式,只不過exe是直接加載的)。
動態庫也是?個?件,要訪問也是要被先加載,要加載也是要被打開的
讓我們的進程找到動態庫的本質:也是?件操作,不過我們訪問庫函數,通過虛擬地址進
?跳轉訪問的,所以需要把動態庫映射到進程的地址空間中

庫已經被我們映射到了當前進程的地址空間中
庫的虛擬起始地址我們也已經知道了
庫中每?個?法的偏移量地址我們也知道
所有:訪問庫中任意?法,只需要知道庫的起始虛擬地址+?法偏移量即可定位庫中的?
?且:整個調?過程,是從代碼區跳轉到共享區,調?完畢在返回到代碼區,整個過程完
全在進程地址空間中進?的.

7.3.4全局偏移量表GOT(global offset table)

?注意:
也就是說,我們的程序運?之前,先把所有庫加載并映射,所有庫的起始虛擬地址都應該
提前知道
然后對我們加載到內存中的程序的庫函數調?進?地址修改,在內存中?次完成地址設置
(這個叫做加載地址重定位)
等等,修改的是代碼區?不是說代碼區在進程中是只讀的嗎?怎么修改?能修改嗎?
所以:動態鏈接采?的做法是在 .data (可執?程序或者庫??)中專?預留??區域?來存放函數的跳轉地址,它也被叫做全局偏移表GOT,表中每?項都是本運?模塊要引?的?個全局變量或函數的地址。
因為.data區域是可讀寫的,所以可以?持動態進?修改
1. 由于代碼段只讀,我們不能直接修改代碼段。但有了GOT表,代碼便可以被所有進程共享。但在不同進程的地址空間中,各動態庫的絕對地址、相對位置都不同。反映到GOT表上,就是每個進程的每個動態庫都有獨?的GOT表,所以進程間不能共享GOT表。
2. 在單個.so下,由于GOT表與 .text 的相對位置是固定的,我們完全可以利?CPU的相對尋址來找到GOT表。
3. 在調?函數的時候會?先查表,然后根據表中的地址來進?跳轉,這些地址在動態庫加載的時候會被修改為真正的地址。
4. 這種?式實現的動態鏈接就被叫做 PIC 地址?關代碼 。換句話說,我們的動態庫不需要做任何修改,被加載到任意內存地址都能夠正常運?,并且能夠被所有進程共享,這也是為什么之前我們給編譯器指定-fPIC參數的原因,PIC=相對編址+GOT。

8.總結

靜態鏈接的出現,提?了程序的模塊化?平。對于?個?的項?,不同的?可以獨?地測試和開發
??的模塊。通過靜態鏈接,?成最終的可執??件。
我們知道靜態鏈接會將編譯產?的所有?標?件,和?到的各種庫合并成?個獨?的可執??件,
其中我們會去修正模塊間函數的跳轉地址,也被叫做編譯重定位(也叫做靜態重定位)。
?動態鏈接實際上將鏈接的整個過程推遲到了程序加載的時候。?如我們去運??個程序,操作系
統會?先將程序的數據代碼連同它?到的?系列動態庫先加載到內存,其中每個動態庫的加載地址
都是不固定的,但是?論加載到什么地?,都要映射到進程對應的地址空間,然后通過.GOT?式進?調?(運?重定位,也叫做動態地址重定位)。

結束語

動靜態庫相關知識總結完畢

感謝觀看!!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/93325.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/93325.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/93325.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Android ADB 常用指令全解析

ADB&#xff08;Android Debug Bridge&#xff09;是 Android 開發和測試不可或缺的調試工具&#xff0c;它建立了電腦與 Android 設備之間的通信橋梁&#xff0c;通過命令行指令可實現對設備的全方位控制。掌握 ADB 指令能大幅提升開發效率&#xff0c;解決各類調試難題。本文…

使用 Rust 創建 32 位 DLL 的完整指南

使用 Rust 創建 32 位 DLL 的完整指南 在 Rust 中創建 32 位 DLL 需要特定的工具鏈配置和編譯選項。以下是詳細步驟和最佳實踐&#xff1a; 環境準備 1. 安裝 Rust 工具鏈 # 安裝 Rust curl --proto https --tlsv1.2 -sSf https://sh.rustup.rs | sh# 安裝 32 位目標 rustu…

算法基礎 第3章 數據結構

1.單調棧 1.什么是單調棧 單調棧&#xff0c;即具有單調性的棧。 實現 #include <iostream> #include <stack> using namespace std; const int N 3e6 10; int a[N], n; void test1() {stack<int> st; // 維護?個單調遞增的棧for(int i 1; i < n; i…

[機器學習]08-基于邏輯回歸模型的鳶尾花數據集分類

使用sklearn的LogisticRegression多分類模型程序代碼&#xff1a;import numpy as np from sklearn.linear_model import LogisticRegression import matplotlib.pyplot as plt import matplotlib as mpl from sklearn import datasets from sklearn import preprocessing impo…

【STM32入門教程】stm32簡介

一、STM32簡介二、ARM三、stm32f103c8t6四、命名規則五、系統結構六、引腳定義七、啟動配置一般情況下&#xff0c;都是在flash開始程序&#xff0c;而啟動程序也可以進行配置在其他地方啟動程序&#xff0c;通過配置boot0和boot1來進行配置八、最小系統電路

SAE J2716多協議網關的硬件架構與實時協議轉換機制解析

本文解析符合SAE J2716標準的工業級協議轉換設備技術架構&#xff0c;通過拆解其四路雙向SENT通道與多總線&#xff08;CANFD/Ethernet/USB&#xff09;的實時交互機制、MicroSD獨立日志系統設計及模擬量動態映射方案&#xff0c;為汽車電子與工業通信開發者提供可復用的技術參…

VS2022+QT5.15.2+OCCT7.9.1的開發環境搭建流程

以下是VS2022 QT5.15.2 OCCT7.9.1開發環境搭建的完整流程&#xff1a; 一、安裝Visual Studio 2022 下載安裝程序 訪問VS官網下載Community版安裝組件 選擇"使用C的桌面開發"工作負載勾選&#xff1a; MSVC v143 - VS 2022 C x64/x86生成工具Windows 10 SDK (建議…

數據庫訪問模式詳解

數據庫訪問模式詳解數據庫訪問模式是軟件架構中數據訪問層&#xff08;Data Access Layer&#xff09;設計的核心&#xff0c;它定義了應用程序如何與數據庫進行交互的策略和方法。選擇合適的訪問模式對于系統的性能、可維護性、可擴展性、事務一致性和開發效率至關重要。不同的…

BGE向量算法

一、是什么 什么是BGE向量算法&#xff1f;先說說網上的概念吧。本文不講解太深的算法知識&#xff0c;主要講解如何用&#xff01; BGE&#xff08;BAAI General Embedding&#xff09;是北京智源研究院開源的“通用語義向量模型”。一句話&#xff1a;把中文或英文句子變成…

AI數據倉庫的核心優勢解析

內容概要本文旨在全面解析AI數據倉庫的核心優勢&#xff0c;為讀者提供清晰的框架。文章首先從基礎定義出發&#xff0c;探討其如何高效整合多源數據&#xff0c;并支持人工智能與機器學習應用。隨后&#xff0c;將詳細闡述處理TB級數據的能力&#xff0c;包括兼容結構化和非結…

具身智能Scaling Law缺失:機器人界的“摩爾定律“何時誕生?

8月9日&#xff0c;在世界機器人大會的演講臺上&#xff0c;宇樹科技創始人王興興談論到目前機器人運動控制領域存在的RL Scaling Law問題&#xff0c;他認為現在的機器人在學習一項新的技能時&#xff0c;往往都是需要從頭開始研究以及教學。而在未來更加希望的是能夠在原有的…

【跨越 6G 安全、防御與智能協作:從APT檢測到多模態通信再到AI代理語言革命】

跨越 6G 安全、防御與智能協作&#xff1a;從APT檢測到多模態通信再到AI代理語言革命引言單篇總結**2. Integrated Multimodal Sensing and Communication: Challenges, Technologies, and Architectures****3. Why do AI agents communicate in human language?**引言 在邁向…

微前端-解決MicroApp微前端內存泄露問題

前言 之前使用京東微前端框架MicroApp集成10個微前端的頁面到AngularJs的后臺管理系統中&#xff0c;每個微前端做成一個菜單&#xff0c;一共10個&#xff0c;每次打開都是一個新的微前端&#xff0c;但是發現打開的微前端越多&#xff0c;容易造成內存泄露&#xff0c;下面講…

線性代數 · 向量運算 | 叉乘 / 幾何意義 / 推導

注&#xff1a;本文為 “線性代數 向量運算” 相關合輯。 圖片清晰度受引文原圖所限。 略作重排&#xff0c;未整理去重。 如有內容異常&#xff0c;請看原文。 數學基礎 —— 向量運算&#xff08;叉乘&#xff09; keng_s 于 2016-08-05 17:17:57 發布 1_ 向量的叉乘 向量…

方法中只包含查詢操作需要添加事務嗎?

方法中只包含查詢操作需要添加事務嗎?絕大部分情況都不需要 是否需要為包含數據庫查詢操作的方法添加 @Transactional 注解,取決于業務需求和查詢操作的特性,不能一概而論。以下是具體分析: 一、不需要添加 @Transactional 的常見場景 如果查詢操作滿足以下條件,通常不需…

MTK平臺Wi-Fi學習--wifi channel 通過國家碼進行功率限制和wifi eFEM 基本配置和wifi Tx SEM問題

一. 國家碼可以用來限制功率上限,可以針對各國家實現By channel降功率的能力 可以通過country code來設置不同channel的power limit,操作方法如下: 在rlm_txpwr_init.h文件中g_rRlmPowerLimitConfiguration[]下添加需要限制功率的channel, 例如:國家碼CN,信道:CH1,po…

MedGemma: 多模態醫學文本與圖像處理的創新模型

MedGemma: 多模態醫學文本與圖像處理的創新模型 今天&#xff0c;我有幸參加了在上海舉行的Google 2025 I/O大會&#xff0c;這是一場充滿創新與突破的技術盛宴。作為全球最具影響力的科技大會之一&#xff0c;Google I/O每年都會吸引來自世界各地的開發者、企業領袖以及科技愛…

深入剖析 C++ STL 中的 std::list 容器

基本介紹在 C 標準庫&#xff08;STL&#xff09;中&#xff0c;std::list 是一個基于雙向鏈表實現的序列容器。它與 std::vector、std::deque 等連續存儲容器不同&#xff0c;提供了在序列中高效插入和刪除元素的能力&#xff0c;尤其是在序列中間位置操作時優勢明顯。1. std:…

大規模調用淘寶商品詳情 API 的分布式請求調度實踐

在電商數據分析、比價系統、選品工具等業務場景中&#xff0c;往往需要大規模調用淘寶商品詳情 API 以獲取商品標題、價格、銷量、評價等核心數據。然而&#xff0c;面對淘寶開放平臺的嚴格限流策略、海量商品 ID 的處理需求以及系統高可用要求&#xff0c;傳統的單節點調用方式…

在 Windows 系統中解決 Git 推送時出現的 Permission denied (publickey) 錯誤,請按照以下詳細步驟操作:

完整解決方案步驟&#xff1a; 1. 檢查并生成 SSH 密鑰 # 打開 Git Bash ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" 全程按回車&#xff08;使用默認路徑&#xff0c;不設密碼&#xff09; 密鑰將生成在&#xff1a;C:\Users\<用戶名>\.ssh\ 目…