星期四, 6月 07, 2007

Chrasis 0.1.0 alpha!

嗯... 廣告文 XD~

這兩天把散落一地的 code 整理了一下,作成了可以放到 /usr/lib 底下的 library。又另外拼湊了一個 Trainer 出來,看起來比較「動感(interactive)」一點,可以 online 告訴你辨識率(這樣感覺比較像有在辨識)。

想玩玩看嗎?請見 Chrasis 首頁

星期一, 6月 04, 2007

喂喂,別盜用我的無線網路!

在 oftc 的 #dot,pnt 兄分享了一篇文章

喂喂!這也太狠了吧?XD~

看吧,只要稍微學會 Linux,你就可以做這種事惡整盜用你家 wlan 的鄰居!

星期五, 6月 01, 2007

換湯不換藥

ㄟ... 這會不會太扯?

課程名稱一個樣,課程內容另一個樣...

不知道什麼時候可以改好一點 = = 網址在這裡

星期二, 5月 08, 2007

讓 Firefox 開快點 - 關掉啟動時的自動更新

即使我已經無所不用其極的想辦法讓程式載入的速度快點,但是碰上 Firefox 這頭怪獸,把他從磁碟裡拖到螢幕上裡還是得花個兩三秒。

後來,我發現 Firefox 會嘗試在啟動的時候(程式已經啟動、但是視窗跳出之前)更新附加元件與搜尋引擎。這麼一來,若有更新,可以不必在你已經開了一堆視窗以後,跳個對話方塊出來要求你重新啟動 Firefox。

但是,由於網路的 latency,檢查更新的速度快則半秒,慢則一兩秒,可以說在我 Firefox 的啟動過程中,佔掉了 20~50% 的等待時間。於是,我決定將它關掉,並安裝另一個 extension 來檢查更新。(反正又不是一天到晚有更新檔要裝,但是我可是一天到晚都會關閉、開啟 Firefox 啊!)

怎麼關呢?請至「編輯 => 偏好設定 => 進階 => 更新」下,將「自動檢查是否有更新」下的所有東西(應該有三個,分別是「Firefox」、「以安裝的附加元件」、「搜尋引擎」)取消選取。如下圖所示:

可是這樣 Firefox 不就不會幫我更新這些東西了嗎?沒關係,讓我們裝上「Update Notifier」來負責這件苦差事:

  • 首先按下「Install Now」來安裝
  • 可憐的你必須重開 Firefox 了!
  • 重開以後,右上角會多出一個小圖示,請你點他一下
  • 按一下「選項」進入設定頁面
  • 於「一般」頁面,將「當 Firefox 啟動時,檢查有無更新檔可用。」取消選取
  • 在「通知」頁面,將「設定 Firefox 多久檢查一次更新。」選取,並依照您雞皮的程度設定每次檢查的間隔時間。
  • 按下「確定」!

這麼一來,每次啟動 Firefox 至少又可以快個 0.5 秒啦!

星期六, 5月 05, 2007

美勞課考卷發了!

美勞嵌入式系統期中考時,有類似這樣的題組:

# ls -al
Total 20
drwxr-xr-x 2 tblanku users 4096 May 5 02:20 .
drwxr-xr-x 9 root root 4096 Mar 24 08:26 ..
-rw-r--r-- 1 tblanku users 127 Mar 4 07:32 .bash_logout
-rw-r--r-- 1 tblanku users 193 Mar 4 07:32 .bash_profile
-rw-r--r-- 1 tblanku users 551 Mar 4 07:32 .bashrc
-rw-r--r-- 1 root root 0 May 5 02:20 somefile
-rw-r--r-- 1 root root 213 May 5 02:20 other file

請問 tblanku 可否刪除 somefile 這個檔案?

我當然是寫「是,因為刪除檔案是看目錄權限。」可是,老師在檢討考卷的時候居然說:「因為 somefile 的擁有者是 root,tblanku 只有讀取的權限,所以不能刪除。」所以就被改錯了 XD~

這種 well-known 的 gotcha 也會掉進去!? @@ 而且,他果然出了「不能使用空格」這種題目...

Linux 的檔案系統並沒有硬性規定檔名使用的編碼(或是,哪些字元可用、哪些不可用),而是把這部份的決定留給了使用者。除了「/」這個目錄分隔符號以外,其他字元都可以拿來當檔名。

還有一些被說得很好笑的答案:

Q: 如果要把「install.sh」更名為「setup.sh」,要下什麼指令?

A: mv {install,setup}.sh

結果被批「沒有人會這樣用」...

還有一題...

Q: 雖然圖形介面簡單好用,為什麼在開發嵌入式系統的時候需要使用指令模式?

A: 因為你不會用!還是有很多圖形介面的工具可以輔助嵌入式系統開發的。Open Source, Open Mind!

結果上課的時候被說「有人跟我嗆聲,說是因為我不會用」XD~

嗯... 我想你還是繼續放 NTT DoCoMo宣傳影片好了...

星期三, 5月 02, 2007

題意 XD

