星期五, 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 的使用方法徵求中!

星期三, 12月 27, 2006

擠壓你的檔案系統

/***********************************
大肥警告!
Big Fat Warning!

這篇文章裡提到的技術可能讓您的資料爆炸,請斟酌使用!
The techniques introduced in this article may blow your data, use them at your own risk!
***********************************/

最近總覺得硬碟很慢... 應該說,作一些需要磁碟 I/O 的動作的時候,感覺硬碟一直嘎啦嘎啦的上氣不接下氣。所以有了這樣的想法:

  1. 現在 CPU 速度呈指數成長
  2. 磁碟傳輸速度呈線性成長(大部分的瓶頸在磁頭讀寫動作)

既然如此,何不適當的要求 CPU 作多一點計算,減少磁碟 I/O 呢?於是就想到要壓縮檔案系統。

可是... 要怎麼壓縮呢?可以從兩個地方著手:

  1. Loop Device Level:使用如 cloopcompFUSEd 等辦法,在 regular filesystem 的 regular file 上再多加一層間接層來壓縮。
  2. Block Device Level:在 Device Driver 加上一層間接層來壓縮,例如使用 dm-cryptcryptsetup-luks
  3. Filesystem Level:讓 Filesystem Driver 來負責處理壓縮,例如使用 jffs2、ext2compr 或 Reiser4experimental cryptcompress plugin

我的想法是,我是要來增進磁碟效能的,所以不應該搞一大堆間接層來拖慢系統速度,所以 cloop 那種東西就根本不考慮了。後來又想從 Block Device Level 來做的,可是 dm-crypt 與 cryptsetup-luks 非常悲慘,還真的只能作 CRYPTographic,沒辦法做 COMPRESSion。我嘗試用 cryptsetup -c lzf luksFormat 來建立 lzf compressed block device,可是殘念的失敗了 XD~

於是,只好讓 Filesystem 來壓啦!可是 jffs2 的間接更多,必須先有個 loopfile map 到 mtd(Memory Technology Device)上,再 mount mtdblock 來用。處理起來速度比沒壓縮的 ext3 還慢,所以... 還是算了吧!而 ext2compr 這個東西實在太老了,只有在 google 搜尋的時候隱約發現他的蹤跡,可是真的想要找 howto 的時候卻什麼也找不到。所以... 這個也算了吧 = =

最後的救星是 Reiser4 的 Cryptcompress plugin,也是要 patch kernel source,也要升級 reiser4progs。

安裝的方法比想像中簡單的多。首先,你需要一個支援 Reiser4 與 cryptcomress 的 kernel,再來就是支援 cryptcompress 的 {mkfs,fsck,debugfs,measurefs}.reiser4。讓我們從 kernel 開始:

// 如果您已經有裝 app-portage/layman,可以省略這個步驟:
# emerge app-portage/layman
// 如果您已經有加入該 custom-kernels overlay,可以省略這個步驟:
# layman -a custom-kernels
// 最後,這是支援 reiser4 與 cryptcompress 的 kernel:
# emerge sys-kernel/beyond-sources

然後,在 make *config 的時候,把 reiser4 support 打開(廢話!),編譯您的新 kernel,然後用它開機。

再來是 userspace tools - 新版的 reiser4progs。這個我沒有找到任何 overlay 裡面有(其實它也還沒有正式 release),所以先貼在這吧!

# Copyright 1999-2006 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# $Header: /var/cvsroot/gentoo-x86/sys-fs/reiser4progs/reiser4progs-1.0.5.ebuild,v 1.5 2006/08/20 21:23:43 vapier Exp $

inherit toolchain-funcs

MY_P=${PN}-${PV/_p/-}
DESCRIPTION="reiser4progs: mkfs, fsck, etc..."
HOMEPAGE="http://www.namesys.com/v4/v4.html"
#SRC_URI="ftp://ftp.namesys.com/pub/reiser4progs/${MY_P}.tar.gz"
SRC_URI="ftp://ftp.namesys.com/pub/tmp/cryptcompress/2.6.19-rc4-mm1/${MY_P}.tar.gz"

LICENSE="GPL-2"
SLOT="0"
#KEYWORDS="amd64 ppc ppc64 -sparc x86"
KEYWORDS="~amd64"
IUSE="static debug readline"

DEPEND=">=sys-libs/libaal-1.0.5
       readline? ( sys-libs/readline )"

S=${WORKDIR}/${MY_P}

