原文地址:http://www.repairfaq.org/filipg/LINK/F_crc_v31.html 1. 前言 1.1 關於著作權和作者
“Everything you wanted to know about CRC algorithms, but were afraid to ask for fear that errors in your understanding might be detected.”
你想要知道所有關於CRC演算法的知識,但又害怕去問一些你不理解的東西,因為它可能是錯誤的。 作者:Ross N. Williams 電子郵箱:ross@guest.adelaide.edu.au 日期:19 August 1993 版本:3.00 FTP地址:ftp.adelaide.edu.au/pub/rocksoft/crc_v3.txt 網址:http://www.on.net/clients/rocksoft/rocksoft/ 公司:Rocksoft(tm) Pty Ltd 地址:16 Lerwick Avenue, Hazelwood Park 5066, Australia 傳真:+61 8 373-4911 (c/- Internode Systems Pty Ltd) 電話:+61 8 379-9217 (10am to 10pm Adelaide Australia time) 備忘:”Rocksoft” is a trademark of Rocksoft Pty Ltd, Australia 狀態:Copyright (C) Ross Williams, 1993,1994,1995,1996. However, permission is granted to make and distribute verbatim copies of this document provided that this information block and copyright notice is included. Also, the C code modules included in this document are fully PUBLIC DOMAIN (PD). 致謝:Thanks to Jean-loup Gailly (jloup@chorus.fr) and Mark Adler (me@quest.jpl.nasa.gov) who both proof read this document and picked out lots of nits as well as some big fat bugs.
本文檔中用到的 C 原始碼: crcmodel.h (9KB) crcmodel.c (6KB) crctable.c (8KB) 1.2 摘要
這個文檔描述了 CRCs(Cyclic Redundancy Codes,迴圈冗餘碼)以及詳細說明了基於 table-driven 的實現方法。實際上有很多關於 CRCs 的文獻,尤其是基於 table-driven 來實現的,但是實在讓人晦澀難懂(至少對於我來說是)。所以本文檔試圖簡單明了(不代表不嚴謹)地解釋 CRCs,並且會把實現方法的每個細節都記錄下來。除此之外,本文檔給出一個參數化模型的CRC演算法—— Rocksoft^tm Model CRC Algorithm。這個模型演算法通過參數化的方法能夠表示大部分的 CRC,因此,如果想瞭解特定的演算法,不妨先參考本文檔中的方法。上面的 C 原始碼中提供了一個低速的 CRC 演算法模型的實現,最後一節給出了實現高速 table-driven 的2種形式,並提供了一個產生 CRC 尋找表程式。 2. 介紹:錯誤偵測
錯誤偵測技術的目標是為了能夠讓接收者知道接收到的資訊是否被破壞了,這是因為傳輸通道存在雜訊幹擾,可能會對所傳輸的資訊引入錯誤。一般的做法是,寄件者調用一個函數對即將傳輸的資訊產生一個被稱為“校正和(checksum)”的值,並把這個值附加到該資訊上。而接收者在接收到資訊時,會調用相同的函數來計算該資訊的校正和,並與該資訊附帶的校正和進行比較,以此來確認該資訊是否正確。例如,我們簡單地對傳輸資訊中各位元組求和,接著用該和 mod 256(即以256為模),它們可能會像下面這樣:(所有數字都是十進位的)
Message : 6 23 4 Message with checksum : 6 23 4 33 Message after transmission : 6 27 4 33
如上,資訊的第2個位元組在傳輸過程中受到了破壞(由23變成27)。不過,接收者能夠對計算所得的校正和(37)與資訊附帶的校正和(33)進行比較。但是如果校正和本身在傳輸過程中受到破壞,那麼正確的資訊就可能會被判斷為錯誤的。確實,這是一個安全方面的缺陷。當傳輸資訊的 and/or 校正和發生了破壞,而所傳輸的資訊是一致的,此時就會導致這樣一個危險的錯誤。很不幸,產生這種錯誤的可能性是完全不可避免的,降低這種可能性最好的辦法就是增加校正資訊量(例如:將校正和由1個位元組拓寬到2個位元組)。
其他錯誤偵測技術,涉及對資訊進行複雜的轉換,並將冗餘資訊注入。然而,本文檔只對 CRC 演算法進行講解,通過錯誤偵測處理後,傳輸的資訊由原始的資訊資料以及附加在其後的校正和組成。如:
<original intact message> <checksum>即:< 原 始 信 息 > < 校 驗 和 >
3. 複雜性的需要
在上一節的校正和例子中,我們通過一種簡單地將各位元組直接相加,然後對256模數的校正和演算法來檢測傳輸資訊是否有被破壞。
Message : 6 23 4 Message with checksum : 6 23 4 33 Message after transmission : 6 27 4 33
這個演算法的問題在於——實在太簡單了。如果同時有很多資料被隨機破壞,但這個錯誤的資訊卻有 1/256 的機率會被認為是正確的。如:
Message : 6 23 4 Message with checksum : 6 23 4 33 Message after transmission : 8 20 5 33
為了加強校正效果,我們嘗試由8位寄存器改為16位寄存器(也就是模數用65536代替原來的256),顯然此時判斷失誤的機率由 1/256 降低到 1/65536。這基本上是一個好主意,但現在問題在於所用的公式不夠“隨機”—— 這個簡單的求和公式,無論求和寄存器有多寬,每一個移入的位元組都只能會影響到它的一個位元組。例如,上面的第二個例子中,即使求和寄存器擁有MB層級的寬度,這個錯誤仍然不被檢測到。解決這個問題的辦法是,用更複雜的公式來代替簡單求和公式,而這個複雜的公式應該能夠讓每一個移入的位元組對整個求和寄存器造成影響。
因此,我們看到,一個健壯的校正和演算法至少對以下兩方面有要求:
寬度
一個寄存器的寬度能夠把這種判斷出錯機率降到足夠低的一個機率(假如是32位的寄存器,則機率為 1/2^32)。
複雜性
需要一個公式,讓每一個移入的位元組有能力去改變任意位元的寄存器,從而產生足夠的混亂性、隨機性。
注意:“校正和”這個術語據推測是用於描述早期的求和公式的,但它現在已經有更廣泛的含義,包括像 CRC 這樣複雜的演算法所產生的值。CRC 演算法能夠很好地滿足複雜性的要求,並且能夠適應不同的校正和寬度。 4. CRC演算法背後的基本思想
我們在哪裡可以找到比求和公式更複雜、更合適的公式呢。自然會想到各種各樣的設計,我們可以使用的圓周率或者雜湊,讓每個移入的位元組與寄存器中的所有位元組之間構成數字表。甚至可以在網上儲存一個大的電話簿,並讓每一個移入的位元組通過索引一個新的電話號碼來作為下一個寄存器的值。因此,處理的方法是無限的。
然而,我們並不需要走這麼遠,接下來介紹的演算法足夠滿足要求。加法顯然不足以形成一個有效校正,而除法卻可以(只要除數的寬度和校正和寄存器寬度一致)。
CRC演算法的基本思想是:把資訊簡單地當作一個巨大的位元,然後用另一個固定的位元字去除,並從中得到餘數。當接收到資訊時,接收者用相同的除數去執行除法運算,然後用得到的餘數與“校正和”進行比較(此時,這裡的“校正和”實際上是餘數)。
例子:假設訊息由2個位元組組成(6,23),跟上面的例子一樣。用十六進位數表示為 0x0617,二進位表示為 0000-0110-0001-0111。假設校正和寄存器的寬度是1個位元組,恒定因子 1001 作為除數,那麼,校正和就等於 0000-0110-0001-0111 除以 1001 後所得的餘數。這樣的話,此方法顯然也適用於32位的校正和寄存器,並且所得的校正和是混亂的。所以,我們將用到曆史悠久、非常好用的長除法(你在學校學過的,記得嗎。),只不過這一次,它用在二進位:
...0000010101101 = 00AD = 173 = QUOTIENT ____-___-___-___-9= 1001 ) 0000011000010111 = 0617 = 1559 = DIVIDENDDIVISOR 0000.,,....,.,,, ----.,,....,.,,, 0000,,....,.,,, 0000,,....,.,,, ----,,....,.,,, 0001,....,.,,, 0000,....,.,,, ----,....,.,,, 0011....,.,,, 0000....,.,,, ----....,.,,, 0110...,.,,, 0000...,.,,, ----...,.,,, 1100..,.,,, 1001..,.,,, ====..,.,,, 0110.,.,,, 0000.,.,,, ----.,.,,, 1100,.,,, 1001,.,,, ====,.,,, 0111.,,, 0000.,,, ----.,,, 1110,,, 1001,,, ====,,, 1011,, 1001,, ====,, 0101, 0000, ---- 1011 1001 ==== 0010 = 02 = 2 = REMAINDER
十進位中,1559 除以 9 得到商為 173,餘數為 2。
雖然並不是輸入資訊的每一位都對商的造成影響,但是4位的餘數在計算過程中受到相當多的影響,如果輸入資訊(被除數)有更多的位元組,那麼它的值所受到的影響會更多。這就是為什麼我們使用除法而不是加法來計算校正和的原因。
如你所想,使用4位校正和,傳輸資訊會變成 0x06172(0617是原始資訊,2是校正和)。然後接收者將用 0x0617 除以 0x9,判斷所得餘數是否為 0x2。 5. 多項式演算法
上一節介紹的長除法校正方法與被稱為 CRC 檢驗的方法非常類似,實際上,CRC 校正方法顯得有些古怪,我們需要深入研究一些奇怪的數字系統來瞭解它們。
在處理 CRC 演算法時,你會聽到一個詞——多項式。一個給定的 CRC 演算法將被稱為使用一個特定的多項式,而 CRC 演算法通常使用多項式運算來操作。這是什麼意思呢。
不像上一節一樣,把除數、被除數(資訊)、商、餘數被視為正整數,而是將它們視為多項式的二進位係數。把每個數展開為一個位串,其位是一個多項式的係數。例如,普通數字23(十進位)的十六進位為 0x17,二進位為 0b10111,因此它對應的多項式為:
1*x^4 + 0*x^3 + 1*x^2 + 1*x^1 + 1*x^0
或者,更簡單地表示為:
x^4 + x^2 + x^1 + x^0
使用這樣的技巧,被除數(資訊)和除數就可以用多項式來表示了,除了現在多了一些 x 外,我們完全可以像以前一樣,使用所有的演算法。例如,假設我們用 0b1101 乘以 0b1011,利用多項式我們可以這樣做:
(x^3 + x^2 + x^0)(x^3 + x^1 + x^0)= (x^6 + x^4 + x^3 + x^5 + x^3 + x^2 + x^3 + x^1 + x^0) = x^6 + x^5 + x^4 + 3*x^3 + x^2 + x^1 + x^0
此時,為了得到正確的答案,我們需要做一些處理,因為二進位逢二進一,所以 3*x^3 項需要進位,得到:
x^7 + x^3 + x^2 + x^1 + x^0
是的,多項式運算就像普通的算術運算,只不過是有點抽象,並把所有的項都明確納入計算中。那麼,現在問題出在哪呢。
問題是,假如我們不知道 x 是多少,我們就無法進行下去。我們不知道 3*x^3 等於 x^4 + x^3,因為我們不知道 x 是2。在多項式運算中,所有係數之間的關係是未知的,因此,每個 x 次冪的係數實際上都是強型別,也就是說 x^2 的係數和 x^3 的係數是兩種完全不同的類型。
由於每個 x 次冪的係數的相互隔離,數學家們想出了各種各樣的多項式演算法,簡單地改變多項式係數的運算規則。由此設計出一種被稱為“多項式模2運算”的運算規則,要求多項式所有的係數必須是0或1,並且多位模2除法採用模2減法(不帶借位的二進位減法)。回到前面的例子:
(x^3 + x^2 + x^0)(x^3 + x^1 + x^0)= (x^6 + x^4 + x^3 + x^5 + x^3 + x^2 + x^3 + x^1 + x^0)= x^6 + x^5 + x^4 + 3*x^3 + x^2 + x^1 + x^0
我們嘗試用另外一種運算規則,按之前的運算規則 3*x^3 這個項會產生進位(因為我們知道 x=2)。現在我們用模2運演算法則,假設我們不知道 x 是多少,並且不帶進位,對所有多項式係數進行模2運算,那麼,會得到如下結果:
= x^6 + x^5 + x^4 + x^3 + x^2 + x^1 + x^0
正如 Knuth [Knuth81] 所說 (p.400):
“讀者應該注意多項式演算法和多精度算術(第4.3.1)之間的相似性,其中基數 b 取代了 x。它們的主要區別是,多項式 x^k 的係數 u_k 與相鄰的係數(x^{k-1} 和 x^{k+1})沒有關係,所以這裡不存在進位或借位的概念。事實上,模數為 b 的多項式運算與基數為 b 的多精度算術運算本質上是一樣的,只是後者存在進位或借位操作。”
所以,多項式模2運算就是不帶進位或借位的、以2為模數的二進位算術運算。在研究和分析 CRC 或其他錯誤校正演算法時,多項式是非常有用的數學工具。為了闡述它們,我們沒有過多的擴充和囉嗦,也沒有對餘數進行過多的講解,更多是通過例子直接運用相關算術。那麼,請記住,多項式模2運演算法則的特點 —— 不帶進位或借位的二進位運算。