前幾天(May 1, 2007)系上舉辦程式設計比賽,出了四道題目。其中一題大概是這樣的(題目卷收回去了,所以只能列出大概...):

今天有一由英文字母與括號組成的字串 S,可以對他們作 HEAD 與 TAIL 兩種操作,行為是這樣:若 S=a(bc),則 HEAD(S)=a、TAIL(S)=bc,若 S=(bc) 則 HEAD(S)=NULL、TAIL(S)=bc,若 S=a,則 HEAD(S)=NULL

範例輸入:
a(b(cd)))
TAIL
HEAD

範例輸出:
b

恩... 不要問我為什麼出這麼簡單的題目,因為我也不知道 = = 總之,看到這個題目,我想到類似以下的表格:

S HEAD TAIL
a(bc) a bc
(bc) NULL bc
a NULL(題目這樣說嘛!) a(猜的)

於是,寫出程式碼以後,驗證失敗... 花了些時間修改以後,最後還是錯誤。

比賽結束後,就去問監考老師,看能不能取得測試資料,順便問他我們到底錯在哪。當時他看了看題目以後,說「喔,應該是題目錯了,如果沒有這行(S=a HEAD(S)=NULL)就對了。」

那... 廢話嘛 = = 哪有「題目錯了,我們也就跟著錯了」這種事,當然要想辦法補救(至少要能讓人家覺得這是場公平的比賽)。可是當時老師跟助教就一付息事寧人的態度,對我們說「不然你是想要我怎樣?向全校公開道歉嗎?」實在很無言。就這樣,我們當天在系辦吵了兩個多小時。

後來,老師、助教、我們自己又調備份資料出來看,才終於弄懂到底錯在哪。原來題目跟測試資料都沒問題,是我們誤解了題意。如果 S=a(bc) 是 general case,括號前面的是 HEAD、裡面的是 TAIL。而 S=(bc) 是只有 TAIL 時的 special case, HEAD 是 NULL、TAIL 是括號中剩下的東西。而 S=a 是只有 HEAD 的 special case,不但 TAIL=NULL、連 HEAD 也一併變成 NULL 了。

嗯,好吧!的確是自己會錯題意,既然這樣就沒話說了。雖然我好像也測過 S=a HEAD=NULL TAIL=NULL 這種組合... 可能是其他部份的程式碼有問題吧!

後來我問我的隊友「我是不是很龜毛?」,他說「是,我當時就想叫你算了」。我告訴他的理由是,「因為這是比賽,我不願意在不明不白的狀況下贏過別人,也不能不明不白讓別人贏過我」。他後來也就理解了,也陪我跟老師囉唆了兩天 XD~

不過題外話... 有另外一題大概是這樣:

小明走在海邊檢石頭,每次只能撿比目前石頭重,而且不能往回走。而且為了避免一下子拿太重手臂負荷不了,所以只能拿比現在重十公斤以內的石頭。例如目前拿的石頭是 5kg,那麼下一次只能換成 6~15kg 的石頭。請問怎樣的拿法,可以達到最多交換次數?

範例輸入:
3 2 21 60 4 6 8 70 44 7 5 10 33 9 11

範例輸出:
6
2 4 6 8 10 11(我沒有實際算,這裡的答案是隨便唬嚨的...)

我一看到這個題目,就跟隊友說「這個可能有兩組最佳解吧?」,於是他就問老師「如果有兩組以上最佳解怎麼辦?」結果老師跟助教很肯定的跟我說「測試資料只有一組最佳解。」

很好,跑出來他的一些測試資料裡會有兩到三組最佳解 = = 嗯... 驗證失敗 XD~

搞成這樣我也不知道該說什麼,不過老師最後還是給我們對,但是時間就不知道該怎麼算了...

星期五, 4月 20, 2007

嵌入式系統 之於 美勞

「如果你那麼想玩(ARM9 的)板子,可以自己去買。」

朝陽科技大學 95 學年度
資訊工程系「嵌入式系統」課程
講師 - 陳宏達

我們系上本學期的選修課之一「嵌入式系統」,預定的開發環境是 Embedded Linux。

無可厚非的,前幾次上課是在作「嵌入式系統」與「Linux 基本操作」的簡介,恩... 考慮到他幫忙推廣 Linux,就... 先算了吧。不過,他還是宣導了許多不知道哪裡發明的謬論...

「你們可以先去下載 VMWare 回來灌(Linux),不過他要註冊,而且只能用 30 天。不過我知道你們一定很有辦法,去一些大陸網站找一下就可以永遠試用下去。」
(我的天啊!身為一個資訊工程系的教師,不但避免使用無授權的軟體,還在課堂上變相要大家想辦法找破解?拜託,資訊工程系的學生,以後出社會可是要寫軟體的。如果連自己都使用盜版軟體,以後寫出來的產品要賣給誰?)

「你們在灌(Linux)的時候,只要用文字介面就好。高手都用文字介面,只有新手才需要圖形介面。」
(怎樣?把全世界的 Linux 桌面使用者都當新手是吧?唔... 沒有啦,我承認我是新手 XD~)

