深入理解計算機系統


深入理解計算機系統

計算機系統漫游

  • 源文件到目標文件的翻譯過程可分為四個階段, 這四個階段的程序被稱為預處理器,編譯器,匯編器和鏈接器,它們一起構成了編譯系統(compilation system)。
    編譯系統
  • 緩沖區溢出是造成大多數網絡和Internet服務器上安全漏洞的主要原因。
    計算機系統的硬件組成
  • 主存是一個臨時存儲設備,用來存放程序和程序處理的數據。
    • 從物理上來說, 主存是由一組動態隨機存儲器(DRAM)芯片組成。
    • 邏輯上, 主存是一個線性的字節數組,每個字節都有唯一的地址, 這些地址從零開始。
  • 中央處理器(CPU)的核心是程序計數器(PC),PC都指向主存中的某條機器語言指令(即含有該條指令的地址)。
    • 處理器一直在不斷地執行程序計數器指向的指令, 再更新程序計數器,使其指向下一條指令。
  • 指令集結構描述的是每條機器代碼指令的效果; 而微體系結構描述的是處理器實際上是如何實現的。
  • 復制就是開銷, 利用高級緩存減少數據間的拷貝。
    存儲器的高級緩存
  • 操作系統有兩個基本功能:
    • 防止硬件被失控的應用程序濫用;
    • 向應用程序提供簡單一致的機制來控制復雜而又通常大相徑庭的低級硬件設備。
  • 操作系統有幾個抽象概念: 進程, 虛擬存儲器和文件。
    • 文件是對I/O設備的抽象表示;
    • 虛擬存儲器是對主存和磁盤I/O設備的抽象表示;
    • 進程是對處理器,主存和I/O設備的抽象表示。
      • 進程是操作系統對一個正在運行的程序的一種抽象,每個進行好像獨占地使用硬件。
      • 一個進程實際上可以由多個線程的執行單元組成,每個線程都運行在進程的上下文中,並共享全局的代碼和全局的數據。
      • 每個進程看到的是一致的存儲器,稱為虛擬地址空間。
        進程的虛擬地址空間
  • 並發與並行:
    • 並發是指同時有多個活動的系統;
    • 並行是指用並發使一個系統運行得更快。
    • 超線程也叫做同時多線程,允許一個CPU執行多個控制流的技術。 --- 允許一個CPU某些硬件有多個備份,而另外硬件部分只能有一份。(4核實際上可以執行8個線程)
  • 抽象是計算機科學中最為重要的概念之一。

程序結構與執行

  • 浮點數雖然可以編碼一個較大的數值范圍,但是這種表示只是近似的。
  • Java創造了一套新的數字表示和運算標准。
  • 所有可能地址的集合稱為虛擬地址空間, 虛擬地址空間(virtual address space)只是展現給機器級程序的概念性映像。
  • typedef提供了一種給數據類型命名的方式。
  • Java只支持有符號數。
    • 在Java中, 單字節數據類型稱為byte,而不是char,而且沒有long long數據類型。

程序的機器級表示

  • 目標代碼是機器代碼的一種形式,它包含所有指令的二進制表示,但是還沒有填入地址的全局值。
  • 機器級程序的格式和行為,定義為指令集體系結構(Instruction set architecture, ISA),定義了處理器狀態指令的格式,以及每條指令對狀態的影響。
  • 匯編代碼不區分有符號或無符號整數,不區分各種類型的指針,甚至不區分指針和整數。
  • 程序存儲器用虛擬地址來尋址。在任給定的時刻,只認為有限的一部分虛擬地址是合法的。
  • 操作系統負責管理虛擬地址空間,將虛擬地址翻譯成實際處理器存儲器中的物理地址。
  • 一條機器指令只執行一個非常基本的操作。
  • 機器世紀之星的程序是一系列指令驚喜編碼的字節序列。
  • 查看目標代碼文件的內容,最有價值的是反匯編(disassembler) --- objdump -d code.o.

