淺談 Linux 中的 core dump 分析方法

文章目錄

  • 一、什么是 core dump
  • 二、發生 core dump 的原因
    • 1. 空指針或非法指針引起 core dump
    • 2. 數組越界或指針越界引起的 core dump
    • 3. 數據競爭導致 core dump
    • 4. 代碼不規范
  • 三、core dump 分析方法
    • 1. 啟用 core dump
    • 2. 觸發 core dump
      • 2-1. 因空指針解引用而崩潰
      • 2-2. 通過 SIGSEGV 信號觸發 core dump
    • 3. gdb 分析 core dump
  • 總結

在 Linux 系統開發領域中,core dump(核心轉儲)是一個不可或缺的工具,它為我們提供了在程序崩潰時分析程序狀態的重要線索。當程序因為某種原因(如段錯誤非法指令等)異常終止時,Linux 系統會嘗試將程序在內存中的映像、程序計數器、寄存器狀態等信息寫入到一個名為 core 的文件中,這個文件就是所謂的 core dump。

對于開發者而言,core dump 文件如同一塊寶藏,其中蘊含著程序崩潰時的現場信息。通過對 core dump 文件的分析,我們可以了解到程序在崩潰時的內存布局、函數調用棧、變量值等重要信息,從而幫助我們快速定位問題原因,優化代碼,提高程序的健壯性。

在本文中,我們將探討 Linux 中 core dump 的分析方法。通過一些簡單的案例來演示 core dump 分析的實際應用,幫助讀者更好地理解和掌握這一技術。
在這里插入圖片描述

一、什么是 core dump

核心轉儲(core dump),在漢語中有時戲稱為吐核,是操作系統在進程收到某些信號而終止運行時,將此時進程地址空間的內容以及有關進程狀態的其他信息寫出的一個磁盤文件。這種信息往往用于調試。

在 UNIX 系統中,常將“主內存稱為核心(core),因為在使用半導體作為內存材料之前,便是使用核心(core)。而核心映像(core image)就是 “進程”(process)執行當時的內存內容。當進程發生錯誤或收到 “信號”(signal)而終止執行時,系統會將核心映像寫入一個文件,以作為調試之用,這就是所謂的核心轉儲(core dump)。

有時程序并未經過徹底測試,這使得它在執行的時候一不小心就會找到破壞。這可能會導致核心轉儲(core dump)。幸好,現行的 UNIX 系統極少會面臨這樣的問題。即使遇到,程序員可以通過核心映像(core image)調試程序來找到錯誤原因。

——引用:核心轉儲_百度百科 (baidu.com)

可以這樣去理解,core dump 是程序運行時在突然崩潰的那一刻的一個內存快照。操作系統在程序發生異常而異常在進程內部又沒有被捕獲的情況下,會把進程此刻內存、寄存器狀態、運行堆棧等信息轉儲保存在一個 core 文件里。這個 core 文件是二進制文件,可以使用 gdbelfdumpobjdump 或者 Windows 下的 windebug 進行打開此文件,并分析里面的具體內容,找出 core dump 的具體原因,并解決問題。

[!NOTE]

core 是在半導體作為內存材料前的線圈,當時用線圈當做內存材料,線圈叫做 core。用線圈做的內存叫做 core memory。故 core dump 也可稱為 core memory dump,真是個充滿歷史味道的詞。

在 Linux 系統下開發,時常會遇到程序突然崩潰了,且沒有留下任何日志的情況,這時就可以查看 core 文件。從 core 文件中分析原因,通過 gdb 看出程序掛在哪里,分析前后的變量,找出問題的原因。

二、發生 core dump 的原因