「Linux 下無法建立以 "-" 開頭的檔案,也不能建立中間有 "*"、"?" 或 " "(空格)的檔案。」
(我已經打算,他如果在期中考出這題,又把我改錯,我就當場問他「如果我現在建出來,是不是全班送分?」。)

好,這些都可以算了,畢竟不關我的事。第一,我電腦裡沒有盜版軟體;第二,我是 Linux 新手(XD~);第三,我知道怎麼建立這些檔案。

可是,都已經要期中考(意思就是開學到現在經過了約兩個月了)了,大家還沒看到板子長什麼樣子。於是我就問了...

「老師,請問到底什麼時候可以玩到板子?」

「Linux 都不會用就想玩板子?」

「我已經用一陣子,也大概會用了。」

「還有 Toolchain 啊,那個都沒弄好就想玩板子?」

「那個我已經都編好等著了。」

「總是要配合其他同學的進度啊!」

「......」(,我繳學費來學校,是為了配合大家的進度?)

「如果你真的那麼想玩板子,可以自己去買。」

「............」(,如果我去買了塊板子放家裏,還需要來上你的課?)

接著,又說了些...

「有一個研究生學長,為了專題,花了十多萬買硬體。」

「普通的開發版,大概一兩萬就買的到了。」

其實想想,從小到大...

  • 上美勞課要自己買彩色筆
  • 上工藝課要自己買材料
  • 上程式設計可以自己買台電腦

以此類推...

  • 上嵌入式系統要自己買開發版

似乎還蠻合理的!

幹,宏達老師,您怎麼不多感冒幾次

星期三, 4月 18, 2007

嚴重的女性傾向......

來吧!測測你的 blog 是男是女?

我輸入本站的 atom 以後,居然出來這種結果:

Palatis's Kafooster
6.0%男性倾向,94.0%女性倾向
评点:明净雅致的语言让人为之倾倒,人生的感悟、生活情趣都融合在其中,自有一番细腻的心思在其间。
yodao | 博客男女

奇怪,一大堆技術文章居然也可以被評為「有一番細膩的心思在其間」,真是搞不懂大陸人 = =

還是說我根本是個娘砲...!?(淚奔 QoQ)

星期四, 4月 05, 2007

啟發式搜尋演算法 - A* Algorithm

恩... AI 的作業 - A* Search Algorithm

老師是要我們寫八陣圖啦!不過我看到 A* 的時候想,其實 A* 需要的也就是這些東西:

  • 當前的 state
  • 接下來可能的 state
  • 要能計算兩個 state 之間的距離

所以,我把 code 提煉成一個 template function,只要餵他一個 start 一個 target,他就會把中間要走的路找出來。而這個 template function 需要:

  • NODE_T:表示 state
  • distance(NODE_T, NODE_T):用來計算兩個 state 之間的距離
  • solution(NODE_T, NODE_T):驗證是否能從一個 state 走到另一個 state
  • NODE_T::childs():接下來可以走的 state

board.h:這是用來測試的 node class 與 distance() function 實做: #ifndef _BOARD_H
#define _BOARD_H

#include <vector>
#include <algorithm>
#include <iostream>
#include <set>

template <int SIZE>
class basic_board
{
public:
        typedef std::vector< char > storage_t;

private:
        class INDEX_HELPER
        {
        public:
                INDEX_HELPER(int const x, const storage_t & s):
                        idx_x_(x), storage_(s)
                { }

                int const operator[] (int const idx_y) const
                {
                        return storage_[idx_y * SIZE + idx_x_];
                }

        private:
                int idx_x_;
                storage_t const & storage_;
        };
public:
        basic_board(storage_t const & prototype):
                storage_(prototype)
        { }

        basic_board(bool random_init = false):
                storage_(SIZE*SIZE)
        {
                for (int i=0;i<storage_.size();++i)
                        storage_[i] = i;
                if (random_init)
                        std::random_shuffle(storage_.begin(), storage_.end());
        }

        template <int S>
        friend class board_distance;

        template <int S>
        friend class board_solution;

        bool operator != (basic_board const & rhs) const
        {
                return storage_ != rhs.storage_;
        }

        INDEX_HELPER const operator [] (int const idx_x) const
        {
                return INDEX_HELPER(idx_x, storage_);
        }

        std::vector< basic_board > childs()
        {
                std::vector< basic_board > ret;

                // first find empty node and its (x, y)
                storage_t::iterator e = std::find(storage_.begin(), storage_.end(), 0);
                int e_x = ( e - storage_.begin() ) % SIZE,
                    e_y = ( e - storage_.begin() ) / SIZE;

                basic_board tmp(*this);
                storage_t::iterator ee = tmp.storage_.begin() + e_y * SIZE + e_x, ii;
                if (e_y - 1 >= 0)        // move up?
                {
                        ii = tmp.storage_.begin() + (e_y - 1) * SIZE + e_x;
                        std::iter_swap(ee, ii);
                        ret.push_back(tmp);
                        std::iter_swap(ee, ii);
                }
                if (e_y + 1 < SIZE)        // move down?
                {
                        ii = tmp.storage_.begin() + (e_y + 1) * SIZE + e_x;
                        std::iter_swap(ee, ii);
                        ret.push_back(tmp);
                        std::iter_swap(ee, ii);
                }
                if (e_x - 1 >= 0)        // move left?
                {
                        ii = tmp.storage_.begin() + e_y * SIZE + e_x - 1;
                        std::iter_swap(ee, ii);
                        ret.push_back(tmp);
                        std::iter_swap(ee, ii);
                }
                if (e_x + 1 < SIZE)        // move right?
                {
                        ii = tmp.storage_.begin() + e_y * SIZE + e_x + 1;
                        std::iter_swap(ee, ii);
                        ret.push_back(tmp);
                        std::iter_swap(ee, ii);
                }

                return ret;
        }

private:
        storage_t storage_;
};

