l

2010年11月10日 星期三

程式卡卡

Nov. 09 23:19~ Nov. 10 00:06

Teddy 9月20日才說要發憤圖強,結果只持續一個月,從10月21日到現在 Teddy 又偷懶快三個禮拜沒寫部落格了。其實也不是 Teddy 故意要偷懶,應該算是天不從人願,每年年底好像是 Teddy『帶賽』的季節,兩個多禮拜前 Teddy 家裡的 Asus MK241H 螢幕壞掉了,電源完全不亮,前幾天才送回來。數一數 Teddy 今年下半年壞掉的設備:

  • Kyocera FS-1030D 印表機一台
  • 奇美 42吋 TL-42W6000D 液晶電視一部
  • WD 1.5 TB 硬碟一台
  • EN7300GT PCI-E 8X  顯示卡一片
  • Asus MK241H 螢幕一部
好懷念以前那種一台電視機可以看 15-20 年都不會壞的時光。

***

言歸正傳,今天談一個技術問題,這是最近 Teddy 在工作上遭遇到的『靈異現象』。話說因故 Teddy 和同事臨時被要求要寫一隻程式,透過 SNMP (Simple Network Management Protocol ) 去查詢遠端電腦的若干資料。在程式中我們使用了 snmp4j 這個 open source library 。我們的程式在 Windows 上面跑得很正常,速度很快,但是當執行在 Linux 上面時,速度卻異常的慢,感覺在 Linux 上面程式好像等待某個事件,等了很久(疑似等到 timeout)才繼續執行,有點卡卡的感覺(Teddy 內心獨白:女神卡卡,讚;程式卡卡,爛)。

同事甲:Java 不是跨平台嗎?

Teddy:你新人啊!


***

既然 SNMP 有用到『網路』,我們一開始『合理懷疑』是不是有甚麼 routing 的問題,或是在 Linux 上面要去得到 host name 之類的資料但是無法取得(以前有類似的經驗),但是經過一番測試之後發現並不是網路的問題。不過還好,既然 snmp4j 是 open source 的軟體,那就把 source 拿出來 trace 一下應該可以看出一些端倪。

在 snmp4j 中,如果要用 SNMP 傳送資料,必須先產生一個 SNMP 物件。在看了 snmp4j 的 source code 之後, 發現產生一個 SNMP 物件的時候,會:

new snmp () ->  initMessageDispatcher() -> SecurityProtocols.getInstance().addDefaultProtocols()

在這個 addDefaultProtocols() method 中,預設會產生下面這六種編碼方式,

        addAuthenticationProtocol(new AuthMD5());
        addAuthenticationProtocol(new AuthSHA());
       
        /// 下面這幾行是嫌疑犯
        addPrivacyProtocol(new PrivDES());
        addPrivacyProtocol(new PrivAES128());
        addPrivacyProtocol(new PrivAES192());
        addPrivacyProtocol(new PrivAES256());

其中屬於 PrivDES 系列的物件,會產生一個 Salt 物件 singleton, Salt 的 constructor 會執行下列程式:

      SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");    
      sr.nextBytes(rnd);  // 卡在這裡

SecureRandom 是 Java 內建的物件,這一行 sr.nextBytes(rnd) 會呼叫 native code。在 Linux 上面會去讀取 /dev/urandom 這個檔案,以得到一個 byte array。但是 /dev/urandom 檔案的內容被讀走之後,會變空的(或是變少),要再等一段時間才會增加。這裡有一篇文章對於 SecureRandom 的問題有詳細的說明,可以參考。http://lists.agentpp.org/pipermail/snmp4j/2005-January/000273.html

***

那... 要如何解?答案就在... google 中...參考這一篇提到的 work around (http://bugs.sun.com/view_bug.do?bug_id=6521844),在啟動 JVM 時設定 -Djava.security.egd=file:/dev/./urandom  這個參數就 OK 了。或是到 jre/lib/security 目錄修改 java.security 這個檔案,把 securerandom.source=file:/dev/urandom 改成 securerandom.source=file:/dev/./urandom 也行。

雖然問題暫時解決了,但是 Teddy 還是不知道 /dev/urandom 和 /dev/./urandom 有什麼不一樣?算了,先混過去再說,又不是要出 paper...XD

 ***

友藏內心獨白:有 Google 固然好,下對 keywords 才是真本事。

4 則留言:

  1. 下次別買主機板廠商做的螢幕啦,直上三菱電機H-IPS螢幕才讚啊

    回覆刪除
  2. 看了一下你的link應該是把/dev/random 換成/dev/urandom吧?
    /dev/random 是收集硬體上的資訊產生的亂數,硬體資訊會block住
    /dev/urandom 是用算出來的假亂數

    /dev/./urandom
    跟/dev/urandom 是一模一樣的東西,除非java實作在其他部份會偷偷判斷path 然後把 /dev/random 打開...

    回覆刪除
  3. To Chih-Min:

    其實 JVM 預設已經是去讀 /dev/urandom 了,還是會卡住,但是改成 /dev/./urandom 就 OK。這就是 Teddy 不了的地方啊。/dev/urandom 和 /dev./urandom 不是同一個檔案嗎?怎麼會這樣。不過暫時能動就好,不想去 trace JDK source code...XD

    回覆刪除
  4. To Lililala2:

    如果口袋夠深,我也想買 H-IPS 面板啊,最好還能夠旋轉,看電子書或網頁比較方便。

    回覆刪除