發表文章

目前顯示的是 2006的文章

G3D - 為什麼在Linux 3D運算效能差?

圖片
還記得近十年前,166、200、233MHz 的 CPU 就已經可以將 3D 遊戲跑的有形有色,當初令我一度沉迷在摩托雷神、極速快感的遊戲當中。可是直到今天CPU速度都破1G、2GHz以上了,在Linux 中 的 3D 遊戲,那怕是幾顆小球滾來滾去的撞球遊戲,都還是慢的可以。一向以技術掛帥不斷改進的Linux,卻在這方面還比不上十年前的 Windows 95?沒錯,其實是因為 3D 卡的驅動程式沒裝,所以3D遊戲才會這麼慢。可是,十年前那時候我好像也沒裝3D加速卡。那到底是為什麼,Linux在 3D 顯示運算這麼的落後?事實上,在 Linux 裡顯示 3D,一向走的是 OpenGL,OpenGL是一個3D的工業標準,遵循它可以良好的利用 3D 運算晶片,但OpenGL也是一個極大的包袱,如果在沒有加速卡的情況下運算,速度肯定令人吐一大桶的血。為 了解決這問題,Windows 早期就開發了 Direct 3D,用來整合3D運算,在有硬體加速的環境,Direct 3D可以良好的使用硬體加速,而在沒有硬體加速的情況下,它自有一套軟體運算方法。所以,在早期的舊電腦中,Windows上的 3D 遊戲都可以在沒有加速 卡的情況下,跑的還算順暢。Linux中可就沒有這種設計,不管硬體是否有支援,全都使用OpenGL硬著頭皮跑下去,畫面當然是卡的嚴重。所以就有人為此設計了一個功能類似Windows上Direct 3D的函式庫,它就是 G3D (http://g3d-cpp.sourceforge.net/)。

如圖所示,G3D是介於3D應用程式與硬體之間溝通的橋樑,它可以依狀況選擇使用CPU運算或是走OpenGL交由圖形晶片運算。不過目前還沒看到什麼遊戲或3D應用程式使用G3D,看來在Linux上的3D環境還是需要3D晶片的強力支援了。後記不過到了今天,幾乎每一台電腦最少都內建 3D 晶片的情況下,是不是要用 G3D 為 CPU 重新開僻一條運算道路,有待評估空間的很大。但是,如果考慮到部分廠商沒公開驅動程式原始碼,且限制驅動程式的使用和散播方式之下,對於在 Linux 上安裝驅動程式這件事還是只有專業玩家能勝任,各 Linux 發行商無法做太大動作的整合。在這種情況下,預設要能跑出正常順暢的 3D 運算,還是要嘗試使用 G3D 看看。

取代Unionfs的好東西 - Aufs

Aufs 的全意是 Another Unionfs,它是為了取代Unionfs而產生的檔案系統。有鑑於Unionfs的極度不穩定,且許久不見改進,作者Junjiro Okajima便建立新的專案取代Unionfs,現在已被Slackware的LiveCD所採用。LiveCD的資料保存和模組化,是 各家開發者急欲解決的問題。自從Unionfs出現之後,提供了一個良好的解決方案,可惜的是,Unionfs一直未能穩定執行,常常導致Kernel Crash。各LiveCD的開發者們,因此紛紛停用或是選擇性的使用Unionfs。一切又回到原點,LiveCD依然無法模組化或是自由的保存資料。Aufs的出現讓這瓶頸又出現解決的曙光,它良好的穩定性,去除了Unionfs的致命缺點,非常令人期待!以下是Aufs的原文簡介:Aufs was entirely re-designed and re-implemented Unionfs. After
many original ideas, approaches, improvements and implementations, it
becomes totally different from Unionfs while keeping the basic features.
Unionfs is being developed by Professor Erez Zadok at Stony Brook
University and his team.

非mmcache!Memcached的應用:多網站伺服器 PHP 共享 Session

請注意是 Memcached 不是 mmcache,很多人搞不清楚他們兩個的不同!多半玩過 PHP 的人大概都聽過 mmcache,它是一個預編譯緩衝的 PHP 加速程式,能夠提升 PHP 的執行效能。但很少人聽過 Memcached ,因為大多人乍看之下都以為它是mmcache,使得它沒什麼機會介紹自己。事實上,若您正打算架構一個真正高負載的大型網站系統,你需要了解的並不是 mmcache,而是 memcached。

Memcached 是什麼?顧名思義,他是由記憶體(Memory)和暫存(cache)所組合起來的常駐程式(Daemon),你也可以稱它為『暫存伺服器』。 Memcached 能提供一個暫存資料的服務,透過網路供其他電腦使用。Memcached 有什麼用途?最常見的應用就是在網站伺服器的叢集,它能讓許多的網站伺服器 Session 互相流通使用。如果你正在傷透腦筋煩惱這一點,恭喜你找到解決方法了!

想要在網站伺服器的叢集中,多網站伺服器 Session 互相流通使用,首先你必須將 Memcached 架起來當 Session 分享伺服器,這邊建議你使用大的記憶體,最好是能多大就有多大,因為 Memcached 並不會以硬碟當資料暫存,而是會完全跑在記憶體上,所以若記憶被用完了,Memcached 就會無法再存放更多資料。

接著,你必須修改 PHP 的 Session Save Handler,讓 PHP 懂得利用 Memcached Server 存放 PHP 的 Session 資料並能從 Memcached Server 取出 Session 的資料。PHP提供了 session_set_save_handler() 函式讓我們能輕易修改 Session Save Handler ,以下是我修改後的 PHP 程式碼,你必須在呼叫 session_start() 之前使用:


<?

define("SHARED_SESS_TIME", 3600); // Timeout

// Session Class by Fred

class Shared_Session
{
function init()
{
ini_set("session.use_trans_sid", 0);
ini…

對於高負載資料庫的突發奇想

一直以來,我所開發的程式、系統,都是往大格局方向去規劃,但是卻往往高估了電腦硬體的能力。縱使程式被設計能夠支 持大架構的使用,電腦硬體卻總是不給面子。尤其資料庫的部份更是令人頭痛,大量的查詢、輸入、修改,都造成資料庫效能不彰。雖然 PostgreSQL 、 MySQL等資料庫系統,都號稱每秒能夠查詢幾百萬筆資料,但事實上確不是如此。可能有人會說我應該將資料表的設計做重新的規劃和分割,或是對系統做更多 的調校。但我敢問心無愧的說,大多數該做的我都做過了。 追根究底,任何機器有它能夠負荷的運算量,縱使我們做再多的最佳化,也不過是將其他用途的運算省略減少以增加我們需 要的運算效能。所以,一直以來都有人提議,將資料庫的新增修改等操作與查詢分開,或是將資料依價值時限分割移出原本的Table。雖都可以暫時解決效能問 題,但在功能操作上多了很多不便限制,也增加了系統設計上的複雜度。市面上常討論的資料庫叢集,不外乎是Master/Slave架構,他能做的用途只不過是資料庫的備援,能迅速彌補 Master資料庫的當機。就算是多Master的架構,也不過是將操作連線分散到各主機上。不論是哪一種,對每台主機而言,每次連線所要處理查詢的負擔 根本沒變,舉例來說,若Table裡有一千萬筆資料,每次連線所要處理的資料量就是一千萬筆。在此我有個想法,我們何不將這一千萬筆資料分散到各主機,查詢操作資料庫時,就可分散至各個主機運算,最後再合併各個主機傳回結果。如此,若我們有10台主機共同運算,每台機器只要負擔一百萬筆的資料,效能也因此可大幅提升。本著新想法至網路上查了一些資料,卻發現Oracle已有如此的設計,看來我不是第一個想到的人。手癢的我又去查了PostgrSQL和MySQL等開放源始碼資料庫的資料,驚訝的發現,居然沒有這種功能,也難怪Oracle始終在資料庫系統市場有一番地位。開放源始碼的社群一向以熱情和效率著稱,或許不久後的某一天,他們也會將這項功能放入他們的系統中。

使用 SquashFS 免受核心版本不支援之苦

SquashFS是一個即時解壓縮的檔案系統,如同Cloop、CramFS一般。只是,SquashFS的壓縮比更高、速度更快,又不像CramFS有單一檔案大小或整體檔案系統大小的限制,在LiveCD的應用上非常有用。但往往這種特殊的模組,並不像 Cramfs是內建於Linux Kernel之中。要使用他,我們必須去抓回最新的 Patch,對自己的Kernel做修改。

問題是,Patch 永遠跟不上 Kernel 發行的速度,如果我們要使用最新的核心,不見得Patch一定能夠跟上支援,導致在考量之下,有時必須退而求其次使用舊的核心。但是,如果將Patch做成模組,就可不必管Kernel的變化,無論Kernel多新,我們都還是可以使用 SquashFS。

這是我對原 Patch 的分解修改 :squashfs-fred.tar.gz

只要解開後直接 make 就可以製作出 .ko 的模組,接著只要將此模組複製到 /lib/modules//kernel/fs/squashfs/ 之中即可使用。修改成單純模組的缺點是,開機時的 initrd 不支援SquashFS,因為若要支援必須修改核心的 init/do_mounts_rd.c 並重新編譯 Kernel,所以若做成 patch 勢必又將與核心版本扯上關係。我做此修正的重點是要讓每一版的Linux Kernel都能很容易使用SquashFS,不需要因為 Kernel 太新而不能使用。有興趣的人可以試試看。

註:本修改只在 Kernel 2.6 版以上測試過。

hotplug 舊時代的產物?udev 才有未來!

什麼是 hotplug ﹝熱插拔﹞?傳統的 PCI 、 ISA ,都需要在電腦處於冷機的時候,才能插上裝置,因為這些規格都不具備 hotplug 的能力。hotplug 意味著不必讓電腦關機、切斷電源後就能插上你的裝置。我們常見的隨身碟就是最好的例子。

常常我們文件完後想要儲存帶走,就會拿起隨身碟插入 USB 連接孔,一般狀況下,系統馬上就能偵測到新的儲存裝置,並做新增配置,讓你可以立刻將資料存放於隨身碟裡。用完後,只要將隨身碟拔起,就可將資料帶走。這種在電腦熱機狀態就增減硬體的機制就是 hotplug。

在 Linux 裡,過去大家都是用 hotplug 套件包來達成動態增減裝置的功能,還必須設定 /proc/sys/kernel/hotplug ,讓核心在偵測到有新的 hotplug 裝置時,會通知 hotplug 套件包去做相關配置。

稍微有注意的人都知道,Linux 2.6 版核心後,全面採用 udev 取代過去的 devfs ,裝置檔現在都改用 udev 自動產生。原因是 udev 在設計理念上比過去的 devfs 擁有更大的格局,他不只是一個設備檔產生器,而是一個核心訊息的監聽器,它總在隨時監聽核心是否有新增裝置或是載入新的模組,並做出相對應的處理。在 udev 發展的過渡時期,由於對新裝置的對應處理機制並沒有設計的很完善,暫時只能充當設備檔產生器,而在 hotplug 機制的部份,設計了一個小程式 udevsend 與過去的 hotplug 套件包做整合。

udev 發展到今天,已逐漸成熟,hotplug 機制已不必過度仰賴舊的 hotplug 套件包,udevsend 這支程式已經在新版的 udev 中拿掉了,關於這部份可以在 Fedora Core 5 的系統中看到實作。

Static 與 Shared 的函式庫撰寫

簡單來說函式庫分為兩種類型: Static﹝靜態﹞和 Shared﹝共享﹞,前者你可以想像是嵌入式,將函式嵌入到主程式中;後者是主程式能以 Dynamic (動態)方式指定呼叫的外部函式庫。

最近將一些程式模組化,把許多函式分門別類拉到主程式外做成函式庫模組。這工程雖不算浩大,但也挺累人的。因為函式庫的撰寫很有實用價值,我就隨手將它記下來,還有將以前所看過的文件重新翻出來復習一番。有一篇 《Program Library HOWTO》詳細記載了撰寫函式庫的方法和過程,有興趣的人可以參考。

撰寫程式時,程式碼裡的任何工作,扣除程式語法不算,總是由許許多多函式所組成,可別以為這些函式是電腦內建的,CPU可沒有這麼厲害,可以內建這樣龐大的指令集。這些函式都是由前人不斷發展提供的,可能是作業系統研發者,也可能是某應用程式的設計者。

一些公認標準的共用函式庫,讓一般程式設計師可以方便的使用各種功能,例如,當你想要在螢幕上顯示一行文字,你只要呼叫如 printf() 這種函式,而不必再去處理 低階的 BIOS Interrupt,然後控制 CPU 上的 Register 來達成螢幕輸出的工作。共用函式庫最大的好處,就是能讓許多工作不必重覆撰寫,以省下不少時間,更棒的是,你可以使用各種程式語言來呼叫使用這些函式。

文章一開始就提到函式庫分兩種類型:Static Library、Shared Library。

關於 Static Library ,應該大多程式設計者都很熟悉,就是將自定的函式寫再不同的 .c 檔中,在編譯時,透過編譯器去連結各個 .c 產生出來的二進位 .o 檔,將所有的函式都寫入同一執行檔中,使執行檔主程式能夠使用這些外部函式。

Shared Library 可就沒有這麼中央集權,我們可以將函式庫做成一個個的 .so 檔,你可以將它放入系統函式庫資料夾中,讓所有程式都能呼叫他。當你想要將自定的函式編譯成 Shared Library 時,可用以下指令:

摘錄於《Program Library - More Example

# 將自定函式的 .c 檔案編譯成 .o
gcc -fPIC -Wall -g -c libhello.c

# 將 .o 連結成 .so 檔
gcc -g -shared -Wl,-soname,libhello.so.0 -o libhello.so.0.0 l…

建立良好的寫程式風格

大概最近許多人都在趕作業或是要考試吧,有不少人拿著一些他們的心血跑來問我問題。然而,我看到差點沒吐血,我只能說,那真的是一堆人類讀不懂的程式碼。或許我講得太誇張了,但是這卻是不爭的事實。由於許多人良好的 Coding 習慣尚未建立,所以他們所寫出來的東西,真得是『醜』的很可怕。我甚至相信,短短不到100行的程式碼,在半年後他一定讀不出來自己在寫什麼。這一切讓我不禁懷疑,難到他們的老師都沒有告訴他們寫程式的習慣這件事嗎?

其實,擁有好的寫程式習慣,可以讓我們寫出來的程式碼乾淨明瞭,不但在當下能夠容易除錯,日後也容易維護,因它而省下的時間,絕對是值回票價的。因此,建立良好的寫程式習慣是每個程式設計者所不可缺少的一環。

在建立良好的寫程式習慣之前,我們要先建立自己寫程式的風格。在今天資訊科技這麼發達的世界,透過網路的交流,多數程式設計者流傳了一些不成文的風格定義,像是八格的縮排等等。你可以追隨前人,延用他們的風格,也可以自己創造一套風格,但不管你的風格如何,適當的駐解、縮排和乾淨的程式,絕對都是不能缺少的。

這邊提供了一份關於 Linux Kernel 程式的編寫風格說明以供參考:

http://www.linuxjournal.com/node/5780/print


有許多尚未建立習慣風格的人﹝多半是初學者﹞跑來問我問題時,我發現他們寫出來的程式會無法編譯通過,很多都是因為少了一個 { 或 }。追根究底,都是因為程式碼沒有縮排,我們很難看出其巢狀結構,自然就無法發現是否多或少了結尾符號。由此可知,良好的寫程式習慣和風格,是寫好程式的第一要素。

你還沒有好的習慣嗎?趕快去建立吧!

用 C 呼叫 rand() 每次都回傳相同結果?

為了寫一個隨機產生 ID 的程式,我用C去呼叫 rand() 這亂數產生函式,然而經過測試後發現,每次程式執行所產生的亂數都是一樣的。這問題讓我花了點時間研究了一下,我發現並非每次執行都一樣,而是每秒內產生 出來的亂數是一樣的。很明顯的,這裡的亂數種子設定出了點問題。在過去習慣上,在使用亂數前 我們會用 srand(time(0)) 設定亂數種子,而一般的書籍也是這樣教我們。但這種方式設定亂數種子,種子的變化是以秒為單位,也就是每秒只變動一次。由於種子在一秒鐘之內都是一樣的, 每次使用 rand() 時,產生出來的亂數也都會是相同的。
這樣設定亂數種子其實 並沒有錯,因為一般程式只需要在開頭設定第一次,之後就不必再設。但如果我們要寫的是一個指令,使用者就可能在一秒鐘之內重覆執行你的程式很多遍。那將會 導致亂數種子被重新定義很多遍。每當種子被重新設定,rand()自然就會把亂數表重頭讀取,當然,每次所得到的亂數就會相同。
我暫時想出了個解決辦法,就是針對種子再給予時間以外的變數,讓每次執行的種子變得更不一樣。於是我將種子設定改成這樣:

srand(time(0)+getpid());
這 以程式執行時的行程處理ID加了進來當種子,因為每個程式的 PID 一定不一樣,如果要一樣也要等許多的程式執行後,重頭分配 PID 才有可能相同。以 PID 再加上原本每秒產生出來的時間,如此產生出來的種子必定獨一無二。這點小技巧能夠解決 rand() 每次回傳都相同的問題。註:本文是架構在 Linux 上,並以 gcc 編譯所產生的程式當基準,若在 Windows 等其他平台和以 VC++ 、Turbo C等其他款編譯器,不能保證本文所談到的東西能起任何作用。

用心智圖軟體整理自己的思緒

圖片
以前寫各式各樣的程式,總是邊寫邊想其中的邏輯運作。碰到大一點的專案,最多是在紙張上用手畫一畫結構圖,寫一些重點關鍵。但講是講寫一些,最後一定是整疊的資料。
最近因緣際會發現了一個好用的軟體 FreeMind,你可以在 http://freemind.sourceforge.net/ 抓到它,它可以幫我很快的整理思緒,隨時可以刪減、修改或移動。更好用的是,它除了可以用圖顯示外,也可以轉成 HTML、XHTML、XML 等檔案,並條列式的將你的心智圖列出來。什麼是心智圖呢?可以參考下圖:以 FreeMind 完成的心智圖
以 前寫程式邊想邊寫,碰到比較複雜的問題,每次都因為太複雜一時想不出頭緒而心情煩躁,嚴重點甚至缺少往下寫的意願。現在,我管他三七二十一,就是先把整個 程式結構流程寫出來就對了。然後就會發現,一項項原本混亂的想法,都可在心智圖上歸類。而且許多想法在此架構下是否妥當,都可以在製作這張心智圖時就會發 現,並很快可以修改。不容易再發生像過去一樣當程式寫到某種程度時,才發現它的架構並不妥當,面臨是否要重新改寫的問題了。當製作完這張圖後,我只要照圖上的邏輯流程,用我的頭腦轉換成程式碼就好了,不用再去煩惱其中的結構問題,真是好用!因為它,我現在寫程式效率倍增!歡迎大家一同來使用這好用的 FreeMind!

Linux 的 random 總是裝死

應該有不少人碰過 Apache 啟動時卡在 digest module 過不去,導致 PHP 不能使用,或是碰過某些程式卡在某個地方很久沒動作。這時候大家可能要去檢查一下 /dev/random 這個設備檔案。可以用 cat /dev/random 來看它的內容,如果你發現他一直沒顯示任何內容﹝可能是亂碼數字之類的﹞,那就是它出問題了。


據我從 digest module 的 source code 裡所知,這模組呼叫了一個亂數產生的函數,此函數會呼叫 /dev/random。所以當 /dev/random 卡住顯示不出內容時,很自然的任何 read() 它的程式會停在那等它回應。


我了解 Linux kernel 的 source code 之後發現,/dev/random 會使用 key/mouse/disk 的 interrupt 當做亂數種子的計算來源,好處是會有更大的安全性,但問題是當我們沒在使用 key/mouse/disk 或是他們的 interrupt 訊號量不足以產生亂數種子時,/dev/random 就會永遠是空白沒東西。

這問題有個暫時的解決辦法,就是將 /dev/random 砍掉,先用 link的方式使用 /dev/urandom 取代 /dev/random。

要完全解決此問題,可至 SourceForge 找到 gkernel 這 Project。

下載 rng-tool 回來編譯安裝。

然後於開機的 Scripts 中加入:

rngd -b -o /dev/random -r /dev/urandom

此 Deamon 將會在沒足夠 interrupt 訊號時使用 urandom 的亂數當random 的亂數種子。

用 UPX 幫你的二進位執行檔瘦身

什麼是 UPX ?它的全名是 the Ultimate Packer for eXecutables,顧名思義就是能將執行檔做壓縮,使執行檔變得很小。有多小呢?一般狀況下可以壓縮成原來的 40% 甚至更小!

程式執行會不會變慢?

關於被壓縮過後的程式執行速度,只有執行剛開始需要解壓縮會比較慢,當程式啟動完畢後就和原先速度一樣,所以最多是啟動時速度比較慢而已。據官方數據統計,在 Pentium 133 的系統上,解壓縮的速度每秒可達 10mb;而在 Athlon XP 2000+ 的系統上,每秒可達 200mb,程式會到達 200 mb 的應該沒有才是。所以說啟動速度只慢個一秒不到,甚至感覺不到有慢,尤其今天的 CPU 隨便就 3.0GHz 以上,解壓縮速度的問題根本就不用煩惱。

能應用在哪裡呢?

一般人不會無聊到要去省那一點點的空間,反正現在硬碟大的很。但是,若用在嵌入式及 LiveCD 系統中,這可以省下不少空間,甚至在 LiveCD 中,程式執行效能還會增加。程式執行效能增加?何解呢?因為光碟讀取是機械動作,速度極慢,尤其當光碟有刮損、不乾淨時,更是一點點資料讀半天。若是用 upx 將執行檔壓縮成一半大小不到,就可以減少光跌機讀取的時間。如此,速度不就增加了嗎?

如何使用呢?

可以去官方網站 Download 壓縮主程式: http://upx.sourceforge.net/

裝好後,就可以用 upx 壓縮執行檔了,如下:

upx <binary-file>

後記

UPX 除了可處理 Linux kernel 和 ELF 檔外,在其他的平台如 Windows、 DOS上也可運作,官方網站上有詳細的資料。