template <int SIZE>
std::ostream &
operator << (std::ostream & lhs, basic_board<SIZE> const & rhs)
{
        for (int i = 0; i < SIZE; ++i)
        {
                for (int j = 0; j < SIZE; ++j)
                        lhs << rhs[j][i] << ", ";
                lhs << std::endl;
        }
}

// solution verifier
template <int SIZE>
class board_solution
{
public:
        bool operator() (basic_board<SIZE> lhs, basic_board<SIZE> rhs)
        {
                // move both empty cell to upper-left corner
                typename basic_board<SIZE>::storage_t::iterator e, i;
                int e_x, e_y;
                
                e = std::find(lhs.storage_.begin(), lhs.storage_.end(), 0);
                e_x = ( e - lhs.storage_.begin() ) % SIZE;
                e_y = ( e - lhs.storage_.begin() ) / SIZE;
                while (e_y - 1 >= 0)        // move up?
                {
                        i = lhs.storage_.begin() + (e_y - 1) * SIZE + e_x;
                        std::iter_swap(e, i);
                        e = i;
                        e_x = ( e - lhs.storage_.begin() ) % SIZE;
                        e_y = ( e - lhs.storage_.begin() ) / SIZE;
                }
                while (e_x - 1 >= 0)        // move left?
                {
                        i = lhs.storage_.begin() + e_y * SIZE + e_x - 1;
                        std::iter_swap(e, i);
                        e = i;
                        e_x = ( e - lhs.storage_.begin() ) % SIZE;
                        e_y = ( e - lhs.storage_.begin() ) / SIZE;
                }
                
                e = std::find(rhs.storage_.begin(), rhs.storage_.end(), 0);
                e_x = ( e - rhs.storage_.begin() ) % SIZE;
                e_y = ( e - rhs.storage_.begin() ) / SIZE;
                while (e_y - 1 >= 0)        // move up?
                {
                        i = rhs.storage_.begin() + (e_y - 1) * SIZE + e_x;
                        std::iter_swap(e, i);
                        e = i;
                        e_x = ( e - rhs.storage_.begin() ) % SIZE;
                        e_y = ( e - rhs.storage_.begin() ) / SIZE;
                }
                while (e_x - 1 >= 0)        // move left?
                {
                        i = rhs.storage_.begin() + e_y * SIZE + e_x - 1;
                        std::iter_swap(e, i);
                        e = i;
                        e_x = ( e - rhs.storage_.begin() ) % SIZE;
                        e_y = ( e - rhs.storage_.begin() ) / SIZE;
                }

                std::cout << lhs << std::endl;
                std::cout << rhs << std::endl;

                // checking parity
                int p(0);
                for (e = lhs.storage_.begin() + 1;
                     e != lhs.storage_.end();
                     ++e)
                {
                        i = std::find(rhs.storage_.begin()+1, rhs.storage_.end(), *e);

                        for (typename basic_board<SIZE>::storage_t::iterator j = e;
                             j != lhs.storage_.end();
                             ++j)
                                if (std::find(rhs.storage_.begin()+1, i, *j) != i)
                                        ++p;
                }
                std::cout << "Parity: " << p << " (" << p%2 << ")" << std::endl;

                return (p%2) != ((SIZE*SIZE)%2);
        }
};

// manhatten distance
template <int SIZE>
class board_distance
{
public:
        board_distance(basic_board<SIZE> const & target):
                pos_cache_(SIZE*SIZE)
        {
                for (int i=0;i<SIZE*SIZE;++i)
                {
                        typename basic_board<SIZE>::storage_t::const_iterator
                                t = std::find(target.storage_.begin(), target.storage_.end(), i);
                        pos_cache_[i] = std::make_pair(
                                ( t - target.storage_.begin() ) % SIZE,
                                ( t - target.storage_.begin() ) / SIZE
                        );
                }
        }

        int operator() (basic_board<SIZE> const & lhs)
        {
                int ret(0);
                for (int i=1;i<SIZE*SIZE;++i)
                {
                        typename basic_board<SIZE>::storage_t::const_iterator
                                l = std::find(lhs.storage_.begin(), lhs.storage_.end(), i);
                        ret += std::abs(
                                        pos_cache_[i].first - 
                                        ( ( l - lhs.storage_.begin() ) % SIZE )
                                ) + std::abs(
                                        pos_cache_[i].second - 
                                        ( ( l - lhs.storage_.begin() ) / SIZE )
                                );
                }
                return ret;
        }