匯編中一些語句的說明

  • 以‘.’開頭的行都是指導匯編器和鏈接器的命令,通常可以忽略這些行。

  • 有些指令以固定的寄存器作為源寄存器或目的寄存器。
    整數寄存器

  • 寄存器%ebp和%esp保存着指向程序棧中重要的位置指針。

    • %ebp是幀指針。
    • %esp是棧指針。
  • 存儲器引用(memory),會根據計算出來的地址(通常稱為有效地址)訪問某個存儲器位置。
    操作數格式

  • 數據傳送指令:
    數據傳送指令

    • 數據的條件轉移是指先計算一個條件操作的兩種結果,然后再根據條件是否滿足從而選一個。
  • 算術和邏輯操作:

    • 加載有效地址(load effective address)指令leal實際上是movl指令的變形,實際上是從寄存器讀數據到寄存器中。
      • 將有效地址寫入到目的操作數。
        算術和邏輯操作
    • 一元操作指只有一個操作數,即是源又是目的。
    • 特殊的算術操作:
      特殊的算術操作.
  • 控制:

    • 除了整數寄存器, CPU還維護着一組單個位的條件碼(condition code)寄存器,它們描述了最近的算術或邏輯操作的屬性。
      • CF(carry flag): 進位標志,最近的操作使最高位產生了進位,可以用來檢查無符號操作數的溢出。
      • ZF(zero flag): 零標志,最近的操作得出的結果為0.
      • SF(symbol flag): 符號標志位,最近的操作得到的結果為負數。
      • OF(over flag): 溢出標志,最近的操作導致一個補碼溢出---正溢出或負溢出。
        比較和測試指令
      • CMP指令根據兩個操作數之差來設置條件碼,如果兩個操作數之差相等,這些指令會將零標志設置為1.
      • TEST根據兩個操作數的與來設置零標志和負數標志。
        set指令
    • 匯編語言入門教程.
    • 跳轉(jump)指令會導致執行切換到程序中一個全新的位置 --- jmp .L1(標號, label)。
      • jmp指令是無條件跳轉,它可以是直接跳轉,也可以間接跳轉。
        jmp跳轉指令.
    • 基於條件數據傳送的代碼比基於條件控制轉移的代碼性能要好。
    • 處理器采用非常精密的分支預測邏輯試圖猜測每條跳轉指令是否會執行。
  • 過程:

  • 幾個有用的命令工具:

    • gcc -S 源文件.c --- 將源文件變為匯編文件。
    • gcc -c 源文件.c --- 將源文件變為.o的二進制文件。
    • objdump -d 目標文件或二進制可執行文件 --- 進行反匯編。
  • 匯編用條件測試和跳轉組合起來實現循環的效果。

  • 流水線通過重疊連續指令的步驟來獲得高性能。

    • 分支預測邏輯試圖猜測每條跳轉指令是否執行, 只要猜測比較可靠(90%以上的成功率),指令流水線中就會充滿着指令。
    • 錯誤預測會導致嚴重的懲罰,大約20~40個時鍾周期的浪費。
  • 當兩個表達式都很容易計算時,才使用條件傳送改進代碼的效率。

  • switch語句的匯編語句會做一個跳轉表放到.text文本段,優化多重分支跳轉的效率。

  • 轉移控制:

    • call Label --- 過程調用。
    • call *Operand --- 過程調用。
    • leave --- 為返回准備棧。
    • ret --- 從過程調用中返回。
    • call的效果是將返回地址入棧(eip所指的地址入棧)。
  • 程序寄存器組是唯一能被所有過程共享的資源。

    • 寄存器%eax, %edx和%ecx被互粉為調用者保存寄存器。
    • 寄存器%ebx, %esi, %edi被互粉為被調用者保存寄存器。(調用前把他們放到棧中, 調用后返回)
    • 必須保持寄存器%ebp和%esp.
  • 每個調用在棧中都有它自己的私有空間,多個未完成調用的局部變量不會互相影響。

  • leal指令可以用來產生地址。

  • 結構的所有組成部分都放到存儲器中的一段連續區域,而指向結構的指針就是結構第一個字節的地址。

    • 為了對齊和訪問結構的字段,編譯器產生的代碼要將結構的地址加上適當的編譯。
    • 聯合繞過了C語言類型系統提供的安全措施。
  • 數據對齊:

    • 要求某種類型對象的地址必須是某個值K(通常是2,4或8)的倍數。
    • 對齊限制是為了簡化處理器和存儲器系統之間接口的硬件設計。
    • 不同的操作系統可能對齊方式也會不一樣,Linux8字節的double進行4字節對齊,而Windows采取8字節對齊。
    • .align 4表示它后面的數據的起始地址是4的倍數。
    • struct結構體的對齊更為嚴格,內部成員都要保證對齊。
  • 函數指針的值是該函數機器代碼表示中第一條指令的地址。

  • 對越界的數組元素的寫操作會破壞存儲在棧中的狀態信息。(緩沖區溢出, buffer overflow)

  • 棧隨機化的思想使得棧的位置在程序每次運行時都有變化。

    • 實現方式是在程序開始時,在棧上分配一段0~n字節之間的隨機大小的空間。
    • 地址空間布局隨機化(Address-Space Layout Randomization):程序代碼,庫代碼,棧,全局變量,堆數據都會被加載到不同的區域。
  • gcc版本中加入了一種棧保護者機制(stack protector), 用來檢測緩沖區越界.

    • 在棧幀中任何緩沖區與棧狀態之間存儲一個特殊的金絲雀(canary)值, 也稱為哨兵值(guard value).
    • 最新的NX(No-eXecute)技術支持棧可讀可寫但不可執行。
  • callq指令將一個64位返回地址存儲在棧上。

    • 許多函數不再需要棧幀,只有那些不能將所有的局部變量都放到寄存器中的函數才需要在棧上分配空間。
    • 函數最多可以訪問超過當前棧幀值的128個字節的棧上存儲空間。
      • 一些信息存儲在棧上而不需修改棧指針。
    • 最多有6個參數可用寄存器進行傳參。
  • Java的目標代碼是一種特殊的二進制表示,稱為Java字節代碼, 這種代碼可以看作是虛擬機的機器級程序。

    • 用軟件解釋器處理字節代碼,模擬虛擬機的行為。

