嗯... 廣告文 XD~
這兩天把散落一地的 code 整理了一下,作成了可以放到 /usr/lib 底下的 library。又另外拼湊了一個 Trainer 出來,看起來比較「動感(interactive)」一點,可以 online 告訴你辨識率(這樣感覺比較像有在辨識)。
想玩玩看嗎?請見 Chrasis 首頁!
嗯... 廣告文 XD~
這兩天把散落一地的 code 整理了一下,作成了可以放到 /usr/lib 底下的 library。又另外拼湊了一個 Trainer 出來,看起來比較「動感(interactive)」一點,可以 online 告訴你辨識率(這樣感覺比較像有在辨識)。
想玩玩看嗎?請見 Chrasis 首頁!
即使我已經無所不用其極的想辦法讓程式載入的速度快點,但是碰上 Firefox 這頭怪獸,把他從磁碟裡拖到螢幕上裡還是得花個兩三秒。
後來,我發現 Firefox 會嘗試在啟動的時候(程式已經啟動、但是視窗跳出之前)更新附加元件與搜尋引擎。這麼一來,若有更新,可以不必在你已經開了一堆視窗以後,跳個對話方塊出來要求你重新啟動 Firefox。
但是,由於網路的 latency,檢查更新的速度快則半秒,慢則一兩秒,可以說在我 Firefox 的啟動過程中,佔掉了 20~50% 的等待時間。於是,我決定將它關掉,並安裝另一個 extension 來檢查更新。(反正又不是一天到晚有更新檔要裝,但是我可是一天到晚都會關閉、開啟 Firefox 啊!)
怎麼關呢?請至「編輯 => 偏好設定 => 進階 => 更新」下,將「自動檢查是否有更新」下的所有東西(應該有三個,分別是「Firefox」、「以安裝的附加元件」、「搜尋引擎」)取消選取。如下圖所示:
可是這樣 Firefox 不就不會幫我更新這些東西了嗎?沒關係,讓我們裝上「Update Notifier」來負責這件苦差事:
這麼一來,每次啟動 Firefox 至少又可以快個 0.5 秒啦!
美勞嵌入式系統期中考時,有類似這樣的題組:
# 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 的宣傳影片好了...
前幾天(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~
搞成這樣我也不知道該說什麼,不過老師最後還是給我們對,但是時間就不知道該怎麼算了...
「如果你那麼想玩(ARM9 的)板子,可以自己去買。」
朝陽科技大學 95 學年度
資訊工程系「嵌入式系統」課程
講師 - 陳宏達
我們系上本學期的選修課之一「嵌入式系統」,預定的開發環境是 Embedded Linux。
無可厚非的,前幾次上課是在作「嵌入式系統」與「Linux 基本操作」的簡介,恩... 考慮到他幫忙推廣 Linux,就... 先算了吧。不過,他還是宣導了許多不知道哪裡發明的謬論...
「你們可以先去下載 VMWare 回來灌(Linux),不過他要註冊,而且只能用 30 天。不過我知道你們一定很有辦法,去一些大陸網站找一下就可以永遠試用下去。」
(我的天啊!身為一個資訊工程系的教師,不但避免使用無授權的軟體,還在課堂上變相要大家想辦法找破解?拜託,資訊工程系的學生,以後出社會可是要寫軟體的。如果連自己都使用盜版軟體,以後寫出來的產品要賣給誰?)「你們在灌(Linux)的時候,只要用文字介面就好。高手都用文字介面,只有新手才需要圖形介面。」
(怎樣?把全世界的 Linux 桌面使用者都當新手是吧?唔... 沒有啦,我承認我是新手 XD~)「Linux 下無法建立以 "-" 開頭的檔案,也不能建立中間有 "*"、"?" 或 " "(空格)的檔案。」
(我已經打算,他如果在期中考出這題,又把我改錯,我就當場問他「如果我現在建出來,是不是全班送分?」。)
好,這些都可以算了,畢竟不關我的事。第一,我電腦裡沒有盜版軟體;第二,我是 Linux 新手(XD~);第三,我知道怎麼建立這些檔案。
可是,都已經要期中考(意思就是開學到現在經過了約兩個月了)了,大家還沒看到板子長什麼樣子。於是我就問了...
「老師,請問到底什麼時候可以玩到板子?」
「Linux 都不會用就想玩板子?」
「我已經用一陣子,也大概會用了。」
「還有 Toolchain 啊,那個都沒弄好就想玩板子?」
「那個我已經都編好等著了。」
「總是要配合其他同學的進度啊!」
「......」(幹,我繳學費來學校,是為了配合大家的進度?)
「如果你真的那麼想玩板子,可以自己去買。」
「............」(幹,如果我去買了塊板子放家裏,還需要來上你的課?)
接著,又說了些...
「有一個研究生學長,為了專題,花了十多萬買硬體。」
「普通的開發版,大概一兩萬就買的到了。」
其實想想,從小到大...
以此類推...
似乎還蠻合理的!
幹,宏達老師,您怎麼不多感冒幾次?
恩... AI 的作業 - A* Search Algorithm。
老師是要我們寫八陣圖啦!不過我看到 A* 的時候想,其實 A* 需要的也就是這些東西:
所以,我把 code 提煉成一個 template function,只要餵他一個 start 一個 target,他就會把中間要走的路找出來。而這個 template function 需要:
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!
昨天(March 26, 2007)中午左右,在我回味老動畫 - MADLAX 的時候,突然螢幕一黑,伴隨而來一陣燒焦的味道。我首先以為是顯示卡燒了,所以馬上去按著主機上的 Power Button 四秒(感謝 Journaling Filesystem 的加持,沒有 dataloss。)然後又想到也可能是螢幕的問題,所以馬上又伸手按下螢幕的電源開關。
後來靠近聞主機屁股,並沒有半導體的焦味,再去聞一下螢幕... 果然是 AG Neovo E19 A 燒了...... 幸好機板跟面板都是三年保固,May 2004 買的,差兩個月就過期了 = = 趕快打他網頁上的電話,問他要怎麼辦。
AG Neovo 的處理還蠻迅速的,昨天下午打電話,今天上午在我睡夢中就來拿了。不過他沒有收件的單子給我,也不知道要多久才會送回來,看來我這兩個禮拜電話要開著了 @@
所以,這台電腦現在完全從 MultiMedia Center + Server 退化成 Server 了 QQ,主要跑的服務剩下:
最近我的筆電動不動就 kernel panic,也不知道到底出了什麼問題。想看 backtrace 也不行,因為總是在 X 底下死掉,沒有 console 讓我看 log。而 kernel panic 的時候 print 出來那些東西,也不會跑進 syslog 裡。所以啦,只好讓他 log 到 remote 了...
其實,在 Gentoo 要用 netconsole 是很簡單的。因為是 "NET"console,所以當然會有一台 client 一台 server。首先假設我們的環境看起來像這樣:
首先在 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!
編輯 /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 傳來的訊息!
那就這麼作吧!
// 首先當然是關掉你要測的那個程式, 別忘了先把重要資料存檔
# killall -9 firefox-bin
// 呼叫 sync 把重要資料寫入硬碟
# sync
// 然後清掉 buffer/cache
# echo 3 > /proc/sys/vm/drop_caches
// 重新啟動(Enjoy the waiting :P )!
$ firefox
奇怪,怎麼都沒人想到呢?
先前提到說,適當的將檔案系統壓縮,可以提高磁碟的存取效率 - 尤其是 CPU 速度與 IO 速度差很多的時候。雖然沒有完整的數據顯示到底快多少,不過 RANMA K 兄有初步的測試過不壓縮與壓縮時的寫入、讀取速度。況且還有個良好的副作用 - 同樣大的硬碟空間裡,可以裝的東西更多了,而且完全不會變重!
根據用屁股思考就可以領悟的常識,我們知道:
所以,希望有一種檔案系統的,會根據檔案的狀態來選擇性的壓縮檔案,大概像這樣:
當然,這個檔案系統要有現代檔案系統該有的功能,例如:
如果有相關的資料或論文,還請不吝留個言... 多謝!
過年嘛,總是要掃除一下。雖然我沒有想要打掃的意思,但是還是把櫃子裡的東西拿出來,東翻翻西翻翻以後再放回去,假裝有整理過些什麼。
翻一翻,找到了國小五年級的作文簿!真感謝我媽沒把他當資源垃圾拿去回收,讓我看到我以前有多麼無厘頭。下面還只是其中一篇(其實我懶得打字,而且其他幾篇寫的還蠻丟臉的 |||>_<b)...
冬天
今天是冬天。(啊!說錯台詞!?現場直播!不能改了。繼續編下去吧!)那一顆松是在搖。對,他在姚。它是咖啡色的,葉子也是。很醜。對!很... 什麼?很醜?你... 算了。我有一個故事,我們來分享一下:
在一個大雪紛飛的冬天,在那個森林裡...。「那個」森林是什麼東東?就是「這個」森林!有一對父子,在打獵。嗯!廁所在哪裡。廁所?你說「廁所」做什麼?你「嗯!」不是要「便」魔術嗎?你不必管!繼續說!經過一天,也打不到東西,卻在森林中迷路了。在那時,他們看到一間小屋,就近去了。這時,他們因為很累,就睡了下來。到了半夜,兒子看到了一個「石膏色」的女人,從們旁邊的牆中飛了進來。向他爸爸吹了一口氣。他爸爸的血液就結冰了!雪女說:「我不要你的命。但是你把今天的事情說出去,你也是我牙下的客人。」說完,就走了。這個小男孩長大以後,在十二月十二日的晚上十二點十二分十二秒,遇到了一個名叫「小雪」的女孩。這個小男孩就和這女孩結婚了。在洞房的那一天,那個小男孩說:「我...,」那小雪的眼睛為之一亮!「就是『怪歐吉桑』!」結果,在外面的爸爸靈魂跌了一跤「掉」進來,這場戲再也演不下去了。
完!
現在看看,發現...
PS. 『怪歐吉桑』是志村大爆笑裡,由志村健飾演的那個怪叔叔。
最近獨孤木前輩弄了個 Diggirl.Net 出來,服務的內容... 恩... 連過去玩一圈就知道了。於是,就搞得一些相簿的主人不開心了!
其實,我也認為任何資料只要放到網路上(應該說,資料公開以後),就不能再做什麼進一步的禁止行為了。因為網路本身是一種類似「群播(broadcast)」的機制,放出去的東西就是放出去了,沒什麼好限制的。就好像你想到台北車站前面大喊「我腦殘啊!」,卻又不想讓任何人聽到,是不可能的事情一樣。所以,圖片如果不想流出去,就好好收在自己的硬碟裡吧!
Okay... 以上是前言,接下來進入我實際想到的東西。
也許,在某些時候,你會希望能在相簿上分享某些圖片,卻又只想給指定的某些人看到。其實有很多 PKI 早已行之有年,也在很多關鍵應用上扮演重要的角色。所以,應該也可以應用到這個狀況上來!
讓我們拿 Alice 跟 Bob 來當例子,他們是我眾多虛擬朋友中的兩位:
Alice 想傳一些裸照給 Bob,可是又怕被別人看。因為有幾萬張照片要傳,用 email 又太花時間了。於是,她決定選用由 Palatis Inc. 所開發的圖片加密函式庫!
首先,他使用程式提供的兩套軟體,產生適當的 keypair,並使用該 keypair 將資料加密:
- 使用函式庫提供的 keygen 產生 keypair (pubkey, prikey)
- 將自己的裸照 (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 以後,世界頓時亮了起來!
- Bob 把 Alice 給他的 prikey 放到函式庫搜尋 key 的資料夾(如 /home/bob/.libsecreatpic/received_keys/)底下。
- 然後用他心愛的網頁瀏覽器(順便廣告一下 Firefox!)開啟 Alice 的相簿。
Yeah~胡椒鹽都不見了,取而代之的,是 Alice 性感火辣(?)的曲線!
至於實做細節的部份,因為沒空,所以以後再想 *flee*。
之前提到我們學校某教授在個人網頁上弄了這個東西,今天又因緣際會的去到了他的網站。耶~他把那篇文章拿掉了耶!
可是... 直接輸入 http://seafood.csie.cyut.edu.tw/seafood/HSIEN.htm 的話,還是連的到說... 因為看起來的跟之前一樣,所以就不重複貼了。
恩... 這就是所謂的「眼不見為淨」嗎?
赫然發現我的 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 的使用方法徵求中!