        int operator() (basic_board<SIZE> const & lhs, basic_board<SIZE> const & rhs)
        {
                int ret(0);
                for (int i=1;i<SIZE*SIZE;++i)
                {
                        typename basic_board<SIZE>::storage_t::const_iterator
                                l = std::find(lhs.storage_.begin(), lhs.storage_.end(), i),
                                r = std::find(rhs.storage_.begin(), rhs.storage_.end(), i);
                        ret += std::abs(
                                        ( ( l - lhs.storage_.begin() ) % SIZE ) -
                                        ( ( r - rhs.storage_.begin() ) % SIZE )
                                ) + std::abs(
                                        ( ( l - lhs.storage_.begin() ) / SIZE ) -
                                        ( ( r - rhs.storage_.begin() ) / SIZE )
                                );
                }
                return ret;
        }
private:
        std::vector< std::pair<int, int> > pos_cache_;
};

typedef basic_board<3> Board_3x3;
typedef basic_board<4> Board_4x4;
typedef basic_board<5> Board_5x5;

typedef board_distance<3> Distance_3x3;
typedef board_distance<4> Distance_4x4;
typedef board_distance<5> Distance_5x5;

typedef board_solution<3> Solution_3x3;
typedef board_solution<4> Solution_4x4;
typedef board_solution<5> Solution_5x5;

#endif

astar.h:這是 A* 演算法與一個 helper class。

#ifndef _ASTAR_H
#define _ASTAR_H

#include <map>
#include <vector>

template <typename COST_T, typename NODE_T>
class NODE_HELPER
{
public:
        NODE_HELPER(COST_T c, NODE_T const & s, int p):
                cost_(c), state_(s), parent_(p)
        { }

        int parent() const { return parent_; }
        int cost() const { return cost_; }
        NODE_T & state() { return state_; };

        std::vector< NODE_T > childs() { return state_.childs(); }

private:
        int parent_;        // parent step
        COST_T cost_;        // cost from start to this state
        NODE_T state_;        // current state
};

template <typename NODE_T, typename DISTANCE_T, typename VERIFIER_T>
std::vector< NODE_T >
astar_search(NODE_T const & start, NODE_T const & target, DISTANCE_T dist, VERIFIER_T sol)
{
        if (!sol(start, target))
                return std::vector< NODE_T >();

        std::multimap<int, NODE_HELPER<int, NODE_T> > pending;
        pending.insert(std::make_pair(dist(start, target), NODE_HELPER<int, NODE_T>(0, start, 0)));

        std::vector< NODE_HELPER<int, NODE_T> > solution;

        int n_iter(0);
        while(dist(pending.begin()->second.state()))
        {
                solution.push_back(pending.begin()->second);
                int cost_so_far = pending.begin()->second.cost();
                pending.erase(pending.begin());

                std::vector< NODE_T > tmp_cld = solution.rbegin()->childs();
                for(typename std::vector< NODE_T >::iterator i = tmp_cld.begin();
                    i != tmp_cld.end();
                    ++i)
                {
                        int cost = cost_so_far + dist(solution.rbegin()->state(), *i);
                        pending.insert(
                                std::make_pair(
                                        cost + dist(*i),
                                        NODE_HELPER<int, NODE_T>(
                                                cost,
                                                *i,
                                                solution.size() - 1)
                                )
                        );
                }
        }

        std::vector< NODE_T > ret;
        ret.push_back(pending.begin()->second.state());
        for (int p_idx = pending.begin()->second.parent();
             p_idx != 0;
             p_idx = solution[p_idx].parent())
                ret.push_back(solution[p_idx].state());
        ret.push_back(start);
        std::reverse(ret.begin(), ret.end());
        return ret;
}

#endif

main.cpp:用來測試的...

#include <iostream>
#include <vector>
#include <sstream>

#include <unistd.h>

#include "board.h"
#include "astar.h"

typedef Board_4x4 BOARD_T;
typedef Distance_4x4 DIST_T;
typedef Solution_4x4 SOL_T;

int main(int argc, char* argv[])
{
        int random_seed = 0;
        if (argc > 1)
        {
                std::stringstream ss(argv[1]);
                ss >> random_seed;
        }
        srand(random_seed);

        std::cout << "Random seed: " << random_seed << std::endl;

        BOARD_T b(true), t(false);

        std::cout << "Start:" << std::endl << b << std::endl;
        std::cout << "End:" << std::endl << t << std::endl;
        std::cout << "Trying hard to solve (with A*)..." << std::endl;

        std::vector< BOARD_T > result = astar_search(b, t, DIST_T(t), SOL_T() );

        std::cout << "Solution steps: " << result.size() << std::endl;
        for(std::vector< BOARD_T >::iterator i = result.begin();
            i != result.end();
            ++i)
        {
                //sleep(1);
                std::cout << *i << std::endl;
        }

        return 0;
}

如果編不起來,那是的問題!BSD License

星期二, 3月 27, 2007

螢幕燒掉了 @o@