src_unpack() {
    unpack ${A}
    cd "${S}"
    # bundled libtool sucks, so rebuild autotools #74817
    aclocal && libtoolize -c -f && autoconf && automake || die "autotools failed"
    cat <<-EOF > run-ldconfig
        #!/bin/sh
        true
    EOF
}

src_compile() {
    econf \
        $(use_enable static full-static) \
        $(use_enable static mkfs-static) \
        $(use_enable static fsck-static) \
        $(use_enable static debugfs-static) \
        $(use_enable static measurefs-static) \
        $(use_enable static cpfs-static) \
        $(use_enable static resizefs-static) \
        $(use_enable debug) \
        $(use_with readline) \
        --enable-libminimal \
        --sbindir=/sbin \
        || die "configure failed"
    emake || die "make failed"
}

src_install() {
    make DESTDIR="${D}" install || die
    dodoc AUTHORS BUGS CREDITS ChangeLog NEWS README THANKS TODO
    #resizefs binary doesnt exist in this release
    rm -f "${D}"/usr/share/man/man8/resizefs*

    # move shared libs to /
    dodir /$(get_libdir)
    mv "${D}"/usr/$(get_libdir)/lib*.so* "${D}"/$(get_libdir)/ || die
    gen_usr_ldscript libreiser4-minimal.so libreiser4.so librepair.so
}

把這個檔案放到 /usr/local/portage/sys-fs/reiser4progs/reiser4progs-1.0.6.ebuild,然後...

# cd /usr/local/portage/sys-fs/reiser4progs
# ebuild reiser4progs-1.0.6.ebuild digest
# emerge reiser4progs -va

編完以後,就可以來作新的分割區了!別忘了將各參數改成您的設定。

// 先將檔案備份起來
# cd /
# tar cp /path/to/mounted_dir > /path/to/backup_file.tar
// 卸載檔案系統
# umount /dev/hdXY
// 建立檔案系統(使用 lzo 壓縮演算法)
# mkfs.reiser4 -o create=ccreg40 /dev/hdXY
// 建立檔案系統(使用 gzip 壓縮演算法,較耗費 CPU)
# mkfs.reiser4 -o create=ccreg40,compress=gzip1 /dev/hdXY
// 掛載檔案系統
# mount /dev/hdXY
// 還原備份檔
# cd /
# tar xf /path/to/backup_file.tar
# sync

在 umount 之前可以先 df 紀錄用掉的空間,跟改用 cryptcompress 以後比較看看。我的狀況是 855M 變成 524M,省了 331M 的空間。

星期日, 11月 12, 2006

用 webSnarp 在網頁上顯示小預覽圖

webSnarp 2.0 是個提供網頁預覽圖服務的網站,詳情請上該網站去了解... 底下有提供一些小玩具,可以在網頁上加上一些特殊功能,讓滑鼠經過連結的時候跳出該連結的預覽圖。

但是我試圖將它套用在 Blogger 上的時候,若是想在以前的文章上也套用該功能,就必須去修改之前的所有文章。想當然我沒那麼勤勞,所以就把其中 Link Preview V2 提供的 JavaScript 改了一下,放在這裡,有需要的人可以去下載來用。

不過有一點需要注意,因為是寫來套用在 Blogger Beta 上的舊文章的,所以我讓他掃描所有的網址,並依照一定的規則排除要跳出預覽圖的網址。詳情請見 websnapr.js 內的「excludeDomains」與「excludeExtensions」兩個變數。

這是適合 Blogger beta 的版本,請不要套用在其他地方。安裝的步驟如下:

  1. 這裡 下載 websnapr.tar.bz2
  2. 修改「websnarp.js」中的「myBaseURI」變數至您的網頁空間
  3. 將所有檔案放到自己網頁空間下,記得要放在同一個資料夾裡!
  4. 去 Blogger 範本中修改 html 的地方,找到 <title> 標籤,在底下插入: <style media='screen' type='text/css'>@import "[您的網頁空間]/websnapr.css";</style>
    <script src='[您的網頁空間]/websnapr.js' type='text/javascript'/>
最後,顯示出來的效果就像這樣

星期日, 10月 29, 2006

COSCUP 2006~~

COSCUP 2006 晃了一圈回來,感覺功力增進了不少!不同於那個偽.OSS 社群本次的議程採雙軌制,一邊以技術議程為主,另一邊則是從 User && Promotor 的角度切入。會外還附點心、午餐、跟晚餐,最重要的是「這些統統都是免費的」!!!話雖如此,由於這次的主辦單位透露經費還是赤字,所以我丟了 TWD$100 進捐款箱...(是... 我知道很少... 我知道我吃了很多點心、喝了很多飲料、還拿了個便當......)