處理器體系結構

  • 一個處理器支持的指令和指令的字節級編碼稱為它的指令集體系結構(Instruction-Set Architecture, ISA).
  • 通過同時處理多條指令的不同部分,處理器可以獲得更好的性能(流水線工作模式)。
  • 硬件和操作系統軟件聯合起來將虛擬地址翻譯成實際地址(物理地址)。
  • RISC指令集和CISC指令集:
    RISC指令集和CISC指令集.
  • 控制邏輯是設計微處理器最難的部分。
  • 一條指令可以划分為幾個部分:
    • 取地址(fetch): 從存儲器讀取指令字節,地址為程序計數器(PC)的值。
    • 譯碼(decode): 譯碼階段從寄存器文件讀入最多兩個操作數。
    • 執行(execute): 算術邏輯單元要么執行指令指明的操作,計算存儲器引用的有效地址,要么增加或減少棧指針。
    • 訪存(memory): 訪問階段可以將數據寫入存儲器,或者從存儲其讀出數據。
    • 寫回(write back): 寫回階段最多可以寫兩個結果到寄存器文件。
    • 更新PC(PC update):將PC設置成下一條指令的地址。
      SEQ的硬件結構.
      流水線結構.
  • 流水線冒險:
    • 數據相關:下一條指令會用到這一條指令的結果。 --- 數據冒險。
      • 用暫停來避免數據冒險,處理器暫停一條或多條指令,直到冒險條件不滿足。(加入nop指令)
      • 用轉發避免數據冒險,從一個流水線階段傳到較早階段。(旁路)。
      • 用暫停(加載互鎖)和轉發結合起來,避免加載/使用數據冒險。
    • 控制相關: 一條指令要確定下一條指令的位置。--- 控制冒險。
      • 預測錯誤的分支。
  • 從處理器角度來看,將用暫停來處理短時間的告訴緩存不命中和用異常來處理長時間的缺頁結合起來,能估計到存儲器訪問時由於存儲器層次結構引起的所有不可預測性。

優化程序性能

  • 每元素的周期數作為一種表示程序性能並指導我們改進代碼的方法。
  • 消除循環的低效率。
  • 減少過程調用。
  • 理解現代處理器。
    • 超標量的意思是每個時鍾周期執行多個操作。
  • 循環展開: 增加每次迭代計算的元素數量,減少循環的迭代次數。
  • 提高並行性。

性能提高技術

  • 高級設計:
    • 為遇到的問題選擇適當的算法和數據結構。
  • 基本編碼原則:
    • 避免限制優化的因素,這樣編譯器能產生高效的代碼。
    • 消除連續的函數調用,將計算移到循環外,考慮妥協模塊性以獲得更大的效率。
    • 消除不必要的存儲器引用,引入臨時變量來保存中間結構,只有在最后值計算出來時,才將結果放到數組或全局變量中。
    • 用功能的風格重寫條件操作,使得編譯器采用條件數據傳送。

