Checksum

版本 5 Unix 使用什麼校驗和算法?

  • July 2, 2019

我不知道在 Unix V5 和 V6 中該sum命令使用了什麼算法。

起初,我認為它是字節模數 2^16 的簡單總和。然而,對於重複 320 次的字元串“1111111111\n”,它計算的校驗和為28930(使用Julius Schmidt 的用於 JavaScript的 PDP-11 模擬器)。而它的簡單字節總和要小兩個字節:

$ python -c 'print(sum(bytearray(b"1111111111\n"*320)) & 0xFFFF)'
28928

後來,從MacOS 的手冊頁中,我發現sumandcksum命令有很長的不一致歷史。然而,即使是 MacOS 上提供的“歷史”算法版本也不同意 Unix V5 的校驗和。最接近的匹配是 UNIX System V 的預設sum命令(在 Mac 上呼叫,如cksum -o 2),它為此字元串返回相同的校驗和,但不同意其他命令:

$ python -c 'print("1111111111\n"*320, end="")' | cksum -o 2
28930 7

更具體地說,cksum -o 2和 Unix V5sum對模擬器中的大多數二進製文件(例如,在文件夾中/bin)產生不同的輸出,儘管它們在大多數文本文件上是一致的。

這是模擬器中的真實行為還是錯誤?如果是正版,是什麼算法?

PS這是原始碼,如果有人可以閱讀1974年的彙編程式碼。

起初,我以為它是字節模數 2^16 的簡單總和

它是一個 sum mod 2^16,只是每次溢出時都會加一個 1。此外,字節將在添加到總和之前進行符號擴展。這是程序集中的“註釋”片段:

# r2 is the pointer into the data
# r0 is the length of the data
# r5 is the sum
2:
       movb    (r2)+,r4    # r4 = sign_extend(*r2++)
       add     r4,r5       # r5 += r4
       adc     r5          # if(r5 overflowed) r5++
       sob     r0,2b       # if(--r0) goto 2 above

同樣放入一個小的 C 程序(用作./v5sum < file):

#include <stdio.h>
int main(void){
       int c, s = 0;
       while((c = getchar()) != EOF){
               s += c & 0x80 ? c | 0xff00 : c; // alternative: s += (unsigned short)(signed char)c
               if(s & 0x10000){ s++; s &= 0xffff; };
       }
       printf("%d\n", s);
       return 0;
}

更具體地說, cksum -o 2 和 Unix V5 的 sum 為模擬器中的大多數二進製文件(例如,在文件夾 /bin 中)產生不同的輸出,儘管它們在大多數文本文件上是一致的。

那是因為原始的 unix v5sum將對字元進行符號擴展,並且只有二進製文件包含 >= 0x80 的字節。否則,算法應該相似,並且僅與非常大的文件不同(其中字元的總和將溢出 32 位無符號整數)。

引用自:https://unix.stackexchange.com/questions/526611