PC客戶端軟體升級方式簡史

吳尼瑪發表於2017-12-19
  • 在windows8之前,微軟的Windows平臺一直沒有提供一個想蘋果的AppStore或者Linux的包管理這樣的統一軟體管理工具。所以Windows下的軟體安裝、升級、解除安裝的事情一般都是軟體自己去負責。這樣導致Windows下的軟體安裝、升級、解除安裝的方式五花八門,但總體上來說方法都大同小異。安裝程式主要分兩種,下載器的安裝包和離線安裝包,這個不贅述。
  • 今天重點聊一下升級,升級功能看似簡單,但對於一個想持續經營的客戶端軟體來說卻是一個重要的生命線。開發團隊辛苦修改的bug、做的新功能都希望使用者能馬上通過升級新版本體驗到。
  • 在網際網路還沒普及的蠻荒年代,很多軟體公司升級都是釋出離線升級包,一般這種包就是一個安裝程式,它只負責安裝程式需要更新的部分,然後做一些修改登錄檔之類的系統配置以適應新版本的功能。
  • 現在網際網路普及後,所有的PC客戶端軟體基本上都是使用的線上升級。
    • 最簡單的線上升級方式是首先客戶端傳送檢測更新的訊息到伺服器,伺服器給返回是否有新版本,最新版本號以及下載地址等資訊,客戶端就根據這些資訊處理。如果有更新就去剛剛獲取到的地址下載最新的安裝程式,然後執行安裝程式更新。
    • 後來大家覺得每次都重新安裝太麻煩,而且安裝包也特別大,下載也非常耗時。於是這個下載的程式被替換成了一個壓縮包,裡面裝的是程式需要更新的檔案。升級程式下載好壓縮包後再解壓到安裝目錄中就完成了軟體的升級。
  • 隨著敏捷開發方式的普及,軟體的升級就變得越來越頻繁了。對於PC客戶端軟體每次升級時主程式以及一些重要的動態庫都有可能更新,所以下載的壓縮包也會比較大。於是就產生了比較檔案二進位制差異的演算法BsDiff,以及Google基於其進一步改進的Courgette(小胡瓜)。這些演算法的加入可以讓補丁包縮小了n個數量級。這樣需要客戶端去下載的壓縮包就會很小了,下載耗時也會大大縮短。
  • 另外值得一提的是,隨著軟體升級包大小越來越大,使用者下載更新檔案的等待時間也越來越長,於是有些軟體就採用了後臺靜默下載的方式。這種方式雖然流氓,但可能對於使用者來說體驗要好一些。那麼這種情況下主程式一般都還在執行,而升級程式下載完成後想要更新檔案立即升級就必須關閉主程式然後進行檔案替換,以免檔案被佔用,導致升級失敗。於是Google的chorme搞出一個雙目錄更新的方法來應對這種情況。所謂雙目錄更新就是把原來的檔案先複製到另一個目錄下,更新程式的時候就更新這個檔案目錄,升級完成後就直接從新的目錄中啟動新版本。
  • chrome的目錄結構是這樣的:
Chorme
    +Application
        +57.0.2987.110
        +57.0.2987.88
            chrome.exe
複製程式碼
  • 可以看到,他是以版本號做目錄名。以後啟動chrome.exe時去載入最新版本就可以了。當然它能這樣做主要因為chrome.exe本身是個很小的程式,基本它自身是不需要升級的,它主要負責的就是檢測版本號然後載入新版本的dll。
  • 當然現在的客戶端升級程式還涉及一些入灰度,md5完整性檢測斷點續傳等技術這裡也不在贅述。下面我簡單介紹一下BsDiff和Courgette。

BsDiff: Linux中的一個開源工具,致力於快速和輕量的更新Linux的作業系統漏洞(跟微軟的安全補丁類似),其演算法的核心思想是基於統計學規律進行近似匹配,然後通過一系列的變化(比如BWT變換)提高“近似段”的壓縮率。 Courgette: Google Chrome升級系統的核心模組,基於BsDiff,但對其進行了一系列的改進,將平臺相關的資訊(即x86彙編指令)融入其中,以期望更精確的定位指標,從而避免統計演算法在差異明顯時候的錯誤率。

  • Google官方給了一個10M的升級包例子使用bsdiff可以看到包小了不少,用Courgette更是少了幾個數量級。

PC客戶端軟體升級方式簡史

  • 使用bsdiff演算法我們的升級過程是這樣的:
server:
    diff = bsdiff(original, update)
    transmit diff

client:
    receive diff
    update = bspatch(original, diff)
複製程式碼
  • 大致流程就是這使用bsdiff演算法比較不同版本的二進位制檔案製作補丁包,客戶端下載補丁包後呼叫bspatch生成新的二進位制檔案。

  • 使用Courgette的升級過程是這樣的:

server:
    asm_old = disassemble(original)
    asm_new = disassemble(update)
    asm_new_adjusted = adjust(asm_new, asm_old)
    asm_diff = bsdiff(asm_old, asm_new_adjusted)
    transmit asm_diff

client:
    receive asm_diff
    asm_old = disassemble(original)
    asm_new_adjusted = bspatch(asm_old, asm_diff)
    update = assemble(asm_new_adjusted)
複製程式碼
  • Courgette對於bsdiff的優化主要就是在adjust這一步上,具體可以參考Courgette官方說明

  • 最後,Google還開源了一套Windows下的升級協議,大家有興趣也可以研究下omaha

相關文章