存儲器層次結構

  • 隨機訪問存儲器(Random-Access Memory, RAM), 分為靜態和動態的, 靜態RAM比動態RAM更快。
    • 動態RAM有干擾將不會恢復。
  • 固態硬盤(Solid State Disk, SSD)是由一個或多個閃存芯片和閃存翻譯層組成。
  • 一個編寫良好的計算機程序常常具有良好的局部性(locality): 時間局部性和空間局部性。
    • 局部性原理: 傾向於引用鄰近於其他最近引用過的數據項的數據項,或者最近引用過的數據項本身。
    • 被引用過一次的存儲器可能在不遠的將來會再被多次引用。
    • 在存儲器一個位置上被引用了一次,那么在不遠的將來可能會引用附近的一個存儲器位置。
      存儲器層次結構
    • 冷不命中: 通常是短暫的事件,不會在反復訪問訪問存儲器使得緩存暖身之后的穩定狀態中出現。
  • 高速緩存友好的代碼:
    • 對局部變量的反復引用是好的。(利用時間局部性)
    • 步長為1的引用模式是好的。(利用空間局部性)
    • 一旦從存儲器中讀入了一個數據對象,就盡可能多地使用它,從而使程序中的時間局部性最大。
      存儲器山
    • 存儲器系統性能是一座時間和空間局部性的山,這座山的上升高度差別可以超過一個數量級。
    • 利用時間局部性, 使得頻繁使用的字從L1中取出。
    • 利用空間局部性, 使得盡可能多的字從一個L1高速緩存中訪問到。

鏈接

  • 鏈接器程序ld,將main.o和swap.o以及一些必要的系統目標文件組合起來,創建一個可執行目標文件。
    • 可重定向目標文件。
    • 可執行目標文件。
    • 共享目標文件。
    • 編譯器和匯編器生成可重定位目標文件(包括共享目標文件)。
    • 鏈接器生成可執行目標文件。
  • 可重定位目標文件:
    • .text:已編譯程序的機器代碼。
    • .rodata:只讀數據,switch語句中的跳轉表。
    • .data:已初始化的全局C變量。
    • .bss: 未初始化的全局C變量。不占實際空間,僅僅是個占位符。
    • .symtab: 一個符號表, 它存放在程序中定義和引用的函數和全局變量的信息。但不包含局部變量的信息。
    • .rel.text: 一個.text節中位置的列表,當鏈接器把這個目標文件和其他文件結合時, 需要修改這些位置。
    • .rel.data: 被模塊引用或定義的任何全局變量的重定位信息。
    • .debug: 一個調試符號表,全局變量及類型定義,定義和引用的全局變量,以及原始的C源文件。
    • .line: 原始C源文件中的行號和.text節中機器指令之間的映射。(-g)
    • .strtab: 一個字符串表,其內容包括.symtab和.debug節中的符號表,以及節頭部中的節名字。
  • static全局變量在.data或.bss中分配空間,並在符號表中穿件一個有唯一名字的本地鏈接器符號。
    • 'cpp'在Unix系統中是c預處理器。
    • cc1是C編譯器。
    • as是C匯編器。
    • ld是鏈接器。
      靜態鏈接.
  • 函數和已初始化的全局變量是強符號,未初始化的全局變量是弱符號。
    • 不允許有多個強符號。
    • 如果有一個強符號和多個弱符號, 那么選擇強符號。
    • 如果有多個弱符號,那么沖這些弱符號中任意選擇一個。
    • 鏈接器通常不會表明它檢測到多個x的定義。
    • 遇到多重定義的全局符號時,輸出一條警告信息。
  • 把所有相關的目標模塊打包成一個單獨的文件,稱為靜態庫(static library),它可以用作鏈接器的輸入。
    • 使用AR工具可以創建靜態庫。
      與靜態庫鏈接
  • 可執行文件的連續片(chunk)被映射到連續的存儲器段。
    • 段頭部表(segment header table)描述了這種映射關系。
      可執行目標文件.
    • 通過調用留在存儲器中稱為加載器(loader)的操作系統代碼來運行它。
      • 通過execve函數來調用加載器, 加載器將可執行目標文件中的代碼和數據從磁盤拷貝到存儲器中,然后通過跳轉到程序的第一條指令和入口點(entry point --- 符號表_start的地址)來運行程序。
        Linux運行時存儲器映像.
      • 當shell運行一個程序時,父外殼進程生成一個子進程,它是父進程的一個復制品, 子進程通過execve系統調用啟動加載器;加載器刪除子進程現有的虛擬存儲器段,並穿件一組新的代碼、數據、堆和棧段,然后初始化為可執行文件的內容,最后跳轉到_start地址調用main函數執行。
      • 操作系統利用頁面調度機制自動將頁面從磁盤傳遞到存儲器。
  • 共享庫(shared library)是一個目標模塊, 在運行時,可以加載到任意的存儲器地址,並和另一個存儲器中的程序連接起來,以.so后綴表示。
    • 一個共享庫中的.text段的一個副本可以被不同的長在運行的進程共享。
      動態鏈接庫
    • 共享庫的一個主要目的是允許多個正在運行的進程共享存儲器中相同的庫代碼,因而節約寶貴的存儲器資源。
    • 位置無關碼(Position-Independent Code, PIC), GCC的-fPIC產生位置無關碼。
      • 數據段使用一個全局偏移量表(Global Offset Table, GOT)。
      • 不需要鏈接器修改庫代碼就可以在任何地址加載和執行的代碼。
  • Java有一個本地接口可以調用本地的C和C++函數。