想當然爾,我聽的當然全都是技術議程啦!(程式設計之神來演講,去聽什麼 Open Source 數位生活還是Open Source 最佳實踐的,實在太浪費了!)我聽的是「中文輸入工作坊」、「羽量級桌面」、與「Web-Oriented Architecture」三項,議題內容就不再贅述,只提一下自己覺得有趣的東西好了~

首先一開始介紹的輸入法技術,真是救了我一條命 @@ 我的畢業專題是 OSS 的手寫輸入法(目前有一點點 code 可以從 svn checkout),而今天的議程有提到許多數學模型與相關演算法,給了我個大方向!其中提到目前中文分詞技術的進展,講了許多技術性的議題,非常有意思。

接下來的羽量級桌面,jserv 與 pcman 發表了一堆好玩的東西,包括如何從根本的設計上改善桌面效能,還有 FreeDesktop.org 這個邪惡組織公佈的黑心標準有多可惡怎樣讓效能好不起來。

然後是 Web-Oriented Architecture,介紹了目前很紅(比 Britney Spears 還要紅上好幾倍)的 Web 2.0。先前有在構思一個線上翻譯網站,其技術實現的部份,就很想朝 Web 2.0 與 AJAX 的方向走。而為了縮短開發時間,應該會使用某些既有的 Framework,如 Ruby on RailsCakePHP 等。雖然目前已經申請了專案網頁,但是目前還沒有開始動手作。想想,應該也是時候開始了,就在期中考週有空的時候 來作吧!

另外... 必須對 jserv 的筆記型電腦表達哀悼之意,它被 PingYeh 踢到地上以後,就不會開機了...

總覺得,來這裡聽一天演講,學到的東西比去學校聽黃國安陳金鈴劉啟東陳宏達這些爛教授上課 自己在家猛 k 文件 得到的東西多的多!

星期五, 10月 06, 2006

愛因斯坦他老師

今天上通識課(中國經籍與說話藝術)的時候,老師跟我們講了個愛因斯坦的故事...

大家都知道愛因斯坦小時候是個不討老師喜愛的學生吧?有一次上勞作課的時候,老師要求每個同學作一張小板凳當作業。當愛因斯坦交出他所作的板凳給老師的時候,老師對他說:「我想,在這個世界上,大概找不出比這張板凳更爛的作品了。」

但是「不,」愛因斯坦說道,「老師你錯了!」。接著,他從旁邊拿出另外兩張板凳,「這張是我第一次做的,這張是第二次做的。」

說完這個故事,老師要求我們在一張紙上寫下一句話的啟發,並交上來當成一次課堂成績。於是我寫下了...

  • 當老師的不要太機車。

就交過去了......

哎呀... 真糟糕... 不知道會不會被當 = =

星期二, 9月 19, 2006

你會上網買新台幣 $236,600,000 的東西嗎?

知名網路公司 PCHome 在網路上賣遊艇耶!

有誰會在網路上買一台價值超過兩億三千萬的遊艇呢?即使買了,要怎麼付錢呢?如果刷卡,有什麼卡一個月可以刷六百五十萬(36 期 0 利率)的嗎?如果有,我還真想知道 XD~~

照 PCHome 標錯價格的機率,等他少標個幾個零就可以趕快去下標了。

星期六, 9月 16, 2006

圖書館排擠我,不准我推薦書~

我們學校圖書館有個立意甚好的服務,叫做「線上圖書薦購系統」。可是這個立意甚好的服務,被這個兩光的網頁設計公司(好啦,我不知道這個是外包給外面的廠商,還是「外包」給我們學校資管系的...)一搞,變得亂七八糟...

怎麼說是亂七八糟呢?因為...

  1. 縮排亂七八糟,想 trace 還要先整理一次。
  2. 檔頭趾高氣昂的說自己是 XHTML,可是卻用了一堆 HTML 的 tag(如 center、font... 等等)。(不能因為是 Transitional 就這樣惡搞吧?)
  3. 使用 VBScript 作為 User 端的驗證,搞得只有用 IE 的使用者有辦法推薦...

我只是百無聊之中想看一下 ARM(The Annotated C++ Reference Manual),所以想說去圖書館找找,卻找不到這本書,想讓他們下次買個幾本放著生菇。不能推薦實在不方便,於是寫了封信給圖書館...(個人資料部份消音)

Subject: 圖書薦購系統能不能不要使用 VBScript 作驗證?

辛勞的圖書館採編組工作人員您們好,

能不能不要用 VBScript 作用戶端資料驗證?因為敝人並非使用微軟作業系統,沒有 Internet Explorer。如果有需要,我可以寫好有相同功能的 JavaScript 讓您替換就好。

因為此 VBScript 驗證碼的緣故,造成敝人在推薦書籍時的不便,所以懇請您們換用其他方法。

諸多感謝!
**同學,您好!

先謝謝你的來信建議!

圖書薦購系統開發之初,由於上線時間急迫,確實僅能以大多數用者作業環境為優先考量,以求立竿見影之效。在本館戮力於提供更便利、多元、精緻的服務過程中,"人力" 問題則是我們經常感到捉襟見肘之處;若你真願意義務性提供協助,我們則熱誠的歡迎,一起來嘗試並努力,以期能縮短過程、增進時效。

後續就有請你主動與本館資訊組**小姐聯繫
(分機:****, mail address :   *****@cyut.edu.tw)。

謝謝您!!

圖書館採編組 敬上

基本上,我實在看不出什麼原因會導致上線時間急迫就必須採用 ASP+VBScript。說實在 Server 端用什麼系統跟 Client 端一點關係也沒有,反正 Server 就那麼一台(一群?),關起門來愛怎麼搞都沒差。不過丟出來給 Client 的東西,難道不值得多考慮一下嗎?還是因為「上線時間急迫,所以省去評估的時間,直接下去 Coding」?

我一直以為,「網路服務」應該要以「能服務越多客戶越好」為目標。採取開放規格開發出來的東西,總是能比較簡單的符合各式客戶的需求。無論如何,支援 JavaScript 的瀏覽器再怎樣也比支援 VBScript 的多吧?

好啦,可能廠商要賺錢,賣 MS 的東西賺比較多,所以最後搞出來這個亂七八糟的系統吧 = =

測試編譯器功力的九九乘法表原始碼

最近迷上 template meta-programming,所以百無聊之中(相對於百忙之中...)寫了個入門等級的小程式。類似功能的程式應該很多程式設計師還沒出茅廬的時候就都寫過了吧!

為什麼說是測試「編譯器」的功力呢?因為這個程式用到了一些高階的 template 功能,並不是所有 compiler 都支援。如果您手上的編譯器是 VC6 的話,請去 Program Files 底下把它拖曳進資源桶,然後改用 dev-c++(它使用 GCC 3.4.2)。使用 GCC 4.1.1(我是用這個編譯、測試的)是完全沒有問題的 :)

本來想稍微講解,可是想想又覺得沒什麼好說的,畢竟只是無聊的小作業 = = 如果有什麼問題,歡迎留言、MSN、Email、電話討論。以下附上程式碼!


/*
 * Meta-programming Infrastructure
 */

struct NullType { };

template <typename Head, typename Tail>
struct Typelist { };

/**
 * meta-(data structure) to store "X x Y = Z"
 */

template <unsigned int X, unsigned int Y>
struct ChartNode
{
    enum { first = X, second = Y, product = X * Y };
};

/**
 * Chart Maker...
 *
 * @param X, Y: current X and Y
 * @param XN, YN: max X and Y
 */

template <unsigned int X, unsigned int Y, unsigned int XN, unsigned int YN>
struct makeChart
{
    typedef Typelist< ChartNode<X, Y>, typename makeChart<X+1, Y, XN, YN>::Result > Result;
};

template <unsigned int Y, unsigned int XN, unsigned int YN>
struct makeChart<XN, Y, XN, YN>
{
    typedef Typelist< ChartNode<XN, Y>,
        Typelist< NullType, typename makeChart<1, Y+1, XN, YN>::Result > > Result;
};

template <unsigned int XN, unsigned int YN>
struct makeChart<XN, YN, XN, YN>
{
    typedef Typelist< ChartNode<XN, YN>, NullType > Result;
};

/**
 * Algorithm to write a ChartNodeList to an Ostream
 */
template <typename TList>
struct toOstream;

template <typename X, typename XS>
struct toOstream< Typelist<X, XS> > : toOstream< XS >
{
    template <typename OST, typename DELIM>
    OST & operator() (OST & dest, DELIM delim1, DELIM delim2)
    {
        dest << X::first << "x" << X::second << "=" << X::product << delim1;
        return toOstream<XS>::operator()(dest, delim1, delim2);
    }
};

template <typename XS>
struct toOstream< Typelist<NullType, XS> > : toOstream< XS >
{
    template <typename OST, typename DELIM>
    OST & operator() (OST & dest, DELIM delim1, DELIM delim2)
    {
        dest << delim2;
        return toOstream<XS>::operator()(dest, delim1, delim2);
    }
};

template <>
struct toOstream<NullType>
{
    template <typename OST, typename DELIM>
    OST & operator() (OST & dest, DELIM, DELIM delim2)
    {
        dest << delim2;
        return dest;
    }
};

/*
 * Main Program...
 */
#include <iostream>
#include <iterator>
using namespace std;

int main()
{
    enum { x = 9, y = 9 };
    cout << "make a Chart from [(1x1=1) .. (" << x << "x" << y << "=" << x*y << ")]:" << endl;

    typedef makeChart<1, 1, x, y>::Result ChartList;
    toOstream<ChartList> generator;
    generator(cout, ", ", "\n");

    return 0;
}

整個演算法包裝成一系列的 template objects,最後再丟給 toOstream<> 這個唯一有被具現化的 template object,其他程式碼全部都在編譯時期被展開了!帶入 x = 9, y = 9 的結果:

$ ./ninenine
make a Chart from [(1x1=1) .. (9x9=81)]:
1x1=1, 2x1=2, 3x1=3, 4x1=4, 5x1=5, 6x1=6, 7x1=7, 8x1=8, 9x1=9,
1x2=2, 2x2=4, 3x2=6, 4x2=8, 5x2=10, 6x2=12, 7x2=14, 8x2=16, 9x2=18,
1x3=3, 2x3=6, 3x3=9, 4x3=12, 5x3=15, 6x3=18, 7x3=21, 8x3=24, 9x3=27,
1x4=4, 2x4=8, 3x4=12, 4x4=16, 5x4=20, 6x4=24, 7x4=28, 8x4=32, 9x4=36,
1x5=5, 2x5=10, 3x5=15, 4x5=20, 5x5=25, 6x5=30, 7x5=35, 8x5=40, 9x5=45,
1x6=6, 2x6=12, 3x6=18, 4x6=24, 5x6=30, 6x6=36, 7x6=42, 8x6=48, 9x6=54,
1x7=7, 2x7=14, 3x7=21, 4x7=28, 5x7=35, 6x7=42, 7x7=49, 8x7=56, 9x7=63,
1x8=8, 2x8=16, 3x8=24, 4x8=32, 5x8=40, 6x8=48, 7x8=56, 8x8=64, 9x8=72,
1x9=9, 2x9=18, 3x9=27, 4x9=36, 5x9=45, 6x9=54, 7x9=63, 8x9=72, 9x9=81,

令人驚豔的囉唆... @@

星期日, 8月 20, 2006

這東西只要 USD $18,500!

之前提到過,使用 Quantum-Optimized CPU 的 Super Notebook,一台只要 $18,500!

規格好像有點變化...

  • 中央處理器:AtomChip® Quantum® II @ 6.8GHz
  • 主記憶體:AtomChip® Quantum-Optical 非揮發性記憶體 1TB
  • 螢幕: 12.1" WXGA (1280x800, 16:10) TFT 背光 LCD 顯示器
  • 顯示晶片:整合式圖形控制器 [ 支援 AtomChip® DVM(動態顯示記憶體)]
  • 輔助儲存媒體:AtomChip® Quantum Storage 2TB
  • 光碟機:DVD Super Multi
  • 網路介面:802.11 無線網路 [ Intel® PRO/Wireless 2100/2200GB/2915ABG ]、藍牙、GPS、LAN、Modem
  • 連線介面:IEEE1394A x 1、USB 2.0 x 3、SVGA-out、立體聲耳機孔
  • 插槽:PCMCIA x 1、四合一讀卡機 [ SDIO、SD、MS Pro、MS ]
  • 其他週邊:內建 1.3 百萬像素相機、立體聲喇叭、麥克風
  • 輸入介面:多媒體鍵盤 [ E-Mail、Internet、Capture、WLAN 按鍵 ]、內建 4 方向捲動按鈕的 Synaptics 觸控版
  • 大小:(寬)320.0 x (長)242.0 x (高)22.0mm/28.0mm (前/後)
  • 重量:約 4.18lb (1900g)
  • 作業系統:Windows XP 或 Linux

不知道什麼時候開始出貨...