Linux .eh_frame section以及libunwind

文章目錄

  • 前言
  • 一、LSB
  • 二、The .eh_frame section
    • 2.1 簡介
    • 2.2 The Common Information Entry Format
      • 2.1.1 Augmentation String Format
    • 2.3 The Frame Description Entry Format
  • 三、The .eh_frame_hdr section
  • 四、libunwind
  • 五、基于Frame Pointer和基于unwind 形式的棧回溯比較
  • 參考資料

前言

基于FP的棧回溯請參考:
Linux x86_64 基于FP棧回溯
Linux ARM64 基于FP棧回溯

基于FP棧回溯需要一個專門寄存器RBP來保存frame poniter。
gcc優化選項 -O 默認使用-fomit-frame-pointer編譯標志進行優化,省略幀指針。將寄存器RBP作為一個通用的寄存器來使用。

-fomit-frame-pointer 是GCC編譯器的一個編譯選項。當啟用該選項時,它告訴編譯器在不需要基指針的函數中省略基指針。通過省略基指針,編譯器避免了保存、設置和恢復基指針的指令,從而使生成的代碼更小、更快。

省略基指針還提供了一個額外的通用寄存器可供使用,這對于具有有限通用寄存器數量的架構(如x86)非常有用。

這樣就不能基于FP來進行棧回溯了,Linux通過.eh_frame節可以來進行棧回溯,.eh_frame節通常由編譯器(如GCC)在編譯可執行文件或共享庫時生成。調試器(如GDB)能夠讀取并解析這些節,以提供強大的調試功能。

在x86_64體系架構上,大多數軟件在編譯的時采用了gcc的默認選項,而gcc的默認選項不啟用函數幀指針FP,而是把RBP寄存器作為一個通用的寄存器,以及無法進行FP進行棧回溯,因此對于用戶空間程序,通常使用.eh_frame section 來進行棧回溯。

.eh_frame段中存儲著跟函數入棧相關的關鍵數據。
當函數執行入棧指令后,在該段會保存跟入棧指令一一對應的編碼數據,
根據這些編碼數據,就能計算出當前函數棧大小和cpu的哪些寄存器入棧了,在棧中什么位置。

無論是否有-g選項,gcc默認都會生成.eh_frame和.eh_frame_hdr section。

一、LSB

Linux Standard Base(LSB)定義了編譯應用程序的系統接口和支持安裝腳本的最小環境。其目的是為符合LSB的大規模應用程序提供統一的行業標準環境。

LSB規范由兩個基本部分組成:一個通用部分,描述了在LSB的所有實現中保持不變的接口部分;以及一個特定于體系結構的部分,描述了根據處理器體系結構而變化的接口部分。通用部分和特定于體系結構的部分共同為具有相同硬件體系結構的系統上的編譯應用程序提供了完整的接口規范。

LSB包含一組應用程序接口(API)和應用程序二進制接口(ABI)。API可以出現在可移植應用程序的源代碼中,而該應用程序的編譯二進制文件可以使用更大的一組ABIs。符合規范的實現提供了這里列出的所有ABIs。編譯系統可以通過替換(例如通過宏定義)某些API,將其調用轉換為一個或多個底層二進制接口的調用,并根據需要插入對二進制接口的調用。

LSB是由Linux Foundation組織架構下的多個Linux發行版共同參與的項目,旨在標準化軟件系統結構,包括文件系統層次結構(Filesystem Hierarchy Standard)。LSB基于POSIX規范、Single UNIX Specification(SUS)和其他幾個開放標準,但在某些領域進行了擴展。

根據LSB:
LSB的目標是開發和推廣一組開放標準,增加Linux發行版之間的兼容性,并使軟件應用程序能夠在任何符合標準的系統上運行,即使是以二進制形式。此外,LSB還將協調努力,吸引軟件供應商為Linux操作系統移植和編寫產品。

二、The .eh_frame section

2.1 簡介

在Linux系統中,.eh_frame節是一種特殊的節(section),用于存儲程序的調試信息和堆棧回溯相關的信息。
這個節通常在可執行文件或共享庫中存在,以支持運行時的調試和異常處理。

當程序在Linux系統中進行異常處理和堆棧展開時,會使用到.eh_frame節。.eh_frame節是基于DWARF(Debugging With Attributed Record Formats)調試格式的一部分。

.eh_frame節的主要作用是提供運行時支持,用于正確展開函數調用堆棧。它存儲了一系列編碼的調用幀信息,這些信息在異常處理或進行堆棧回溯時起到關鍵作用。

在異常發生或需要進行堆棧回溯時,運行時系統會利用.eh_frame節中的信息來展開堆棧。它會遵循編碼的CFI(Call Frame Information)指令序列,逐層遍歷堆棧幀,獲取返回地址,并找到對應的異常處理程序或回溯信息。

# readelf -S a.out
共有 30 個節頭,從偏移量 0x1930 開始:節頭:[] 名稱              類型             地址              偏移量大小              全體大小          旗標   鏈接   信息   對齊......[16] .eh_frame_hdr     PROGBITS         00000000004005c0  000005c0000000000000003c  0000000000000000   A       0     0     4[17] .eh_frame         PROGBITS         0000000000400600  000006000000000000000114  0000000000000000   A       0     0     8......
Key to Flags:W (write), A (alloc), X (execute), M (merge), S (strings), I (info),L (link order), O (extra OS processing required), G (group), T (TLS),C (compressed), x (unknown), o (OS specific), E (exclude),l (large), p (processor specific)
A (alloc)

.eh_frame帶有SHF_ALLOC flag(標志一個section是否應為內存中鏡像的一部分)。

.eh_frame節包含了用于棧回溯和異常處理的數據結構,其中包括編碼了調用幀信息、異常處理表和其他相關數據的指令序列。這些信息用于在程序運行時進行堆棧展開(stack unwinding),即在異常發生時回溯函數調用堆棧以查找異常處理程序。這些結構可以在程序運行時被調試器或其他工具使用。它們提供了關于函數調用鏈、寄存器狀態和局部變量等信息的詳細描述,以便進行調試和錯誤診斷。

當程序中包含異常處理機制(如C++異常)或使用與堆棧相關的特性(如backtrace函數)時,編譯器會生成和使用.eh_frame節。這些信息允許運行時系統在異常處理期間正確地展開函數調用堆棧,并將控制權傳遞給適當的異常處理程序。

盡管.eh_frame增加了可執行文件的大小,但它提供了重要的運行時支持和調試功能。然而,對于某些嵌入式系統或特定的應用程序,可能需要最小化可執行文件的大小,并且不需要異常處理和調試功能。在這種情況下,可以使用編譯器選項(如-fno-asynchronous-unwind-tables)來禁用.eh_frame的生成,以減少可執行文件的大小。

.eh_frame中的數據結構通常使用一種稱為DWARF(Debugging With Arbitrary Record Formats)的格式進行編碼。
DWARF是一種調試信息格式,廣泛用于Linux系統和其他類Unix系統中。它定義了一組規范,用于描述程序的調試信息,包括函數、類型、變量、源代碼映射等。

通過解析.eh_frame節中的DWARF數據,調試器可以還原函數調用堆棧,獲取函數的參數和局部變量值,以及跟蹤函數調用的路徑。這對于調試復雜的程序、分析錯誤和優化代碼非常有幫助。

.eh_frame節應包含一個或多個調用幀信息(CFI - Call Frame Information)記錄。存在的記錄數量應由節頭中包含的節大小確定。每個CFI記錄包含一個通用信息條目(CIE - Common Information Entry)記錄,后面跟著一個或多個幀描述條目(FDE - Frame Description Entry)記錄。CIE和FDE都應對齊到地址單元大小的邊界。

Call Frame Information Format:

----------------------------------
Common Information Entry Record
----------------------------------
Frame Description Entry Record(s)
----------------------------------

如下圖所示:
在這里插入圖片描述

2.2 The Common Information Entry Format

Common Information Entry Format:

LengthRequired
Extended LengthOptional
CIE IDRequired
VersionRequired
Augmentation StringRequired
Code Alignment FactorRequired
Data Alignment FactorRequired
Return Address RegisterRequired
Augmentation Data LengthOptional
Augmentation DataOptional
Initial InstructionsRequired
Padding
// libunwind/include/dwarf.htypedef struct dwarf_cie_info{unw_word_t cie_instr_start; /* start addr. of CIE "initial_instructions" */unw_word_t cie_instr_end;   /* end addr. of CIE "initial_instructions" */unw_word_t fde_instr_start; /* start addr. of FDE "instructions" */unw_word_t fde_instr_end;   /* end addr. of FDE "instructions" */unw_word_t code_align;      /* code-alignment factor */unw_word_t data_align;      /* data-alignment factor */unw_word_t ret_addr_column; /* column of return-address register */unw_word_t handler;         /* address of personality-routine */uint16_t abi;uint16_t tag;uint8_t fde_encoding;uint8_t lsda_encoding;unsigned int sized_augmentation : 1;unsigned int have_abi_marker : 1;unsigned int signal_frame : 1;}
dwarf_cie_info_t;

(1)Length
一個4字節的無符號值,表示CIE結構的長度(以字節為單位),不包括Length字段本身。如果Length字段的值為0xffffffff,則長度包含在Extended Length字段中。如果Length字段的值為0,則此CIE應被視為終止符,并且處理將結束。

(2)Extended Length
這個8字節的無符號值表示CIE結構的字節長度,不包括長度字段和擴展長度字段本身。除非長度字段包含值0xffffffff,否則該字段不存在。

(3)CIE ID
這個4字節的無符號值用于區分CIE(Common Information Entry)記錄和FDE(Frame Description Entry)記錄。該值應始終為0,表示該記錄是一個CIE。

static inline int
is_cie_id (unw_word_t val, int is_debug_frame)
{/* The CIE ID is normally 0xffffffff (for 32-bit ELF) or0xffffffffffffffff (for 64-bit ELF).  However, .eh_frameuses 0.  */if (is_debug_frame)return (val == (uint32_t)(-1) || val == (uint64_t)(-1));elsereturn (val == 0);
}

(4)Version
這個1字節的值用于標識幀信息結構的版本號。該值應為1。

  /* Read the return-address column either as a u8 or as a uleb128.  */if (version == 1){if ((ret = dwarf_readu8 (as, a, &addr, &ch, arg)) < 0)return ret;dci->ret_addr_column = ch;}

(5)Augmentation String
這個值是一個以NUL(空字符)結尾的字符串,用于標識與該CIE或與該CIE關聯的FDE的增強信息。如果字符串長度為零,則表示沒有增強數據存在。增強字符串是區分大小寫的,并且應按照下面的描述進行解釋。

  /* read and parse the augmentation string: */memset (augstr, 0, sizeof (augstr));for (i = 0;;){if ((ret = dwarf_readu8 (as, a, &addr, &ch, arg)) < 0)return ret;if (!ch)break;  /* end of augmentation string */if (i < sizeof (augstr) - 1)augstr[i++] = ch;}

(6)Code Alignment Factor
這個值是一個無符號的LEB128編碼值,它被從與該CIE或其FDE關聯的所有"advance location"指令中分解出來。該值應與"advance location"指令的增量參數相乘,以獲得新的位置值。

(7)Data Alignment Factor
這個值是一個帶符號的LEB128編碼值,它被從與該CIE或其FDE關聯的所有偏移指令中分解出來。該值應與偏移指令的寄存器偏移參數相乘,以獲得新的偏移值。

  if ((ret = dwarf_read_uleb128 (as, a, &addr, &dci->code_align, arg)) < 0|| (ret = dwarf_read_sleb128 (as, a, &addr, &dci->data_align, arg)) < 0)return ret;

(8)Augmentation Length
這個值是一個無符號的LEB128編碼值,用于表示增強數據的字節長度。只有當增強字符串中包含字符’z’時,該字段才存在。

(9)Augmentation Data
這是一個數據塊,其內容由增強字符串中的內容定義,具體描述如下。只有當增強字符串中包含字符’z’時,該字段才存在。該數據塊的大小由增強長度(Augmentation Length)指定。

(9)Initial Instructions
這是初始的調用幀指令集。指令的數量由CIE記錄中剩余的空間確定。

(10)Padding
這些額外的字節用于將CIE結構對齊到地址單元大小邊界。

2.1.1 Augmentation String Format

增強字符串指示了一些可選字段的存在以及如何解釋這些字段。該字符串區分大小寫。CIE中增強字符串中的每個字符的解釋如下:

‘z’:
字符串的第一個字符可以是’z’。如果存在,則增強數據字段也必須存在。增強數據的內容將根據增強字符串中的其他字符進行解釋。

‘L’:
字符串的第一個字符是’z’時,可以在任何位置上出現’L’。如果存在,它表示CIE的增強數據中存在一個參數,并且FDE的增強數據中也存在相應的參數。CIE的增強數據中的參數是1字節,表示用于FDE的增強數據中的參數的指針編碼,該參數是指向特定語言數據區(LSDA)的地址。LSDA指針的大小由使用的指針編碼指定。

‘P’:
字符串的第一個字符是’z’時,可以在任何位置上出現’P’。如果存在,它表示CIE的增強數據中存在兩個參數。第一個參數是1字節,表示用于第二個參數的指針編碼,該參數是指向人格例程處理程序的地址。人格例程用于處理特定語言和供應商的任務。系統解旋庫接口通過指向人格例程的指針訪問特定語言的異常處理語義。個性例程沒有ABI-specific的名稱。個性例程指針的大小由使用的指針編碼指定。

‘R’:
字符串的第一個字符是’z’時,可以在任何位置上出現’R’。如果存在,則增強數據中應包含一個1字節的參數,該參數表示FDE中使用的地址指針的指針編碼。

  i = 0;if (augstr[0] == 'z'){dci->sized_augmentation = 1;if ((ret = dwarf_read_uleb128 (as, a, &addr, &aug_size, arg)) < 0)return ret;i++;}for (; i < sizeof (augstr) && augstr[i]; ++i)switch (augstr[i]){case 'L':/* read the LSDA pointer-encoding format.  */if ((ret = dwarf_readu8 (as, a, &addr, &ch, arg)) < 0)return ret;dci->lsda_encoding = ch;break;case 'R':/* read the FDE pointer-encoding format.  */if ((ret = dwarf_readu8 (as, a, &addr, &fde_encoding, arg)) < 0)return ret;break;case 'P':/* read the personality-routine pointer-encoding format.  */if ((ret = dwarf_readu8 (as, a, &addr, &handler_encoding, arg)) < 0)return ret;if ((ret = dwarf_read_encoded_pointer (as, a, &addr, handler_encoding,pi, &dci->handler, arg)) < 0)return ret;break;case 'S':/* This is a signal frame. */dci->signal_frame = 1;/* Temporarily set it to one so dwarf_parse_fde() knows thatit should fetch the actual ABI/TAG pair from the FDE.  */dci->have_abi_marker = 1;break;default:Debug (1, "Unexpected augmentation string `%s'\n", augstr);if (dci->sized_augmentation)/* If we have the size of the augmentation body, we can skipover the parts that we don't understand, so we're OK. */goto done;elsereturn -UNW_EINVAL;}

2.3 The Frame Description Entry Format

Frame Description Entry Format:

LengthRequired
Extended LengthOptional
CIE PointerRequired
PC BeginRequired
PC RangeRequired
Augmentation Data LengthOptional
Augmentation DataOptional
Call Frame InstructionsRequired
Padding

(1)Length
一個4字節的無符號值,表示FDE(Frame Description Entry)結構的長度(以字節為單位),不包括Length字段本身。如果Length字段的值為0xffffffff,則長度包含在Extended Length字段中。如果Length字段的值為0,則該FDE應被視為終止器,并且處理過程應該結束。

(2)Extended Length
一個8字節的無符號值,表示FDE(Frame Description Entry)結構的長度(以字節為單位),不包括Length字段或Extended Length字段本身。除非Length字段的值為0xffffffff,否則該字段不會出現。

(3)CIE Pointer
一個4字節的無符號值,從當前FDE中的CIE指針的偏移量中減去,得到關聯CIE的起始偏移量。該值永遠不應為0。

(4)PC Begin
一個編碼值,表示與該FDE關聯的初始位置的地址。編碼格式在增強數據(Augmentation Data)中指定。

(5)PC Range
一個絕對值,表示與該FDE關聯的指令字節數。

(6)Augmentation Length
一個無符號 LEB128 編碼值,表示增強數據的字節長度。只有在關聯的CIE中的增強字符串包含字符 ‘z’ 時,該字段才存在。

(7)Augmentation Data
一個數據塊,其內容由關聯CIE中的增強字符串的內容所定義,如上所述。只有當關聯CIE中的增強字符串包含字符 ‘z’ 時,該字段才存在。該數據塊的大小由增強長度(Augmentation Length)給出。

(8)Call Frame Instructions
一組調用幀指令(Call Frame Instructions)。

(9)Padding
用于將FDE(Frame Description Entry)結構對齊到一個地址單元大小邊界的額外字節。

三、The .eh_frame_hdr section

.eh_frame_hdr 段包含有關 .eh_frame 段的額外信息。該段中包含了指向 .eh_frame 數據起始位置的指針,以及可選的指向 .eh_frame 記錄的二進制搜索表。

定位一個pc所在的FDE需要從頭掃描.eh_frame,找到合適的FDE(pc是否落在initial_location和address_range表示的區間),所花時間和掃描的CIE和FDE記錄數相關。 .eh_frame_hdr包含binary search index table描述(initial_location, FDE address) pairs。

.eh_frame_hdr Section Format:

EncodingField
unsigned byteversion
unsigned byteeh_frame_ptr_enc
unsigned bytefde_count_enc
unsigned bytetable_enc
encodedeh_frame_ptr
encodedfde_count
binary search table

(1)version
.eh_frame_hdr 格式的版本。該值應為 1。

(2)eh_frame_ptr_enc
eh_frame_ptr字段的編碼格式。

(3)fde_count_enc
fde_count 字段的編碼格式。DW_EH_PE_omit 的值表示二進制搜索表不存在。

(4)table_enc
二進制搜索表中條目的編碼格式。DW_EH_PE_omit 的值表示二進制搜索表不存在。

(5)eh_frame_ptr
指向.eh_frame部分開頭的指針的編碼值。

(6)fde_count
二進制搜索表中條目數的編碼值。

(7)binary search table
一個包含 fde_count 個條目的二進制搜索表。每個表條目包含兩個編碼值,即初始位置和地址。這些條目按照初始位置的值按升序排序。

四、libunwind

libunwind 是一個可移植且高效的 C API,用于確定 ELF 程序線程的當前調用鏈,并可以在該調用鏈的任何點上恢復執行。該 API 支持本地(同一進程)和遠程(其他進程)操作。用于顯示引發問題的調用鏈的回溯信息,或用于性能監控和分析。

libunwind的使用比較簡單:

#define UNW_LOCAL_ONLY#include <libunwind.h>
#include <stdio.h>
#include <stdlib.h>#define panic(...)				\{ fprintf (stderr, __VA_ARGS__); exit (-1); }static void do_backtrace (void)
{unw_cursor_t cursor;unw_word_t ip, sp;unw_context_t uc;int ret;unw_getcontext (&uc);if (unw_init_local (&cursor, &uc) < 0)panic ("unw_init_local failed!\n");do{unw_get_reg (&cursor, UNW_REG_IP, &ip);unw_get_reg (&cursor, UNW_REG_SP, &sp);char fname[64];unw_word_t  offset;unw_get_proc_name(&cursor, fname, sizeof(fname), &offset);printf ("(ip=%016lx) (sp=%016lx): (%s+0x%x) [%p]\n", (long) ip, (long) sp, fname, offset, (long) sp);ret = unw_step (&cursor);if (ret < 0){unw_get_reg (&cursor, UNW_REG_IP, &ip);panic ("FAILURE: unw_step() returned %d for ip=%lx\n",ret, (long) ip);}}while (ret > 0);
}void func_c(void)
{do_backtrace();	
}void func_b(void)
{func_c();	
}void func_a(void)
{func_b();
}int main (int argc, char **argv)
{func_a ();return 0;
}
# gcc 1.c -lunwind
# ./a.out
(ip=0000000000400897) (sp=00007fffc131ce40): (do_backtrace+0x1a) [0x7fffc131ce40]
(ip=00000000004009f0) (sp=00007fffc131d660): (func_c+0x9) [0x7fffc131d660]
(ip=00000000004009fb) (sp=00007fffc131d670): (func_b+0x9) [0x7fffc131d670]
(ip=0000000000400a06) (sp=00007fffc131d680): (func_a+0x9) [0x7fffc131d680]
(ip=0000000000400a1c) (sp=00007fffc131d690): (main+0x14) [0x7fffc131d690]
(ip=00007ff1df9a2555) (sp=00007fffc131d6b0): (__libc_start_main+0xf5) [0x7fffc131d6b0]
(ip=00000000004007b9) (sp=00007fffc131d770): (+0xf5) [0x7fffc131d770]

五、基于Frame Pointer和基于unwind 形式的棧回溯比較

(1)基于Frame Pointer - fp寄存器的棧回溯:
優點:棧回溯比較快,理解簡單。相對較簡單:基于Frame Pointer寄存器的棧回溯通常比解析unwind節更簡單直接。
缺點:gcc添加了優化選項 -O 就會省略掉省略基指針。這樣就不能都通過這種形式進行棧回溯了。
-fomit-frame-pointer編譯標志進行優化:避免將%rbp用作棧幀指針,把FP當作一個通用寄存器,這樣就提供了一個額外的通用寄存器,提高程序運行效率。
通用寄存器用來暫存數據和參與運算。通過load\store指令操作。

如果把fp寄存器當作棧幀寄存器,那就不能參與指令數據運算,CPU寄存器是很寶貴的,多一個寄存器對加快指令數據運算是有積極意義的。

(2)基于unwind 形式的棧回溯:
優點:只是將入棧相關的指令的編碼保存到unwind段中,不用把無關的寄存器保存到棧中,也不用浪費fp寄存器。
把FP當作一個通用寄存器,這樣就提供了一個額外的通用寄存器,提高程序運行效率。
更準確:unwind節中的調試信息提供了更詳細的函數調用和棧幀信息,可以更準確地還原函數調用鏈和參數傳遞。
不受優化影響:unwind節通常包含了編譯器生成的準確信息,不受編譯器優化選項的影響。
提供更多調試功能:unwind節提供了豐富的調試信息,可以用于更深入的調試和錯誤診斷。

缺點:棧回溯的速度肯定比fp形式棧回溯慢,理解難度要比fp形式大很多。
復雜性:解析和使用unwind節的調試信息可能需要更多的工具和技術知識。

參考資料

https://refspecs.linuxfoundation.org/LSB_4.0.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html#EHFRAME
https://github.com/libunwind/libunwind

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

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

相關文章

雙向鏈表C++,C#,Java版,這些程序大多已經過測試,一直在用。

先C版吧&#xff0c;我最先用的是C#,后來是Java&#xff0c;后來改用C版的&#xff0c;因為現在一直在用C&#xff0c;單鏈 表一直沒寫上去&#xff0c;因為我很少用&#xff0c;用的是雙鏈表。 執行代碼例子1&#xff1a; int main() { _DList<_string> s…

9.STL中list的常見操作(圖文并茂)

目錄 1.list的介紹及使用 1.1.list的構造 1.2 list iterator的使用 1.3. list capacity 1.4.list modifiers 1.5.list的迭代器失效 1.list的介紹及使用 list介紹 &#xff0c;可以通過以下圖直觀的感受到 vector 和 list 的區別 Vector 插入代價高&#xff0c;但便于排…

力扣HOT100 - 72. 編輯距離

解題思路&#xff1a; 動態規劃 class Solution {public int minDistance(String word1, String word2) {int n1 word1.length();int n2 word2.length();int[][] dp new int[n1 1][n2 1];for (int j 1; j < n2; j) dp[0][j] dp[0][j - 1] 1;for (int i 1; i < …

Android Studio 使用MQTT協議開發應用時怎樣關閉MQTT連接

Android Studio 使用MQTT協議開發應用時怎樣關閉MQTT連接 Android Studio 使用MQTT協議開發應用時關閉MQTT連接 在使用mqtt開發的時候&#xff0c;有時候需要通過 返回 按鈕關閉界面或者Activity時&#xff0c;關閉當前頁面使用的mqtt連接&#xff0c;這里有兩種方式徹底銷毀…

《藝術大觀》知網藝術刊:可加急, 出刊上網快

《藝術大觀》 《藝術大觀》征文通知 《藝術大觀》期刊誠邀學者、藝術家和文化工作者積極投稿&#xff0c;共同探索藝術領域的前沿問題&#xff0c;促進學術交流和藝術創作的發展。我們歡迎各類藝術形式的研究與評論&#xff0c;包括但不限于繪畫、雕塑、音樂、舞蹈、戲劇、電…

共享IP和獨享IP之間的區別了解一下

在網絡環境中&#xff0c;代理服務器作為一種常見的工具&#xff0c;用于保護用戶隱私和拓寬網絡訪問范圍。在選擇代理服務器時&#xff0c;用戶經常會遇到共享IP和獨享IP兩種選擇。那么&#xff0c;這兩種代理方式有何區別呢&#xff1f;今天就為大家詳細解讀這個問題。 兩者…

【數據結構】排序詳解(希爾排序,快速排序,堆排序,插入排序,選擇排序,冒泡排序)

目錄 0. 前情提醒&#xff1a; 1. 插入排序 1.1 基本思想&#xff1a; 1.2 直接插入排序 實現步驟&#xff1a; 動圖演示&#xff1a; 特性總結&#xff1a; 代碼實現&#xff1a; 1.3 希爾排序&#xff08;縮小增量排序&#xff09; 基本思想&#xff1a; 步驟演示&…

AI大模型如何賦能智能座艙

AI 大模型如何賦能智能座艙 從上海車展上&#xff0c;我們看到由于智能座艙配置性價比較高&#xff0c;已經成為車企的核心競爭點之一&#xff0c;隨著座艙硬件規模化裝車&#xff0c;蔚小理、嵐圖、極狐等新勢力開始注重座艙多模態交互&#xff0c;通過集成語音/手勢/觸控打造…

Leetcode—2769. 找出最大的可達成數字【簡單】

2024每日刷題&#xff08;139&#xff09; Leetcode—2769. 找出最大的可達成數字 實現代碼 class Solution { public:int theMaximumAchievableX(int num, int t) {return num t * 2;} };運行結果 之后我會持續更新&#xff0c;如果喜歡我的文章&#xff0c;請記得一鍵三連…

【實戰】SpringBoot整合Websocket、Redis實現Websocket集群負載均衡

文章目錄 前言技術積累什么是Websocket什么是Redis發布訂閱Redis發布訂閱與消息隊列的區別 實戰演示SpringBoot整合WebsoketWebsoket集群負載均衡 實戰測試IDEA啟動兩臺服務端配置nginx負載均衡瀏覽器訪問模擬對話 前言 相信很多同學都用過websocket來實現服務端主動向客戶端推…

【知識蒸餾】deeplabv3 logit-based 知識蒸餾實戰,對剪枝的模型進行蒸餾訓練

本文將對【模型剪枝】基于DepGraph(依賴圖)完成復雜模型的一鍵剪枝 文章中剪枝的模型進行蒸餾訓練 一、邏輯蒸餾步驟 加載教師模型定義蒸餾loss計算蒸餾loss正常訓練 二、代碼 1、加載教師模型 教師模型使用未進行剪枝&#xff0c;并且已經訓練好的原始模型。 teacher_mod…

利用Python去除PDF水印

摘要 本文介紹了如何使用 Python 中的 PyMuPDF 和 OpenCV 庫來從 PDF 文件中移除水印&#xff0c;并將每個頁面保存為圖像文件的方法。我們將深入探討代碼背后的工作原理&#xff0c;并提供一個簡單的使用示例。 導言 簡介&#xff1a;水印在許多 PDF 文件中都很常見&#x…

全國數據庫管理系統設計賽-人大金倉內核實訓安排正式發布

作為數據庫領域國家隊&#xff0c;人大金倉積極響應國家戰略&#xff0c;通過賽題設計、內核技術支撐及賽前培訓等多方面&#xff0c;大力支持全國大學生計算機系統能力大賽-數據庫管理系統設計大賽成功舉辦。目前第二屆全國大賽正在火熱報名中&#xff0c;各種獎項等你來拿&am…

《web應用設計》第八次作業

我的小組長是姚若希&#xff0c;我們組課程設計的題目是&#xff1a;學生管理系統 &#xff0c;我認領的功能模塊是&#xff1a;課程管理 2.查詢并分頁

Html5 + Css3筆記詳細匯總大全

Html5 + Css3知識點Gitee地址 html5css3知識點: 尚硅谷—HTML5+CSS3知識點 介紹 屬性 屬性:在標簽中(開始標簽或自結束標簽)還可以設置屬性 屬性是一個名值對(x=y) 屬性用來設置標簽中的內容如何顯示 屬性和標簽名或其它屬性應該使用空格隔開 屬性名不能瞎寫,應該根據文…

只需三步,即可配置HTTPS跳轉

HTTPS&#xff08;全稱&#xff1a;Hyper Text Transfer Protocol over Secure Socket Layer&#xff09;&#xff0c;是以安全為目標的HTTP通道&#xff0c;簡單講是HTTP的安全版。通過SSL/TLS協議對數據進行加密&#xff0c;保證了數據傳輸的安全&#xff0c;防止數據被截獲、…

UWB論文:Introduction to Impulse Radio UWB Seamless Access Systems(2):脈沖;超寬帶;測距;定位

3) 測距/接收器 像全球定位系統&#xff08;GPS&#xff09;這樣的系統依賴于單向測距One Way Ranging&#xff08;OWR&#xff09;&#xff0c;其中多個衛星&#xff08;代表固定節點&#xff0c;稱為錨點anchors&#xff09;定期傳輸同步的無線電數據包集合&#xff0c;這允許…

sh控制臺輸入文字多行 按“# ? ?”結束

如果在Unix shell中輸入多行文字&#xff0c;那么這樣操作&#xff1a; 1. 打開您的終端&#xff08;Terminal&#xff09;。 2. 輸入您的文字&#xff0c;每行文字后按回車鍵。 3. 當您完成輸入所有文字后&#xff0c;輸入“# ? ?”然后按回車鍵&#xff0c;表示輸入結束。…

將Surface的分辨率減半以省電(二合一本\筆記本電腦適用)

【完全自定義分辨率教程】這篇教程用于將Surface之類的高分屏&#xff08;高分辨率&#xff09;的二合一本或筆記本等的分辨率調整為原來的一半&#xff0c;以實現省電等目的。 下載CRU&#xff08;Custom Resolution Utility&#xff09;解壓后&#xff0c;打開CRU.exe選擇當…

Java期末復習指南(1):知識點總結+思維導圖,考試速成!

&#x1f516;面向對象 &#x1f4d6; Java作為面向對象的編程語言&#xff0c;我們首先必須要了解類和對象的概念&#xff0c;本章的所有內容和知識都是圍繞類和對象展開的&#xff01; ? 思維導圖1 ? 類和對象的概念 ? 簡單來說&#xff0c;類就是對具有相同特征的一類事…