異常控制流

  • 異常是異常控制流的一種形式,它一部分是由硬件實現的,一部分是由操作系統實現的。
    異常剖析
  • 異常可以分為四類:
    • 中斷(interrupt). --- 來自I/O設備的信號,是異步的。
    • 陷阱(trap). --- 有意的異常, 同步的。
    • 故障(fault). --- 潛在可恢復的錯誤, 是同步的。
    • 終止(abort). --- 不可恢復的錯誤, 是同步的。
  • 異常是允許操作系統提供進程(一個執行中程序的實例)的概念所需要的基本構造塊。
  • 進程提供應用程序的關鍵抽象:
    • 一個獨立的邏輯控制流,它提供一個假象,好像我們的程序獨占地使用處理器。
    • 一個私有的地址空間, 它提供一個假象, 好像我們的程序獨占地使用存儲器系統。
  • 一個程序為每個函數提供自己的私有空間,不能被其他進程寫。
    • 地址空間底部是保留給用戶程序的,包括通常的文本、數據、堆和棧。
    • 對32位Linux系統進程來說,代碼段從0x08048000開始;
    • 對64位Linux系統進程來說,代碼段從0x00400000開始。
    • 地址空間頂部保留給內核(內核執行指令時的代碼、數據和棧)。
      進程地址空間.
  • 限制應用可以執行的指令以及它可以訪問的地址空間范圍。
    • 控制寄存器中的一個位模式來提供這種功能。
      • 該寄存器描述了進程當前享有的特權。
      • 內核模式,超級用戶模式。
      • 用戶模式, 不允許用戶模式的進程直接引用地址空間中內核區的代碼和數據。 --- 必須通過系統調用。
        • 進程從用戶模式變為內核模式的唯一方法是通過諸如中斷、故障、陷入系統調用等異常。
        • proc/文件系統,將許多內核數據結構的內容輸出為一個用戶程序可以讀的文本文件的層次結構。
  • 上下文是指內核重新啟動一個被搶占的進程所需的狀態。
    • 通用目的寄存器,浮點寄存器,程序計數器,用戶棧,狀態棧和各種內核數據結構(頁表,進程表,文件表)。
      上下文切換.
  • 一個終止了但還未被回收的進程稱為僵死進程(zombie)。
    信號.
    • 一個待處理的信號最多只能被處理一次,發送類型為k的信號,內核會設置pending的第k位,接收則清除pending的第k位。
  • 進程組:
    • 每個進程都有一個進程組,進程組是由一個正整數進程組ID來標識的。
    • getgrp返回當前進程的進程組ID。
    • 一個進程默認和它的父進程同屬於一個進程組。
    • 進程組的作用
  • 非本地跳轉通過setjmp和longjmp函數來提供。
  • 操作進程的工具:
    • strace: 打印進程調用每個系統調用的軌跡。
    • ps: 列出當前系統中的進程(包括僵死進程)。
    • top: 打印關於當前進程資源使用的信息。
    • pmap: 顯示進程的存儲器映射。
  • 中斷會污染高速緩存。