C/C++ 程序員遇到的比較常見的一個問題,就是自己編寫的代碼, 在運行過程中出現了意想不到的 core dump。程序發生 core dump 的原因是多方面的,不同的 core dump 問題有著不同的解決辦法。同時,不同的 core dump 問題解決的難易程度也存在很大的區別。有些在短短幾秒鐘內就可以定位問題,但是也有一些可能需要花費數天時間才能解決。這種問題是對軟件開發人員的極大的挑戰。筆者從事 C/C++ 語言的軟件開發工作多年,前后解決了許多此類問題,久而久之積累了一定的經驗,現把常見 core dump 總結一下。

1. 空指針或非法指針引起 core dump

空指針或非法指針(野指針、懸空指針)引起 core dump 是一種最常見的核心轉儲,大致可以有 3 種原因導致程序出現異常:

  1. 對空指針進行解引用等操作;

  2. 聲明指針變量后未進行初始化,并直接進行操作,極大概率引發 core dump,此類未經初始化的指針,統稱野指針

  3. 對某個指針,調用了 free 函數或者 delete 函數,該指針指向的空間已經被釋放,但未將該指針重新指向 NULL,此類指針成為懸空指針。對懸空指針再次操作,也會引發 core dump;

此類問題通常是代碼編寫時的疏漏造成的,屬于低級 bug,也比較容易解決的問題。Linux 平臺常用的 core dump 文件分析工具是 gdb,調試一下產生的 core 文件,對照代碼定位問題出現的原因,可以輕松解決問題。

2. 數組越界或指針越界引起的 core dump

提到這個,筆者不由得想起互聯網大廠百度的一道 C 語言面試題,如下代碼:

#include <stdio.h>int main()
{int i;int array[6];for (i = 0; i < 8; i++) {array[i] = 0;printf("Grayson Zheng\n");}return 0;
}

問:以上代碼中的 printf 函數會執行多少次?

這個問題的答案在不同操作系統下有不同的答案,當下只討論 Linux 系統的結果,執行該程序,結果如下:

在這里插入圖片描述

可以看出,在打印了 8 次之后,程序結束,但這并不是一次正常的結束,而是一次 core dump。不難看出這是數組越界導致的內存踩踏,數組定義了 6 個元素,遍歷完 6 個元素之后,還對數組之外的內存進行了操作,從而引發了這次的 core dump。

這種情況還相對簡單,而指針越界引發的 core dump,有的是就比較簡單,有的就屬于一種隱藏比較深的 core dump 了。遇到這種問題時,在調試 core 文件,盡管也能定位到代碼行,但是有可能唄定位到的那行代碼本身并沒有什么問題,它只是一個 “被陷害者”。

根據經驗,這種 core dump 問題很可能是其他代碼處理過程中的內存越界造成的(親身經歷:一個指針越界導致內存踩踏,讓 7.5 萬臺機器拆包重流,經濟損失估計超過 40 w。當然,我不是那個寫 bug 的人,哈哈),通常由以下兩個原因引起:

  1. 假如有以下三個全局變量:

    int  global_vsrisble_a;
    char global_vsrisble_b;
    char global_vsrisble_c;
    

    在不同操作系統中,這個三個全局變量在內存的位置可能不一樣,以 Ubuntu 為例,三個全局變量的內存位置分布如下圖所示:

    在這里插入圖片描述

    假設在某些做了如下代碼所作的事:

    #include <stdio.h>int  global_vsrisble_a = 0x11223344;
    char global_vsrisble_b = 0x55;
    char global_vsrisble_c = 0x66;int main()
    {printf("%p = 0x%X\n%p = 0x%X\n%p = 0x%X\n", &global_vsrisble_a,global_vsrisble_a, &global_vsrisble_b, global_vsrisble_b,&global_vsrisble_c, global_vsrisble_c);char *p_1 = (char *)(&global_vsrisble_a);p_1 += 2;int *p_2 = (int *)p_1;*p_2 = 0x09ABCDEF;printf("%p = 0x%X\n%p = 0x%X\n%p = 0x%X\n", &global_vsrisble_a,global_vsrisble_a, &global_vsrisble_b, global_vsrisble_b,&global_vsrisble_c, global_vsrisble_c);return 0;
    }
    

    [!CAUTION]

    以上代碼只是為了示范,現實情況并不可能如此。

    執行代碼后如下:

    0x6447cc49a010 = 0x11223344
    0x6447cc49a014 = 0x55
    0x6447cc49a015 = 0x66
    0x6447cc49a010 = 0xCDEF3344
    0x6447cc49a014 = 0xFFFFFFAB
    0x6447cc49a015 = 0x9
    

    從執行結果來看,global_vsrisble_bglobal_vsrisble_c 的值被破環。

    舉這個例子是為了說明,如果通過調試工具定位到是因為 global_vsrisble_b 的值被破壞了,很可能不是操作 global_vsrisble_b 的代碼有問題,而是操作 global_vsrisble_a 或者 global_vsrisble_c 失誤,導致了 global_vsrisble_b 的出錯,進而引發 core dump。

  2. 內存變量的值莫名其妙出現奇怪的值。跟上面的情況有點類似,也是因為有些變量相鄰問題被覆蓋原有的值。例如,執行了 memcpystrcpy 等函數(string.h 涉及到復制功能的函數,在復制過程中是不會檢查是否有越界的風險的)引起的 core dump。對于這類問題,肯定是代碼走到了某個特殊的邏輯里面,代碼處理缺少必要的保護而引起的。

    此類 core dump 可以通過復現 bug,對比前后兩次的 core 文件,找出內存變量存在的某種共性特征,根據這個特征來分析解決問題。

    [!NOTE]

    曾經在工作中遇到過一個 core bump,起因是對一段未初始化的緩沖存儲區做字符串搜索(搜索并不會引發 core dump)。但是代碼流程走了很長一段之后,對一個與緩沖存儲區相鄰的變量執行了操作,導致了 core dump。

3. 數據競爭導致 core dump

多線程訪問全局變量,如果不進行適當的同步保護,確實可能導致內存值異常,從而引發不可預測的行為,甚至可能導致程序崩潰并生成核心轉儲文件(core dump)。這種問題通常稱為 “數據競爭” 或 “競態條件”(race condition)。

競態條件是指兩個或多個線程同時訪問共享數據,并且至少有一個線程在修改數據時未進行適當的同步。這可能導致以下問題:

  1. 數據不一致:多個線程讀取和修改全局變量時,可能會導致數據處于不一致的狀態。
  2. 程序崩潰:未同步的訪問可能導致非法的內存訪問,從而引發段錯誤(segmentation fault),導致程序崩潰并生成核心轉儲文件。

4. 代碼不規范

初學者有時候編譯一個程序,出現了一整頁的編譯錯誤,其實這種情況也不用擔心,很可能就是某一行代碼多了幾個字符,當把這些代碼刪去再編譯,幾百個編譯錯誤全都消失了。

有些時候,程序發生 core dump 的根本原因還是程序員自己進行程序設計時的編碼失誤造成的,這種代碼失誤絕大多數都是因為沒有嚴格遵守相應的代碼編寫規范(比如用 0 做為除數等)。所以,要從根本上杜絕或者減少程序 core dump 的發生,還是要從嚴格遵守代碼編寫規范來做起。

三、core dump 分析方法

1. 啟用 core dump

默認情況下,程序運行崩潰導致 core dump,是不會生成 core 文件的,因為系統的 RLIMIT_CORE(核心文件大小)資源限制,默認情況下設置為 0。

使用 ulimit -c 命令可以查看 core 文件的大小,其中 -c 的含義是 core file size,單位是 blocks 也就是 KB 的意思。ulimit -c 命令后面可以寫整數,表示生成寫入值大小的 core 文件。如果使用 ulimit -c unlimited 設置無限大,則任意情況下都會產生 core 文件。

以下命令可在用戶進程觸發信號時啟用 core dump 生成,并使用合理的名稱將核心文件位置設置為 /tmp/。請注意,這些設置不會永久存儲。

ulimit -c unlimited
echo 1 > /proc/sys/kernel/core_uses_pid
echo "/tmp/core-%e-%s-%u-%g-%p-%t" > /proc/sys/kernel/core_pattern

[!IMPORTANT]

后面兩條命令在運行時,即使是加了 sudo 執行,也可能會被提示權限不足。這可能是由于 shell 的重定向在命令前已經處理完成,因此重定向操作并沒有被提升到超級用戶權限,這就導致了 “Permission denied” 的錯誤。可以通過以下命令來解決這個問題:

echo 1 | sudo tee /proc/sys/kernel/core_uses_pid
echo "/tmp/core-%e-%s-%u-%g-%p-%t" | sudo tee /proc/sys/kernel/core_pattern

順便解釋一下 "/tmp/core-%e-%s-%u-%g-%p-%t" 的各個參數的含義:

  • %e:導致 core dump 的程序的可執行文件名。
  • %s:導致 core dump 的信號編號。
  • %u:導致 core dump 的程序的實際用戶 ID。
  • %g:導致 core dump 的程序的實際組 ID。
  • %p:導致 core dump 的程序的進程 ID。
  • %t: core dump 發生時的時間戳(自 epoch 時間以來的秒數)。

因此,/tmp/core-%e-%s-%u-%g-%p-%t 會生成包含如下信息的 core 文件:

/tmp/core-<executable>-<signal>-<uid>-<gid>-<pid>-<timestamp>

舉個例子,如果一個進程名為 my_program,用戶 ID 為 1000,組 ID 為 1000,進程 ID 為 12345,并且在 1617701234 時間點崩潰于信號 11,則生成的 core 文件名將是:

/tmp/core-my_program-11-1000-1000-12345-1617701234

2. 觸發 core dump

我們使用兩個簡單的 C 程序作為示例。

2-1. 因空指針解引用而崩潰

文件名為 example.c

#include <stdio.h>void func()
{int *p = NULL;*p = 13;
}int main()
{func();return 0;
}

編譯并運行程序:

gcc -g -o example example.c
./example

運行程序時后,會在 /tmp/ 文件夾下生成一個 core 文件。

在這里插入圖片描述

2-2. 通過 SIGSEGV 信號觸發 core dump

文件名為 example2.c

#include <stdio.h>
#include <unistd.h>int global_num;int main()
{while(1) {printf("global_num = %d\n", global_num++);sleep(1);}return 0;
}

編譯并運行程序:

gcc -g -o example2 example2.c
./example2

運行程序時后,在另一個終端查找進程的 PID,并用 kill -11 加上 PID,向進程發送段錯誤信號,結束掉進程。之后會在 /tmp/ 文件夾下生成一個 core 文件。

在這里插入圖片描述

3. gdb 分析 core dump

兩個例子都是段錯誤導致的 core dump,所以用 gdb 調試的方法也是一樣的,命令格式如下:

gdb <program_name> <core_dump_file>

比如先調試第一個例子的 core 文件,則輸入 gdb example,再加上 core 文件名,命令如下(建議先提前復制 core 文件名,不知道為什么,按 Tab 鍵不給補齊):

gdb example /tmp/core-example-11-1000-1000-88496-1719910934

隨后可以看到,gdb 提示在代碼第 6 行的地方出現了段錯誤,如下圖:

在這里插入圖片描述

如果函數關系調用關系很復雜,可以用 bt 命令(全稱 backtrace,堆棧的意思)查看調用堆棧(where 命令也有同樣功能),如下圖可知是在調用 func 函數時產生的段錯誤,可用 list 命令查看,具體就是 list 加函數名,如下圖。找到提示錯誤的那一行代碼,print 命令可以打出 p 的值,由下圖可知,p 是空指針,不能進行解引用操作。

輸入 quitexit 可以退出 gdb。

在這里插入圖片描述

第二個例子,也是同樣用 gdb 打開 core 文件:

gdb example2 /tmp/core-example2-11-1000-1000-88552-1719911473

執行結果如下圖:

在這里插入圖片描述

雖然這個段錯誤是因為我們人為地發送了 SIGSEGV 信號,導致了程序地段錯誤,而在打開 core 文件后,可以看出在執行 __GI___clock_nanosleep 函數時,遇到了段錯誤。

[!NOTE]

通常情況下,分析 core dump 問題,除了 core 文件之外,還會結合程序的 log 信息和系統的 log 信息(包括 kernel log、systemd log 等)一起分析。

當然人為故意制造出來的 core dump,有時候是分析不出來的。所以這個例子的作用在于分析的過程,也順便告訴大家,不是所有的 core dump 都可以分析出具體原因。

如果我們不事先知道是由 SIGSEGV 信號導致段錯誤的,首先要用 bt 命令找到函數的調用關系鏈:

在這里插入圖片描述

由上圖可知,先是在 main 函數調用了 __sleep 函數,接著 __sleep 函數調用了 __GI___nanosleep 函數,__GI___nanosleep 函數調用了 __GI___clock_nanosleep 函數,到這里,執行到了 __GI___clock_nanosleep 函數的第 78 行時,發生了段錯誤,使程序崩潰。

此時,我們是沒辦法通過 list 命令去找出問題的,因為棧區的那三個函數是封裝后的庫函數,根本看不到源碼:

在這里插入圖片描述

在輸入 bt 命令查看堆棧情況時,有出現了兩個變量,分別是 reqrem。使用過nanosleep 函數的小伙伴可能會很眼熟這兩個變量,因為這個兩個變量是 nanosleep 函數的形參,原型是 int nanosleep(const struct timespec *req, struct timespec *rem)

print 命令打印出兩個變量的地址:

在這里插入圖片描述

使用 info registers 命令查看寄存器狀態,檢查程序在崩潰時的上下文:

在這里插入圖片描述

從寄存器狀態來看,沒有明顯的錯誤跡象,函數的棧幀空間沒什么問題,形參的位置和值也沒什么問題,所有值看起來都在正常范圍內。

當下是沒辦法直接了當的判斷為人為干預造成 core dump,如果此時想到了信號會引發段錯誤,可以用 info signals 命令查看信號情況:

在這里插入圖片描述

info signals 的輸出中可以看出,SIGSEGV(Segmentation fault)信號是設置為在程序接收到該信號時停止執行并打印信息的。也就說,可以人為地使用 kill -11 發送了 SIGSEGV 信號來終止程序并生成 core dump。

總結

分析 core dump 的具體原因不可能僅憑兩個案例就學會,本文只是提供一個基本的排除思路和方法。通過查看調用堆棧、源代碼和變量的值,可以逐步確定程序崩潰的原因。通過向程序發送 SIGSEGV 信號來生成 core 文件是一個有效的調試手段。通過 gdb,可以詳細分析程序在崩潰時的狀態,并確定具體的崩潰原因。確保在信號觸發時,檢查程序的變量和內存狀態,能夠幫助你更好地理解和解決程序中的問題。

之后如果遇到一個實際工作中產生的 core dump,且具有學習價值,我一定會總結這個分析過程,并輸出成文檔的形式,分享給大家,共勉,respect~

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

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

相關文章

圖形編輯器基于Paper.js教程06:鼠標畫圓與橢圓

繪制橢圓與圓形&#xff1a;利用Paper.js進行交互式圖形設計 在Web應用中實現交互式圖形繪制功能&#xff0c;對于提高用戶體驗至關重要&#xff0c;尤其是在設計和藝術相關的應用中。Paper.js是一款強大的JavaScript庫&#xff0c;專門用于處理矢量圖形&#xff0c;它提供了一…

智能語音門鎖:置入NV170D語音芯片ic 打造便捷生活新體驗

一、智能門鎖語音芯片開發背景 隨著科技的飛速發展&#xff0c;傳統門鎖的局限性日益凸顯&#xff0c;無法滿足現代人對高效、安全生活的需求。在這樣的時代背景下&#xff0c;智能門鎖應運而生&#xff0c;它不僅繼承了傳統門鎖的基本功能&#xff0c;更通過融入先進的科技元素…

商標的近似分辯,商標起名稱時注意!

曾有過網友發來商標名稱&#xff0c;普推知商標老楊說有近似&#xff0c;然后網友起過新名稱還是存有近似&#xff0c;或者加字&#xff0c;后面加的通用詞&#xff0c;與先有商標名稱也是近似。 “良信健康”這個名稱健康是行業通用詞&#xff0c;加成健康后變成四個字&#x…

出現 images and labels...0 found, xx missing, 0 empty, 0 corrupt 解決方法

目錄 1. 問題所示2. 原理分析3. 解決方法1. 問題所示 訓練VOC的數據的時候出現如下問題: val: Scanning /home/l228/huoyanhao/yolov5/datasets/VOC/images/VOCdevkit/VOC2007/2007_val images and labels...0 found, 2510 missing, 0 empty, 0 corrupt: 100%|███████…

HTTP協議深入

1.了解web和網絡基礎 有客戶端和服務端雙方參與交互 客戶端發送請求:request 服務端根據請求給出響應:response 請求通過URL來指定要獲取都得資源 響應內容可以是HTML網頁&#xff0c;或者用json表示的數據或者其他二進制文件內容 Web使用一種名為HTTP的協議作為規范&…

jEasyUI 添加分頁組件

jEasyUI 添加分頁組件 jEasyUI(jQuery EasyUI)是一個基于jQuery的用戶界面插件集合,它為用戶提供了一系列的UI組件,如菜單、窗口、數據網格等,以簡化Web頁面的開發。分頁組件是jEasyUI中的一個重要部分,它允許用戶在處理大量數據時,將數據分頁顯示,提高用戶體驗和數據…

AI與大模型工程師證書研修班報名啦!

人工智能大模型是指擁有超大規模參數&#xff08;通常在十億個以上&#xff09;、超強計算資源的機器學習模型&#xff0c;能夠處理海量數據&#xff0c;完成各種復雜任務&#xff0c;如自然語言處理、圖像識別等。計算機硬件性能不斷提升&#xff0c;深度學習算法快速優化&…

ESP32CAM物聯網教學03

ESP32CAM物聯網教學03 物聯網小車 小智突發奇想&#xff1a;要是我在點燈物聯APP中多增加幾個按鈕&#xff0c;控制小車的行駛方向&#xff0c;不就可以做成遙控小車了嗎&#xff1f; 點燈物聯控制小車的行駛方向 我們可以重新編輯點燈物聯APP中的設備控件界面&#xff0c;如…

自定義控件之動畫篇(六)——聯合動畫的代碼及xml實現

在Android中&#xff0c;聯合動畫&#xff08;即組合多種類型的動畫&#xff09;可以通過編寫Java/Kotlin代碼或XML資源文件來實現。這里我們將分別展示如何通過這兩種方式來實現一個簡單的自定義控件動畫&#xff0c;該動畫將包含平移和縮放效果。 1. XML 資源文件實現 首先…

AI學習指南機器學習篇-梯度提升樹模型應用與Python實踐

AI學習指南機器學習篇-梯度提升樹模型應用與Python實踐 機器學習領域中的梯度提升樹&#xff08;Gradient Boosting Tree&#xff09;模型是一種非常強大且廣泛應用的模型&#xff0c;它在各種數據類型和問題類型上都表現出色。在本篇博客中&#xff0c;我們將介紹如何使用Pyt…

開關電源中強制連續FCCM模式與輕載高效PSM,PFM模式優缺點對比筆記

文章目錄 前言一、連續FCCM模式優點&#xff1a;缺點&#xff1a; 二,輕載高效PSM&#xff0c;PFM優點&#xff1a;缺點: 總結 前言 今天我們來學習下開關電源中&#xff0c;強制連續FCCM模式與輕載高效PSM&#xff0c;PFM模式優缺點對比 一、連續FCCM模式 優點&#xff1a; …

mac中如何恢復因為破解腳本導致的IDEA無法啟動的問題

問題 為了在mac中安裝免費的2024版idea&#xff0c;導致下載了一個腳本&#xff0c;使用這個腳本后&#xff0c;但是發現idea還沒有破解&#xff0c;相反導致idea無法啟動&#xff0c;每次點擊&#xff0c;都會彈出“cannot start IDE…” 問題排查 在訪達中點擊mac的應用程…

docker -run hello-world超時

主要原因就是嘗試拉取庫的時候沒有從阿里云鏡像里拉&#xff0c;所以設置一下就好了 這里使用的是ubuntu系統&#xff08;命令行下逐行敲就行了&#xff09; sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json <<-EOF {"registry-mirrors": [&quo…

什么是生成式人工智能

什么是生成式人工智能 生成式人工智能生成式人工智能的特點生成式人工智能的工作原理生成式人工智能的類型生成式人工智能面臨的挑戰數據要求訓練復雜性控制輸出道德問題監管障礙 生成式人工智能 生成式人工智能是指旨在生成書面文本、音頻、圖像或視頻形式的新內容的人工智能…

Adobe Acrobat添加時間戳服務器

文章目錄 前言一、Adobe Acrobat添加時間戳服務器1.打開Adobe Acrobat軟件2.點擊【菜單】→ 【首選項】3.點擊【安全性】→【更多】4.點擊【新建】5.輸入【名稱】→【服務器URL】 前言 一、Adobe Acrobat添加時間戳服務器 1.打開Adobe Acrobat軟件 2.點擊【菜單】→ 【首選項…

模擬退火算法1——簡介

模擬退火算法來源于固體退火原理&#xff0c;將固體加溫至充分高&#xff0c;再讓其徐徐冷卻&#xff0c;加溫時&#xff0c;固體內部粒子隨溫升變為無序狀&#xff0c;內能增大&#xff0c;而徐徐冷卻時粒子漸趨有序&#xff0c;在每個溫度都達到平衡態&#xff0c;最后在常溫…

[C++][設計模式][訪問器]詳細講解

目錄 1.動機2.模式定義3.要點總結4.代碼感受1.代碼一2.代碼二 1.動機 在軟件構件過程中&#xff0c;由于需求的變化&#xff0c;某些類層次結構中常常需要增加新的行為(方法)&#xff0c;如果直接在基類中做這樣的更改&#xff0c; 將會給子類帶來很繁重的變更負擔&#xff0c…

加密基本知識:密鑰、簽名、證書

一、密碼(clpher) 是一種用于加密或者解密的算法 密碼學中的密碼&#xff08;cipher&#xff09;和我們日常生活中所說的密碼不太一樣&#xff0c;計算機術語『密碼 cipher』是一種用于加密或者解密的算法&#xff0c;而我們日常所使用的『密碼 password』是一種口令&#xff…

數據恢復篇:5 款最佳 Mac 數據恢復軟件

說到保護我們的數字生活&#xff0c;數據恢復軟件的重要性怎么強調都不為過。無論您是意外刪除了假期照片的普通用戶&#xff0c;還是面臨硬盤損壞的專業人士&#xff0c;隨之而來的恐慌都是普遍存在的。幸運的是&#xff0c;數據恢復工具可以緩解這些壓力。在Mac用戶可用的眾多…

搜維爾科技:使用Manus VR手套和ART光學追蹤功能實現虛擬場景工業仿真操作

&#xff1a;使用Manus VR手套和ART光學追蹤功能實現虛擬場景工業仿真操作 搜維爾科技&#xff1a;使用Manus VR手套和ART光學追蹤功能實現虛擬場景工業仿真操作