昨天(March 26, 2007)中午左右,在我回味老動畫 - MADLAX 的時候,突然螢幕一黑,伴隨而來一陣燒焦的味道。我首先以為是顯示卡燒了,所以馬上去按著主機上的 Power Button 四秒(感謝 Journaling Filesystem 的加持,沒有 dataloss。)然後又想到也可能是螢幕的問題,所以馬上又伸手按下螢幕的電源開關。

後來靠近聞主機屁股,並沒有半導體的焦味,再去聞一下螢幕... 果然是 AG Neovo E19 A 燒了...... 幸好機板跟面板都是三年保固,May 2004 買的,差兩個月就過期了 = = 趕快打他網頁上的電話,問他要怎麼辦。

AG Neovo 的處理還蠻迅速的,昨天下午打電話,今天上午在我睡夢中就來拿了。不過他沒有收件的單子給我,也不知道要多久才會送回來,看來我這兩個禮拜電話要開著了 @@

所以,這台電腦現在完全從 MultiMedia Center + Server 退化成 Server 了 QQ,主要跑的服務剩下:

  • 動物
  • NAT
  • mpd
  • apache2 + php
  • dante

星期日, 3月 25, 2007

netconsole & syslog-ng

最近我的筆電動不動就 kernel panic,也不知道到底出了什麼問題。想看 backtrace 也不行,因為總是在 X 底下死掉,沒有 console 讓我看 log。而 kernel panic 的時候 print 出來那些東西,也不會跑進 syslog 裡。所以啦,只好讓他 log 到 remote 了...

其實,在 Gentoo 要用 netconsole 是很簡單的。因為是 "NET"console,所以當然會有一台 client 一台 server。首先假設我們的環境看起來像這樣:

  • Server:192.168.1.1:6667
  • Client:192.168.1.99:6665

Client 端(Kernel module 與 cancd)

首先在 client 上,編譯核心的時候,選擇 netconsole 模組(記得選 M 唷!):

  Device Drivers  --->
    Network device support  --->
      <M>   Network console logging support (EXPERIMENTAL)
      [*] Netpoll support for trapping incoming packets
      [*] Netpoll traffic trapping

好啦,雖然我不知道最下面兩個是幹嘛的,不過還是把他們打開吧(反正打開也不用錢...)!順便打開 Magic SysRq:

    Kernel hacking  --->
      [*] Magic SysRq key

編譯並安裝,請把 {N} 替換為您的 cpu core 數量 +1:

# make modules -j{N} && make modules_install

然後,裝上 cancd(CA NetConsole Daemon)...

# emerge cancd -va

設定 /etc/conf.d/netconsole:

# 填入 Server 的 IP,這裡以 192.168.1.1 為例:
TGT_IP='192.168.1.1'

# 與該 IP 對應的 device
DEVICE=eth0

# 如果沒寫的話,會用該 device 的第一個 IP 位置。
# 如果該設備在開機時不會拿到 IP 位置,則您必須在這裡輸入它。
SRC_IP=''

# Client 要使用的 Port,可以隨便給。以下為預設值!
SRC_PORT=6665

# Server 端的 Port,當然要與 Server 上設定的相同
TGT_PORT=6667

# 如果要使用廣播,則輸入 'broadcast'。但是要注意安全性!
# 我們不用,所以就不輸入了...
TGT_MAC=''

# 紀錄的等級,從 [0..7] 分為:
# EMERG, ALERT, CRIT, ERR, WARNING, NOTICE, INFO, DEBUG
# 恩... 我選了 7 (DEBUG)
LOGLEVEL='7'

將 netconsole 加入 default runlevel 並啟動它...

# rc-update add netconsole default
# /etc/init.d/netconsole start

「咦~」您也許會問,「就這樣丟 log 出去不會連線失敗嗎?那 Server 咧?」。別急別急,反正也不會直接有訊息丟出來(唔,如果您很倒楣的,Client 在此時 Kernel Panic 了,那我只能說很抱歉 = =),馬上接著來設定 Server!

Server 端(Syslog-ng)

編輯 /etc/syslog-ng/syslog-ng.conf,「加入」以下設定(別把上面之前的東西刪掉喔,不然本來系統的 log 就不知道要記到哪裡去了 = =):

# 首先是 ip 與 port,如果不知道要改哪裡,
# 請把 Linux 分區 format 掉,去買套 Windows Vista
source net_src { udp(ip(192.168.1.1) port(6667)); };

# 在這裡設定紀錄檔,此以 /var/log/net_msgs 為例。
destination net_msgs { file("/var/log/net_msgs"); };

# 接著是要從哪裡紀錄到哪裡...
log { source(net_src); destination(net_msgs); };

# 如果要順便寫到 tty12,就加入以下這行:
# (一般來說都是 tty12 吧?要看之前上面的設定。)
log { source(net_src); destination(console_all); };

好啦,重新啟動 syslog-ng 吧!用 reload 他就會重新讀取設定檔了,不必用 restart。

# /etc/init.d/syslog-ng reload

測試一下!

恩... 我想,您剛才應該有把 Magic SysRq 打開吧?如果有的話,請在 Client 按下 Alt-SysRq-m,然後去 Server 看一下 /var/log/net_msgs 從 Client 傳來的訊息!

星期六, 3月 17, 2007

如果你想知道磁碟 I/O 到底會拖慢多少啟動程式的速度

那就這麼作吧!

// 首先當然是關掉你要測的那個程式, 別忘了先把重要資料存檔
# killall -9 firefox-bin
// 呼叫 sync 把重要資料寫入硬碟
# sync
// 然後清掉 buffer/cache
# echo 3 > /proc/sys/vm/drop_caches
// 重新啟動(Enjoy the waiting :P )!
$ firefox

星期日, 3月 04, 2007

令我日思夜想的檔案系統

奇怪,怎麼都沒人想到呢?

先前提到說,適當的將檔案系統壓縮,可以提高磁碟的存取效率 - 尤其是 CPU 速度與 IO 速度差很多的時候。雖然沒有完整的數據顯示到底快多少,不過 RANMA K 兄有初步測試過不壓縮與壓縮時的寫入、讀取速度。況且還有個良好的副作用 - 同樣大的硬碟空間裡,可以裝的東西更多了,而且完全不會變重

根據用屁股思考就可以領悟的常識,我們知道:

  1. CPU 的 IO 速度非常快(相對於硬碟)
  2. Disk 的 IO 速度非常慢(相對於 CPU)
  3. 大部分人的 Desktop PC,CPU 使用率並不會常保 100%...
  4. 越大的檔案越有機會產生 Fragmentation,而 Fragmentation 會增加 Seek Time。
  5. 若資料已經壓縮過了,再壓一次只是浪費時間。(呃... 廢話! = =)

所以,希望有一種檔案系統的,會根據檔案的狀態來選擇性的壓縮檔案,大概像這樣:

  1. 首先有個選項來針對檔案設定是否壓縮,例如 compress_attribute:
    • Always:總是壓縮
    • Automatic:根據下述規則決定是否壓縮
    • Never:絕對不要壓縮
  2. 如果檔案太小,就不壓縮。
  3. 如果檔案已經被壓縮過,就不壓縮。例如:
    • 一般壓縮檔:ace, bz2, gz, rar, tbz2, tgz, zip... etc.
    • 壓縮過的多媒體檔案:avi, mpg, mpeg, mp3, wma, wmv... etc.
    • 壓縮過的圖片檔:gif, jpg, mng, png... etc.
  4. 其他還沒想到的規則...

當然,這個檔案系統要有現代檔案系統該有的功能,例如:

  • Journaling
  • Online Repacker
  • POSIX ACL
  • Online resizing (grow and shrink)
  • 其他還沒想到的功能...

如果有相關的資料或論文,還請不吝留個言... 多謝!

星期四, 2月 22, 2007

小時候寫的作文

過年嘛,總是要掃除一下。雖然我沒有想要打掃的意思,但是還是把櫃子裡的東西拿出來,東翻翻西翻翻以後再放回去,假裝有整理過些什麼。

翻一翻,找到了國小五年級的作文簿!真感謝我媽沒把他當資源垃圾拿去回收,讓我看到我以前有多麼無厘頭。下面還只是其中一篇(其實我懶得打字,而且其他幾篇寫的還蠻丟臉的 |||>_<b)...

冬天

今天是冬天。(啊!說錯台詞!?現場直播!不能改了。繼續編下去吧!)那一顆松是在搖。對,他在姚。它是咖啡色的,葉子也是。很醜。對!很... 什麼?很醜?你... 算了。我有一個故事,我們來分享一下:

在一個大雪紛飛的冬天,在那個森林裡...。「那個」森林是什麼東東?就是「這個」森林!有一對父子,在打獵。嗯!廁所在哪裡。廁所?你說「廁所」做什麼?你「嗯!」不是要「便」魔術嗎?你不必管!繼續說!經過一天,也打不到東西,卻在森林中迷路了。在那時,他們看到一間小屋,就近去了。這時,他們因為很累,就睡了下來。到了半夜,兒子看到了一個「石膏色」的女人,從們旁邊的牆中飛了進來。向他爸爸吹了一口氣。他爸爸的血液就結冰了!雪女說:「我不要你的命。但是你把今天的事情說出去,你也是我牙下的客人。」說完,就走了。這個小男孩長大以後,在十二月十二日的晚上十二點十二分十二秒,遇到了一個名叫「小雪」的女孩。這個小男孩就和這女孩結婚了。在洞房的那一天,那個小男孩說:「我...,」那小雪的眼睛為之一亮!「就是『怪歐吉桑』!」結果,在外面的爸爸靈魂跌了一跤「掉」進來,這場戲再也演不下去了。

完!

現在看看,發現...

  • 我以前一定很討厭寫字 - 加超多標點符號(很多單引號跟驚嘆號)跟簡單的字(十二月十二日的晚上十二點十二分十二秒)撐篇幅。
  • 喜歡自言自語 - 第一段沒用的前導跟故事開始的第一段,因為一直在跟讀者說話,所以搞不清楚到底要表達些什麼。
  • 故事中的人物也搞不清楚自己在幹嘛 - 雪女說「你也是我牙下的客人」,但是她並沒有嚼食他爸爸啊...!
  • 從頭爛到尾 - 一定是因為老師說要寫四頁,所以我一寫超過四頁,馬上就隨便作個結束 = =
  • 導師評語 - 文章形式特殊,但似乎沒有表達出「冬天」!(哪有!看完後不覺得很冷嗎?想想其實蠻切題的 = =)

PS. 『怪歐吉桑』是志村大爆笑裡,由志村健飾演的那個怪叔叔。

星期二, 2月 13, 2007

圖片加密函式庫

最近獨孤木前輩弄了個 Diggirl.Net 出來,服務的內容... 恩... 連過去玩一圈就知道了。於是,就搞得一些相簿的主人不開心了!

其實,我也認為任何資料只要放到網路上(應該說,資料公開以後),就不能再做什麼進一步的禁止行為了。因為網路本身是一種類似「群播(broadcast)」的機制,放出去的東西就是放出去了,沒什麼好限制的。就好像你想到台北車站前面大喊「我腦殘啊!」,卻又不想讓任何人聽到,是不可能的事情一樣。所以,圖片如果不想流出去,就好好收在自己的硬碟裡吧!

Okay... 以上是前言,接下來進入我實際想到的東西。

也許,在某些時候,你會希望能在相簿上分享某些圖片,卻又只想給指定的某些人看到。其實有很多 PKI 早已行之有年,也在很多關鍵應用上扮演重要的角色。所以,應該也可以應用到這個狀況上來!

讓我們拿 Alice 跟 Bob 來當例子,他們是我眾多虛擬朋友中的兩位:

Alice 想傳一些裸照給 Bob,可是又怕被別人看。因為有幾萬張照片要傳,用 email 又太花時間了。於是,她決定選用由 Palatis Inc. 所開發的圖片加密函式庫!

首先,他使用程式提供的兩套軟體,產生適當的 keypair,並使用該 keypair 將資料加密:

  1. 使用函式庫提供的 keygen 產生 keypair (pubkey, prikey)
  2. 將自己的裸照 (naked*.{jpg,png,gif,bmp,tiff, ... }) 經程式轉為 bitmap (naked*.bmp),並使用 pubkey 加密以後,轉回 png 一類非破壞性壓縮的圖檔 (naked*_secured.png)。於是,轉出來的東西用其他圖形函式庫看起來,就像是白胡椒鹽撒在黑胡椒鹽(... 總之就是胡椒鹽)上一樣。

再來,將這些胡椒鹽照片傳送到適合大量下載的網路空間(如網頁、相簿、ftp、bt)上。

使用另外的管道(如 email)將 prikey 送給 Bob,並祈求 Bob 不會把她的裸照洩漏出去。當然,該 email 會用其他軟體(如 GnuPG)加密。(或簽章,否則 Bob 可能也不知道收到的是否真的是 Alice 的裸照。)

Bob 本來去 Alice 的相簿看到的只是一堆胡椒鹽的裸照,但是當他拿到 Alice 送來的 prikey 以後,世界頓時亮了起來!

  1. Bob 把 Alice 給他的 prikey 放到函式庫搜尋 key 的資料夾(如 /home/bob/.libsecreatpic/received_keys/)底下。
  2. 然後用他心愛的網頁瀏覽器(順便廣告一下 Firefox!)開啟 Alice 的相簿。

Yeah~胡椒鹽都不見了,取而代之的,是 Alice 性感火辣(?)的曲線!

至於實做細節的部份,因為沒空,所以以後再想 *flee*。

星期六, 1月 06, 2007

Hidden 火星文++

之前提到我們學校某教授在個人網頁上弄了這個東西,今天又因緣際會的去到了他的網站。耶~他把那篇文章拿掉了耶!

可是... 直接輸入 http://seafood.csie.cyut.edu.tw/seafood/HSIEN.htm 的話,還是連的到說... 因為看起來的跟之前一樣,所以就不重複貼了。

恩... 這就是所謂的「眼不見為淨」嗎?

星期一, 1月 01, 2007

libata - 新的 ata 子系統!

赫然發現我的 Linux 還在用舊的 ATA Subsystem,而使用 SCSI Layer 的 libATA 已經出來很久了!

所以,今天來升級!

因為我是個懶得作 initrd 的懶人,所以底下的支援都編進核心。如果您要編譯成模組,則必須自己想辦法作 initrd。

Device Drivers --->
    SCSI device support --->
        <*> SCSI disk support
    Serial ATA (prod) and Parallel ATA (experimental) drivers --->
        <*> ATA device support
        // 這邊請選與您 IDE 晶片對應的驅動程式
        <*>   ATI PATA support (Experimental)
        <*>   Generic ATA support

然後去 /boog/grub/grug.conf 與 /etc/fstab 或其他設定檔中,把用到 hdXY 的地方統統改成 sdXY 就可以了!

不知道用新的 ATA Layer,會不會有感覺得到 performance boost?

sdparm 的使用方法徵求中!