虛擬存儲器

  • 一個系統中海的進程是與其他進程共享CPU和主存資源的。
  • 虛擬存儲器(VM)是硬件異常,硬件地址翻譯、主存、磁盤文件和內核軟件的完美交互,為每個進程提供了一致的私有地址空間。
  • 虛擬存儲器提供三個重要的能力:
    • 它將主存看成是一個存儲在磁盤上的地址空間的高速緩存,在主存中只保存活動區域,根據需要在磁盤和主存之間來回傳送數據。
    • 為每個進程提供了一致的地址空間,從而簡化了存儲器管理。
    • 保護每個進程的地址空間不被其它進程破壞。
      虛擬尋址.
      • CPU通過生成一個虛擬地址(virtual address, VA)來訪問主存。
      • 把虛擬地址轉換為物理地址的任務叫做地址翻譯(address translation).
      • CPU芯片上叫做存儲器管理單元(Memory Management Unit, MMU)的專用硬件, 利用存放在主存中的查詢表來動態翻譯虛擬地址, 該表由操作系統管理。
  • 地址空間(address space)是一個非負整數地址的有序集合。
  • 主存中的每個字節都有一個選自虛擬地址空間的虛擬地址和一個選自物理地址空間的物理地址。
  • 虛擬存儲器(VM)被組織為一個由存放在磁盤上的N個連續的字節大小的單元組成的數組。
    • 每字節都有一個唯一的虛擬地址,這個唯一的虛擬地址是作為到數組的索引。
    • 磁盤上數組的內容被緩存在主存中。
    • VM系統通過將虛擬存儲器分割為大小固定的虛擬頁(Virtual Page, VP). 物理存儲器分割為物理頁(Physiscal Page, PP).
      • 任何虛擬頁都可以放置在任何物理頁中。
  • 存放在物理存儲器中的頁表(page table)數據結構,將虛擬也映射到物理頁。
    缺頁
  • 許多虛擬頁面可以映射到統一個共享物理頁面上。
    vm進程地址空間
  • 帶許可位的頁表:
    頁面級的存儲器保護.
  • 使用頁表的地址翻譯:
    頁表的地址翻譯.
  • 翻譯后備緩沖器(Translation Lookaside Buffer, TLB)是一個小的、虛擬尋址的緩存,其中每一行都保存着一個由單個PTE(頁表項)組成的塊。
    二級頁表結構
    k級頁表結構.
    i7存儲器系統.
    linux組織虛擬存儲器結構.
  • 內部碎片的數量只取決於以前強求的模式和分配器的實現方式。
    • 內部碎片實在一個已分配塊比有效載荷大時發生的。

系統級I/O

  • linux文件:
    打開文件的內核數據結構.
    文件共享.
    子進程繼承父進程的打開文件.
    • I/O重定向: 允許用戶將磁盤文件和標准輸入輸出聯系起來。

網絡編程

  • 因特網.
  • 套接字接口.
  • Web服務器,Web客戶端和服務器之間的交互用的是一個基於文本的應用級協議,叫做HTTP(Hypertext Transfer Protocol, 超文本傳輸協議)。
    • 磁盤文件稱為靜態內容。
    • 運行可執行文件產生的輸出稱為動態內容。
    • HTTP事務, 使用Unix的TELNET程序來和因特網上的任何Web服務器執行事務。
    • HTTP請求,一個請求行(request line), 后面跟隨零個或更多個請求報頭,再跟隨一個空的文本行來終止報頭列表。
    • HTTP支持許多不同的方法: 包括GET,POST,OPTIONS,HEAD,PUT,DELETE和TRACE。
      • GET方法指導服務器生成和返回URI(Uniform Resource Identifier, 統一資源標識符)標識的內容。
  • 基於線程化的並發服務器:
    線程並發.
  • 可重復性:
    • 有一類重要的線程安全函數,叫做可重入函數(reentrant function), 當被多個線程調用時,不會引用任何共享數據。
    • 可重入函數不需要同步操作。
    • 如果所有的函數參數都是值傳遞,並且所有的數據引用都是本地的自動棧變量(沒有引用靜態或全局變量),那么這個函數就是顯式可重入的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM