2012年12月6日

How to hide the Apache version information in http header

edit the /etc/httpd/conf/httpd.conf file ( in Ubuntu server is /etc/apache2/apache2.conf )

and add

ServerTokens ProductOnly
ServerSignature Off

or edit if the option exist.

ServerTokens options and results:


ServerTokens ProductOnly

result:
Server: Apache

ServerTokens Major

result:
Server: Apache/2

ServerTokens Minor

result:
Server: Apache/2.0

ServerTokens Minimal

result:
Server: Apache/2.0.55

ServerTokens OS

result:
Server: Apache/2.0.55 (Debian)

ServerTokens Full (or not specified) default

result:
Server: Apache/2.0.55 (Debian) PHP/5.1.2-1+b1 mod_ssl/2.0.55 OpenSSL/0.9.8b


and hide the php information

edit /etc/php.ini

edit expose_php to Off

( expose_php = Off )

2012年11月16日

Amazon RedHat Instance ssh login


ssh -i /your/pem/path/file.pem root@ec2-xxxxx-1.ap-northeast-1.compute.amazonaws.com

use root...not ec2-user

>_<

2012年5月16日

HTC - 三星 - 蘋果




其實我完全不知道標題該怎麼下...

在此分享一篇文章

鴻海、宏達電  誰對台灣更重要?
http://www.cw.com.tw/article/article.action?id=5032848

我對財經是完全的門外漢
所以無法求證或給大家保證裡面數字都是對的
但商業週刊裡的文章應該不會離譜到哪

如果裡面所言屬實
我們是否該思考一下
買三星、蘋果的手機
會造成什麼樣的後果?

原本我也是半信半疑,愛用國貨有差這麼多嗎?
哪可能因為我一個人不買,明天台灣就垮掉了
但從這些現象看起來
還真的有可能...

撇開政治因素
HTC也為台灣帶來不少好處(這樣講應該對吧?肚子裡沒裝太多專有名詞XDD)
如果繼續助長三星、蘋果的勢氣
那真的只能說
『歡喜做,甘願受』
別怪薪水只有22k,也別怪薪水20年沒漲了....


我也不是在強迫大家要買哪家的產品
相信蘋果、三星這兩家廠商有各自的優點
才會吸引大批"粉絲"擁戴
這絕對是我們該學習的
但是選擇的當下
應該可以有另一種不一樣的思維來看待這件事!

2012年5月4日

為什麼我會買iPhone?



蘋果給大眾的印象就是,封閉、不自由、會被洗腦、盲從
半年前,我也還是一個很討厭Apple這字眼的科技人。

從 iPod 盛行的年代開始,我就一直視蘋果為非必要買進的科技產品
加上發現大部分拿Apple的人都有幾個特色
就是覺得這樣很潮、跟得上流行、買了產品,但卻不知道買來要幹嘛(很多人都喜歡在螢幕上滑來滑去,但其實我永遠不知道他們在滑什麼..........)
並且自從Android出來之後更會大肆批評其他品牌
這現象令人從心底討厭這產品

2011年 Steve Jobs 去世,眾多人為他悼念
當時的我純粹抱著死者為大的心態,祝福他離開這人世間
也慢慢接觸到他的傳記、演講、產品發表會...等等,有關於他的一切
發現他是一個很特別的人,他的人生也經歷各種起伏
他從Apple被趕出來到重新回去的那段,最能鼓舞我的心
也驗證了有失敗,就一定會有成功,這是個相對的世界。

那時,我便開始思考
如果一個產品很爛,有可能會出現這麼多支持者嗎?
難道賈伯斯以前是學催眠的嗎?
到底是什麼吸引了這麼多粉絲?
所謂的粉絲其實也是人,應該說到底是什麼吸引了那麼多人?
這些人還很愛用,聽說用了就不會想再換,很快就會變成忠實客戶

帶著這些好奇,我選擇用 iPhone 進入這神祕的世界
而他帶給我的感受
是使用了三 四年Android的我所無法想像的
的確,這是我們科技人所缺乏的設計感
的確,程式寫多了,腦子裡只剩下if then else的邏輯
一直到了現在,雖然我也有Android手機
但早就把主要使用的手機轉移到了 iPhone 上。


這世界很大
如果我們用拒絕的心看某一件事或某個角落
那我們就少接觸了另一種感觸
眼界就變得相對渺小了
不要小看這世界任何一個產品
就像Dropbox剛出來時我也覺得這沒什麼
只要寫過程式的人都會有類似的東西
但它的思維和我們寫程式的人不一樣
他就是要做給不會寫程式的人用的
所以不只在程式上下功夫
更是用心打造、包裝"產品"
如果我們早點學會開放自己的心
我們就能早點在這些發光發熱的產品上學習
而不是一直活在自己的世界

btw, 其實很多人討厭蘋果,都跟我一樣討厭的是"人",如果是這樣,那我會誠心的建議你去體驗一下蘋果的產品,但記住別變成自己曾經討厭的"人"。

2012年4月29日

創業的目的



http://mrjamie.cc/2012/04/30/phil-libin/

這篇文章真的寫得太好了
也讓我之前的疑惑有了解答

雖然文章裡說台灣為了錢創業的現象還好
但我身邊的人很多想創業都是為了錢

舉例來說
有參加過直銷說明會的人應該都知道
他們一貫的說法就是
這裡不是直銷,是在經營一個自己的事業!!
賺不賺錢,純看你的努力!!
有人可以月入十幾萬!!你一定也可以!!
結果就因為那句月入十幾萬就當了別人的下線
還沒月入十萬就先花了十幾萬幫人衝業績

這是一個很畸形的例子
因為這根本不是一個創業的行為
這就只是讓你跳進慾望的坑洞裡
讓妳為了錢賣命
最後一無所獲
甚至只會怪自己不夠努力才沒有收穫


如果是為了錢,Please DON'T do it.
好好當員工,領月薪,學會理財
可能還會賺得更多。

2012年4月26日

【JavaScript】Closure in for loop



我們通常會遇到以下情況

假設有個簡單網頁,所包含的元件如下

<ul>
    <li>123</li>
    <li>456</li>
</ul>

我們要給每個 li 加上一個 onclick 事件

滑鼠點擊後會 alert 順序的編號

為了讓程式聰明點

我們通常會使用 getElementsByTagName

和 foor loop 來 add event

例如:


var list = document.getElementsByTagName("li");
for( var i=0; i<list.length; i++){
list[i].onclick = function(){
alert(i);
}
}

但是這樣會發現一個嚴重的問題

每個 li alert的數字都一樣是2

為什麼?

因為當滑鼠按下時,做的動作是: alert(i);

而當時的 i 早就跑完迴圈變成 2 了

所以應該要這樣寫:


var list = document.getElementsByTagName("li");
for( var i=0; i<list.length; i++){
list[i].onclick = (function(index){
return function(){
alert(index);
}
})(i);
}

把重要的那部分拿出來研究一下:


        (function(index){
return function(){
alert(index);
}
})(i)

最外面的 ()(i) 的意思是執行裡面的 anonymous function

而裡面的 anonymous function 又 return 一個 function

return 的 function 做的事情是 alert(index)

index 又是  anonymous function 的 parameter

就是一個不被外部干擾的 Local variable

所以可以保證 alert 是我們想要的數字


一個看似成功的"手機"平台:Android


隱約記得,在我大三那年
我們學長就在玩著一堆Bug的Android
我還拿著我的Sony Ericsson K810i
只有試玩過那時候破爛破爛的windows mobile
當時對智慧型手機的看法就是:爛

一直到了去年
Android 修掉了大部分的Bug
把使用者感覺的到的問題都修正了
一切看起來很美好
到了Android 4.0 更是對UI做了大幅度的改進
但是Android隱藏的怪物
卻始終在那

不過說是怪物
不如說Android就是靠這賺錢的
如果這篇文章在2010或2011初的時候寫
我還可以大肆批評Android架構如何爛如何差(但其實我並不善於批評XD)
而且可能只有我或少數人能認同
(其實去年我在跟我身邊朋友討論時,多數人都不相信我說的)
到了2012年,我終於敢寫這篇文章了

因為這世界上,不再只有我這樣想
先來引述幾篇文章

2012-02-07
七種跡象表明,Android將被強勢的iOS拽出疾速增長的軌道
http://www.36kr.com/p/81192.html

這篇文章可能是我所看到的第一篇,敢跳出來戰Android的文章
當時下面的回覆可是熱鬧非凡(不過改版後回復都不見了?)
我當然也有跳進去湊熱鬧XD
在今年2月的時候
仍然有大多數人看好Android,不斷吐槽寫這篇文章的人

不過在短短兩個月內!!
是的,就是今年四月
2012-04-22
實時研究表明:相比於Android,iOS設備用戶活躍度佔絕對主導地位
http://www.36kr.com/p/102211.html

這結果我並不意外
比較讓我意外的是下面的回覆
竟然大多數人都贊同這篇文章的研究結果!

自此之後,不利於Android文章越來越多

2008年以來Google通過Android只撈到5.5億美元,通過iPhone收入達數十億
http://www.36kr.com/p/95345.html
Android:什麼情況?
http://www.36kr.com/p/103038.html
單個應用上每用戶貢獻平均收入,Amazon 大於iTunes,並遠超Google Play
http://www.36kr.com/p/103380.html
212-04-27
左有蘋果的 iPhone,右有 Amazon 的 Kindle Fire,Google 的 Andr​​oid 將走向何方
http://techorange.com/2012/04/27/where-should-google-go-with-android/

到底是什麼原因造成現在的局面?

就我所觀察
比較嚴重的有兩點(但都跟Android架構無關XDD)
1. 版本分裂,導致開發者開發Android App時難度增加
我想最常聽見的就是,要為了每種不同解析度的螢幕優化
實在快累死人~~
不過這種情況在Android 4.0被稍稍改善
但由於4.0市占率不高,加上有消息稱5.0就快發布了
我想這問題還會持續好一陣子。

2. Android App太容易複製、傳播
這應該就是嚴重影響,Google Play 上使用者付費意願的主要原因
大家應該都知道SPB Mobile Shell,一個很華麗的桌面應用程式
Google Play上定價快500台幣
在Android 世界裡,算是賣得不錯
但很不幸的,只要在google搜尋欄位輸入"spb mobile shell 5.0 破解"
還真的就找到免費完整版了...
更不用說其他軟體了

不過有人會說 iPhone 越獄後不也一樣嗎?
根據個人使用經驗
我用過Hero, Desire(都是老牌的了XD),一隻iPhone 3GS
Android刷機是一定要的,iPhone越獄也是必然的
就我體驗的結果是
我的iPhone雖然越獄了
但Cydia的搜尋功能真是爛的可以...
而且除非我知道我要哪個軟體
要不然我也只能對著搜尋欄位發呆XD
而Android 我也不知道為什麼
總是可以下載到最新最好用的東西
軟體、遊戲什麼都不缺~~
真是窮人的好手機啊!!!!!!!!!!!讚!!!!
(不過我現在都用iPhone居多....)

當然還有很多安全性議題的文章,在此沒有列出
因為其實越獄後的iPhone只比root 過的 Android安全一點點(在此呼籲有越獄的朋友們,一定要把SSH關掉或改密碼!!!)
(但沒越獄的iPhone可能就比Android安全許多)
主要還是因為apk太容易被拆開再組合回去
這動作看似沒有意義,但最大的意義就在於拆開和組合中間的過程
拆開後可以修改內遷廣告的ID(讓收益變自己的)
也有可能加入隱藏的回傳資訊程式碼(原本就需要GPS和網路的App來說,裡面加進一小段回傳資訊的程式碼也是很難被發現的)
再包回apk,任意散播至各大論壇

說了這麼多
Android 真的無法再進步了嗎?
其實他還是在成長的XD
而且Linux kernel 擁有高移植性
所以對其他裝置來說,仍然有很大發展性(如:電視、智慧家庭....等等)

就手機而言目前除了Apple以外已經沒有其他選擇了
我以前也是個Apple hater,但實在受不了Android 只好跳槽當自己所討厭的人XDD
所以我仍然在期待年底的Windows Phone !!
原因? 下次再說吧....累了XDD


--------------
2012-04-27
再補一篇
Android平台上的盜版概況
http://www.inside.com.tw/2010/08/30/the-piracy-in-android

2012年4月8日

【DIY kernel】為context switch做準備, 實作Task



這部分程式已經提交到gitHub

故不將所有程式碼貼在網誌上瞜~~



task 共有四種狀態,分別是 ready, running, block(類似在休息, 記憶體不會被釋放), close

目前無法真正的將cpu使用權切換給其他task使用

只能修改各task的狀態

等下次將context switch實作之後才會有作用

這部分程式主要就是在這些狀態中切換

所以需要

list_t ready_list[MAX_PRIO];
list_t blocked_list;
list_t termination_list;

這三種list紀錄某種狀態下有哪些task

然後用

u32int task_table[MAX_TASK_NUM];

來放所有task, 有task設為1其餘為0 (所以這個os只能run MAX_TASK_NUM 個 task)

最後還有一個

u8int prioExistFlag[MAX_PRIO];

這是為了取出priority最高的 ready_list 所建的table (array)。

task 的結構如下


struct task{
node_t node;

void *sp;
void *entry;
void *parameter;
void *stack_addr;
u16int stack_size;

u8int priority;

u32int init_tick;
u32int remaining_tick;

u32int task_id;
TASK_STATE state;
char name[NAME_MAXLENTH];

timer_t *timer;
};

比較特別的是 void *sp (stack point)這指向 task 的 stack

void *entry 這指向這task要做的事情的function

void *stack_addr 就是指向stack的開頭摟

總共要實作的function有這些


task_t *task_create( const char *name,
void (*entry)(void *parameter),
void *parameter,
u16int stackSize,
u8int priority,
u16int tick);
s8int task_start( task_t *task );
s8int task_delete( task_t *task );
void task_yield();
s8int task_sleep( u32int tick );
s8int task_suspend( task_t *task );
s8int task_resume( task_t *task );
void task_remove( task_t *task );
void task_addReady( task_t *task );
task_t *ready_task( u8int priority );
u8int get_top_prio();
u32int get_task_id();

static void task_timeout(void *parameter);

在task_create裡


task_t *task_create( const char *name,
void (*entry)(void *parameter),
void *parameter,
u16int stackSize,
u8int priority,
u16int tick){

task_t *task;
u32int *stackAddr;
u32int task_id;

task_id = get_task_id();
if( task_id < 0 ){
return NULL;
}

task = (task_t *)kmalloc(sizeof(task_t));
if(task == NULL)
return NULL;

stackAddr = (void *)kmalloc(stackSize);
if( stackAddr == NULL ){
kfree(task);
return NULL;
}

for( int temp=0; temp<NAME_MAXLENTH; temp++ ){
task->name[temp] = name[temp];
}

task->task_id = task_id;
task->entry = (void *)entry;
task->parameter = parameter;
task->stack_addr = stackAddr;
task->stack_size = stackSize;
task->node.next = NULL;
task->node.prev = NULL;

task->init_tick = tick;
task->remaining_tick = tick;
task->priority = priority;
task->sp = (void *)( (void *)stackAddr + stackSize -4 );

memset( (u8int *)task->stack_addr, '?', task->stack_size );
task->sp = (void *)init_stack(task->sp,
 task->entry,
 task->parameter);
task_table[task->task_id] = 1;

task->timer = timer_create(task->name,
  task_timeout,
  task,
  0,
  SET_ONESHOT);

return task;
}

首先會取得task_id

就是從task_table裡取出value是0的key

之後就是初始化的動作

其中的init_stack在下次context switch時再一併講解

其餘工具的演算法或流程也不難

從一個狀態切換到下個狀態主要是

從原本的list裡移除(critical region)再加入另一個狀態的list中

詳細程式碼請至gitHub下載或線上參考~~

https://github.com/herb123456/DIY_Kernel

============================================

後來想想,還是直接在這寫下 init_stack 吧XD

init_stack在 stack.c 裡實作

是每個 task 專屬的一塊記憶體

裡面放的東西長這樣:


之後context switch 時 會將程式狀態存在這塊stack裡

詳細流程會在下一篇在講~~

2012年4月7日

【DIY kernel】實作 timer



timer 要做的事情也不難

了解整個過程之後code就比較好理解

首先 timer create 之後就將 timer 加入 timer_list

之後在每100個clock發生的interrupt內檢查timer_list

是否有timeout的timer和不是periodic mode(週期模式)的timer

發生timeout就執行 timeout handler

不是periodic mode 就設為inactive

然後還有額外的控制函數

timer_stop, timer_delete, timer_control(可控制timer mode)

接下來的code只會在比較特別的地方做講解!

先定義 timer 的結構和狀態


#define NAME_MAXLEN 20

struct timer{

node_t node;

char name[NAME_MAXLEN];
u8int flag;
void (*timeout_func)(void *parameter);
void *parameter;
u32int init_tick;
u32int timeout_tick;
};
typedef struct timer timer_t;

#define ACTIVATED 1<<0
#define PERIODIC 1<<1
typedef enum{
SET_TIME = 0,
GET_TIME,
SET_ONESHOT,
SET_PERIODIC
} TIMER_CMD_TYPE;


接下來就實作會用到的 function


void init_system_timer(){

init_list( &timer_list );

}



timer_t *timer_create( const char *name, void (*timeout)(void *parameter), void *parameter, u32int time, TIMER_CMD_TYPE flag ){

timer_t *timer = kmalloc( sizeof(timer_t) );
if( timer == NULL ){
kprintf("timer == NULL \n");
return NULL;
}else{
kprintf("timer != NULL \n");
}
timer_init(timer, name, timeout, parameter, time, flag);
return timer;
}

void timer_init( timer_t *timer, const char *name, void(*timeout)(void *parameter), void *parameter, u32int time, TIMER_CMD_TYPE flag ){
if( timer == NULL )
return;
switch(flag){
case SET_ONESHOT:
timer->flag &= ~PERIODIC;
break;
case SET_PERIODIC:
timer->flag |= PERIODIC;
break;
}
timer->flag &= ~ACTIVATED;
timer->timeout_func = timeout;
timer->parameter = parameter;
timer->timeout_tick = 0;
timer->init_tick = time;
}



timer_list 是個 list_t 型態的全域變數

list_t timer_list;


接著是實作 timer_start (將 timer 丟進 timer_list裡,並按照timeout大小排列)
和 timer_check (檢查timer_list是否有timeout的timer)


s8int timer_start( timer_t *timer ){

if( timer == NULL || timer->flag & ACTIVATED )

return ERROR;
timer_t *ptimer;
node_t *pnode, *last_node;
timer->timeout_tick = get_tick() + timer->init_tick;
disableInterrupt();
if( !is_Empty( &timer_list ) ){
pnode = first_node( &timer_list );
last_node = &timer_list.tail;
while( pnode != last_node || pnode == NULL ){
ptimer = (timer_t *)pnode;
if( ptimer->timeout_tick > timer->timeout_tick ){
append_list(pnode->prev, &timer->node);
break;
}else{
pnode = pnode->next;
}
}
if( pnode == last_node ){
insert_rear(&timer_list, &timer->node);
}
}else{
insert_rear(&timer_list, &timer->node);
}
enableInterrupt();
timer->flag |= ACTIVATED;
print_list(&timer_list);
return OK;
}

void timer_check(){
timer_t *ptimer;
node_t *pnode;
u32int current_tick = get_tick();
disableInterrupt();
if( !is_Empty(&timer_list) ){
pnode = first_node(&timer_list);
while( pnode != &timer_list.tail ){
ptimer = (timer_t *)pnode;
if( current_tick >= ptimer->timeout_tick ){
remove_node(&ptimer->node);
ptimer->timeout_func(ptimer->parameter);
if( (ptimer->flag & PERIODIC) && (ptimer->flag & ACTIVATED) ){
ptimer->flag &= ~ACTIVATED;
print_list(&timer_list);
timer_start(ptimer);
}else{
ptimer->flag &= ~ACTIVATED;
}
current_tick = get_tick();
pnode = pnode->next;
}else{
//kprintf("while break \n");
break;
}
}
}
enableInterrupt();
}


接下來是工具


s8int timer_stop( timer_t *timer ){

if( timer == NULL || !(timer->flag & ACTIVATED) )
return ERROR;
timer->flag &= ~ACTIVATED;
disableInterrupt();
remove_node(&timer->node);
enableInterrupt();
return OK;
}

s8int timer_delete( timer_t *timer ){
if( timer == NULL )
return ERROR;
disableInterrupt();
remove_node(&timer->node);
enableInterrupt();
kfree(timer);
return OK;
}

s8int timer_control( timer_t *timer, TIMER_CMD_TYPE cmd, u32int arg ){
if( timer == NULL )
return ERROR;
switch( cmd ){
case SET_TIME:
timer->init_tick = arg;
break;
case GET_TIME:
return timer->init_tick;
case SET_ONESHOT:
timer->flag &= ~PERIODIC;
break;
case SET_PERIODIC:
timer->flag |= PERIODIC;
break;
default:
return ERROR;
}
return OK;
}



我在gitHub上開了個 project
有興趣的人可以去看看

2012年3月31日

專長



以前的我

只要覺得某樣技術很酷很炫

就一定要去玩一玩、碰一碰

甚至有個聲音告訴我

別人可以會,為什麼你不行?

常常因為這樣讓我掉進熬夜的深淵

總是仗著還年輕可以摧殘自己的健康

甚至在前一兩年

我依然覺得一個人會很多技術很屌

從底層的C到中間的embedded、porting

到桌面應用C++、QT、Java、VB

到更上層的網頁應用程式資料庫PHP、MySQL、Oracle、Javascript、ExtJS、jQuery、mootools...

還有最近很夯的手機應用程式和Node.JS....

過了這些歲月(好像也沒幾年)

回頭看自己,除了網頁程式有在接案子的關係所以比平常人熟悉一些外

其餘的要說自己會還真難....(雖然有程式底子學什麼都快,但那不足以證明自己什麼都會)

那我日以繼夜學習的那些,到底是為了什麼?

這幾個月我所體悟到的一件事就是


一個人甚至一個團隊,不可能會所有東西,專注於某件事上吧!!

【DIY kernel】實作簡單資料結構 - Doubly Linked List (雙向鏈結串列)


借用老師投影片的圖~~

實作 timer 之前必須要有基本的資料結構

這部分跟寫作業一樣

只要有修過資料結構的都有寫過

所以就不贅述細節了

先定義 list 和 node 的結構



struct node
{
struct node *next;
struct node *prev;
};
typedef struct node node_t;

struct list
{
node_t head;
node_t tail;
};
typedef struct list list_t;



接下來就是實作double linked list需要用到的方法:

//判斷是否為第一個node
u8int isHeadnode (node_t *node);

//判斷是否為中間的node

u8int isInternalNode (node_t * node);

//判斷是否為最後一個node
u8int isTailNode (node_t * node);

//初始化List
void init_list (list_t * list);

//取得第一個node
node_t *first_node (list_t * list);

//取得最後一個node
node_t *last_node(list_t * list);

//判斷該list是否為空
u8int is_Empty (list_t * list);

//將node依附在某個node之後
void append_list (node_t * before, node_t * node);

//將node插入最前面
void insert_front (list_t * list, node_t * node);

//將node插入最後面
void insert_rear (list_t * list, node_t * node);

//將node刪除
void remove_node (node_t * node);

//將第一個node刪除
node_t *remove_firstNode (list_t * list);

//將最後一個node移除
node_t *remove_lastNode (list_t * list);


以下附上投影片上的程式碼


u8int isHeadNode (node_t *node){
return (node != NULL) && (node->prev == NULL) && (node->next != NULL);
}

u8int isInternalNode (node_t *node){
return (node != NULL) && (node->prev != NULL) && (node->next != NULL);

}

u8int isTailNode (node_t *node){
return (node != NULL) && (node->prev != NULL) && (node->next == NULL);
}

u8int is_Empty (list_t *list){
return (list->head.next == &list->tail);
}

void init_list (list_t *list){
list->head.prev = NULL;
list->head.next = &list->tail;
list->tail.prev = &list->head;
list->tail.next = NULL;
}

node_t *first_node (list_t *list){
if (is_Empty (list))
return NULL;
return list->head.next;
}

node_t *last_node (list_t *list){
if (is_Empty (list))
return NULL;
return list->tail.prev;
}

void append_list (node_t *before, node_t *node){
if( before->next != NULL )
before->next->prev = node;
node->prev = before;
node->next = before->next;
before->next = node;
}

void insert_front (list_t *list, node_t *node){
append_list( &list->head, node );
}

void insert_rear (list_t *list, node_t *node){
append_list( list->tail.prev, node );
}

void remove_node (node_t *node){
if (isInternalNode (node)){
node->prev->next = node->next;
node->next->prev = node->prev;
node->next = NULL;
node->prev = NULL;
}
}

node_t *remove_firstNode (list_t *list){
node_t *node = first_node (list);
if (node != NULL)
remove_node (node);

return node;
}

node_t *remove_lastNode (list_t *list){
node_t *node = last_node (list);
if (node != NULL)
remove_node (node);

return node;
}

2012年3月27日

文章整理


廣告:
未來的廣告生態圈:看到即購買
http://www.36kr.com/p/90126.html
視頻廣告革命——創業公司Impossible Software在HTML5視頻中植入動態圖片和視頻
http://www.36kr.com/p/94564.html
Serge新款移動搜索引擎(SDK)幫你實現:對著物品拍張照片,就能知道哪兒購買
http://www.36kr.com/p/96223.html


Email改革
Paul Graham:最偉大的創意都是令人恐懼的:(二)取代電子郵件
http://www.36kr.com/p/89633.html
沃爾瑪收購生日與節日提醒APP,欲借此構築社會化電子商務平台
http://www.36kr.com/p/89854.html

傳統的電子郵件已經過時了,Minbox要把郵箱變成社交網絡 (視頻)
http://www.36kr.com/p/94355.html
終於出現了嗎! 我也是朝這方面去思考的!
2012.06.02
這新產品也跟我想法類似!
http://www.36kr.com/p/114653.html

Email sucks. This is how you fix it.
http://joncalhoun.posterous.com/email-sucks-this-is-how-we-fix-it

solution list:
http://www.streak.com/
http://zeromail.com/
http://fluent.io/
http://chandlerproject.org/Projects/ProductTour



旅遊:
創業公司Triptrotting旨在把旅行者和地陪聯結起來
類似沙發客
Pinterest與旅遊結合:旅遊推薦引擎Wanderfly全新改版
http://www.36kr.com/p/89107.html
只一週時間,引入Pinterest模式的旅遊推薦引擎Wanderfly獲得了高速發展
http://www.36kr.com/p/90884.html
Pinterest 與旅遊結合,上海創業公司推出社交旅遊景點推薦網站「咕嚕魚」
http://www.36kr.com/p/93963.html


婚戀交友:
theComplete.me希望成為「有Pinterest式體驗的基於社交網絡的婚戀網站」
http://www.36kr.com/p/89911.html
移動應用Between幫助情侶實現一對一社交,打造只有兩個人的社交網絡
http://www.36kr.com/p/90054.html


有趣的應用
iPhone免費應用Picle能讓你在拍照的同時錄製環境音 (視頻)
http://www.36kr.com/p/90316.html
在線工作平台oDesk獲得1500萬美元投資,增長速度連續四年超過100%
http://www.36kr.com/p/92817.html
在線辦公平台Zoho推出iOS和Android版CRM軟件,進一步擴大移動市場
http://www.36kr.com/p/94158.html
無需編程知識,Wix讓你零基礎打造HTML5頁面
http://www.36kr.com/p/94076.html
Carvoyant幫你把愛車的「健康記錄」存儲在雲端,並追蹤她何時需要修理
http://www.36kr.com/p/95116.html
社交媒體管理工具Hootsuite獲得2000萬美元投資,繼續擴大社交網絡管理市場
http://www.36kr.com/p/95214.html
桌面應用Highlight Hunter幫你自動剪輯自拍視頻中的精彩片斷
http://www.36kr.com/p/96365.html
致力於文件搜索的Found讓你更簡單快速找到散佈在網絡上的文件
http://www.36kr.com/p/96196.html
Flurry升級免費移動應用分析工具,增加客戶跟蹤與告警等功能
http://www.36kr.com/p/97645.html
創業公司Mugeda 推出網頁版HTML5 動畫編輯器,實現HTML5 動畫雲端製作、存儲、託管、統計#36氪開放日#
http://www.36kr.com/p/97301.html
泛社交工具Everyme:將你的手機通訊錄打造成最真實的社交網絡
http://www.36kr.com/p/98834.html



創業相關
【創業說】怎樣打造一個酷網站?
http://www.36kr.com/p/7638.html
Mark Suster給創業新人的42條建議
http://www.36kr.com/p/90906.html
要想出初創創意,我的技巧之一是,想像一下,什麼東西在未來人的眼裡是落後的。
「慾望發動機」:想要吊住用戶嗎?把他們逼瘋吧!
http://www.36kr.com/p/93981.html
Y Combinator最成功的10個創業公司
http://www.36kr.com/p/94491.html
新創網站這樣開發才夠快
http://blog.xdite.net/posts/2012/04/07/startup-rapid-development/


產品行銷相關
如何成為一個優秀的產品經理,來自Google內部人士的建議
http://www.36kr.com/p/90690.html
創業公司製作產品講解視頻的6大原則
http://www.36kr.com/p/93870.html
「慾望發動機」:想要吊住用戶嗎?把他們逼瘋吧!
http://www.36kr.com/p/93981.html
它不單單是增強用戶行為,而是創造用戶習慣,驅使用戶自發地行動,而不需要借力諸如廣告這樣的外部刺激











現在不創業,以後會後悔

線上線下共襄盛舉,appWorks Demoday #4 圓滿落幕

2012年3月23日

【三年前作品】WebHD



最近突然翻到以前寫得程式

三年前到現在

完全無進展XDD

想當初還是熱血青年

想說寫網頁也有段時間了

也是該寫個系統給自己用用

當時還沒有DropBox

我也只有一台破破的server

但當初靠這系統讓我出門在外

不管是幫人修電腦還是上課筆記

都不愁沒地方放

算是很方便啦

但其實改版之後根本沒寫完XDD

說忙其實也還好(根本就是懶惰)

現在把原始碼放出來

前台用的是ExtJS 3.X

後台是 PHP + MySQL


目前完成的功能有

右鍵處理檔案與資料夾、搜尋

拖曳方式移動檔案與資料夾

多選檔案上傳

簡單來說就是基本的瀏覽、管理 檔案、資料夾功能都有

只差修一些bug和加入登入畫面


Source code:
http://code.google.com/p/web-hard-disk/

有興趣的人可以繼續完成它XDD

2012年3月22日

【DIY kernel】簡單實作 malloc, free

其實建置環境的下一堂課是在講 GDT & IDT

但實在太深奧

憑我的智商還很難自己寫一篇文章來介紹~~

所以自動跳過XD

改來實作比較有趣的吧

平常我們在 C/C++ 裡所使用的 malloc 

功能是像 os 要一塊記憶體空間

參數是這塊空間的大小

則 free 就是把給出的空間還給 os

現在就要來實作這個功能

下圖是整個工作流程示意圖


從heap.h開始看我們需要哪些功能


#ifndef __HEAP_H__
#define __HEAP_H__

#define BLOCKHEAD_SIGNATURE 0x0000F00D
typedef struct blockHead {
u32int signature;
u8int   allocated;
u32int size;
struct blockHead *next;
struct blockHead *prev;
} blockHead_t;

blockHead_t *gHeapBase;

bool init_heap(u32int heapStarAddress, u32int size);
void *kmalloc( u32int requireSize );
bool kfree(void *block);
bool compactedHeap( blockHead_t *currentHeadPtr );
void compact_forward( blockHead_t *currentHeadPtr );
void compact_backward( blockHead_t *currentHeadPtr );

#endif


我們定義了 blockHead 用來紀錄每塊分配記憶體的資訊

和 BLOCKHEAD_SIGNATURE 來識別這塊記憶體是否為我分配的

還有一個 gHeapBase 在 init_heap 時會將它指向記憶體最頂端

剩下的功能就是分配、釋放記憶體

和釋放之後合併附近區塊記憶體的功能




首先在kernel.c裡我們要呼叫初始化記憶體的函式


u32int memsize = (mboot_ptr->mem_lower + mboot_ptr->mem_upper) * 1024; init_heap(end_address, memsize - end_address );

mboot_ptr 是從 kernel_start 傳進來的參數

是 GRUB 的 multiboot 在開機時從BIOS得來的系統資訊

在 asm 呼叫 kernel_start 時丟進去的參數



start:


    ;push esp ;stack location (GRUB doesn't tell us)
    ; Load multiboot information:
    mov esp, end+0x1000
    push    ebx


    ; Execute the kernel:
    cli                         ; Disable interrupts.
    call _kernelStart           ; call our main() function.
    jmp $                       ; Enter an infinite loop, to stop the processor
                                ; executing whatever rubbish is in the memory
                                ; after our kernel!




所以可以藉此了解整塊記憶體的大小

end_address 是 load 完這支 kernel 之後的位置

是從linker 得來的


/* Link.ld -- Linker script for the kernel - ensure everything goes in the */
/*            Correct place.  */
/*            Original file taken from Bran's Kernel Development */
/*            tutorials: http://www.osdever.net/bkerndev/index.php. */

ENTRY(start)
/*OUTPUT_FORMAT(elf32-i386)*/
SECTIONS
{

    .text 0x100000 :
    {
        code = .; _code = .; __code = .;
        *(.text)
        . = ALIGN(4096);
    }

    .data :
    {
        data = .; _data = .; __data = .;
        *(.data)
        *(.rodata)
        . = ALIGN(4096);
    }

    .bss :
    {
        bss = .; _bss = .; __bss = .;
        *(.bss)
        . = ALIGN(4096);
    }

    end = .; _end = .; __end = .;
}

接下來看 init_heap 做了些什麼


bool init_heap(u32int heapStarAddress, u32int size){
if( gHeapBase != NULL )
return FALSE;

gHeapBase = (blockHead_t *)heapStarAddress;
gHeapBase->allocated = FALSE;
gHeapBase->signature = BLOCKHEAD_SIGNATURE;
gHeapBase->next = NULL;
gHeapBase->prev = NULL;
gHeapBase->size = size - sizeof(blockHead_t);

return TRUE;
}


第一行先判斷是否重複呼叫 init_heap

如果曾經呼叫過則 gHeapBase 就會指向最頂端的 blockHead 而不是NULL

如果沒呼叫過就初始化 gHeapBase


之後若有人呼叫 kmalloc, kernel 就要回傳分配的記憶體位置


void *kmalloc( u32int requireSize ){
blockHead_t *blockPtr = gHeapBase;
blockHead_t *newBlock;

while( blockPtr != NULL ){
if( !blockPtr->allocated && blockPtr->size >= requireSize ){ //check size enough
blockPtr->allocated = TRUE;
if( (blockPtr->size - requireSize) > sizeof(blockHead_t) ){ //check block head size enough
// ______
// |____| ->blockPtr
// | |
// | | ->requireSize
// |____|
// |____| ->newBlock
// | |
// | |
// | | ->newBlock->size
// | |
// | |
// |____|
//

newBlock = (void *)blockPtr + sizeof(blockHead_t) + requireSize;
newBlock->size = blockPtr->size - requireSize - sizeof(blockHead_t);
newBlock->allocated = FALSE;
newBlock->signature = BLOCKHEAD_SIGNATURE;
newBlock->prev = blockPtr;
newBlock->next = blockPtr->next;

if( blockPtr->next != NULL ){
blockPtr->next->prev = newBlock;
}
blockPtr->next = newBlock;
blockPtr->size = requireSize;

}

break;
}

blockPtr = blockPtr->next;
}

return (blockPtr != NULL) ? ( (void *)blockPtr + sizeof(blockHead_t) ) : NULL;
}



一開始先宣告一個指向開頭的指標

和將要新加區域的指標

然後用while從頭開始找 沒有被配置過 而且空間也夠大 的區塊
if( !blockPtr->allocated && blockPtr->size >= requireSize )

如果沒有就指向下一個區塊的 blockHead

若很幸運的有這個區塊

則把這個區塊設為已被配置過
blockPtr->allocated = TRUE;

再判斷該空間是否配置完後有剩餘空間建立新的 blockHead
if( (blockPtr->size - requireSize) > sizeof(blockHead_t) )

如果不夠,則不建立新的 blockHead 並把目前這個 blockHead 位置回傳

如果夠,則建立新的 blockHead (新的 blockHead 是新的未配置的區域)

newBlock = (void *)blockPtr + sizeof(blockHead_t) + requireSize;
newBlock->size = blockPtr->size - requireSize - sizeof(blockHead_t);
newBlock->allocated = FALSE;
newBlock->signature = BLOCKHEAD_SIGNATURE;
newBlock->prev = blockPtr;
newBlock->next = blockPtr->next;

if( blockPtr->next != NULL ){
blockPtr->next->prev = newBlock;
}
blockPtr->next = newBlock;
blockPtr->size = requireSize;



free 的部分比較簡單

就是把已配置的旗標,改成FALSE(未配置)即可

最後再合併上下未配置的區塊成為一個大的區塊


bool kfree(void *block){
blockHead_t *headPtr;

if(block == NULL)
return FALSE;

if( (void *)block < (void *)gHeapBase ){
return FALSE;
}

//
// |----| -> headPtr
// | |      | sizeof(blockHead_t)
// |----| -> block
// | |
// | |
// | | -> data
// | |
// | |
// |    |
// | |
// |----|
//

headPtr = (blockHead_t *)( (void *)block - sizeof(blockHead_t) );
if( headPtr->signature != BLOCKHEAD_SIGNATURE )
return FALSE;

headPtr->allocated = FALSE;

return compactedHeap(headPtr);
}



傳進來的參數是指向 data 的位置

所以要設置 blockHead 的 allocated 要先把位置往上移一個 blockHead 的大小
headPtr = (blockHead_t *)( (void *)block - sizeof(blockHead_t) );

並且判斷 signature 是否跟我設的一致
if( headPtr->signature != BLOCKHEAD_SIGNATURE )

如果都符合,就把 allocated 設為 FALSE

並呼叫合併函式 compactedHeap(headPtr);


bool compactedHeap( blockHead_t *currentHeadPtr ){
if( currentHeadPtr->next != NULL ){
compact_forward( currentHeadPtr );
}
if( currentHeadPtr->prev != NULL ){
compact_backward( currentHeadPtr );
}
}



判斷是否有上一個或下一個

有的話就進行合併


void compact_forward( blockHead_t *currentHeadPtr ){
// ______
// |____| -> currentHeadPtr
// | |
// | |   compact two blocks
// |____|
// |____| -> currentHeadPtr->next
// | |
// | |
// | |
// | |
// | |
// |____|
// |____|
// | |
// | |
// | |
// |____|
//
blockHead_t *nextPtr;
nextPtr = currentHeadPtr->next;
if( nextPtr != NULL && !nextPtr->allocated && nextPtr != currentHeadPtr ){
currentHeadPtr->next = nextPtr->next;
if( nextPtr->next != NULL ){
nextPtr->next->prev = currentHeadPtr;
}
currentHeadPtr->size = currentHeadPtr->size + nextPtr->size + sizeof(blockHead_t);
}
}

void compact_backward( blockHead_t *currentHeadPtr ){
blockHead_t *prevPtr;
prevPtr = currentHeadPtr->prev;
if( prevPtr != NULL && !prevPtr->allocated && prevPtr != currentHeadPtr ){
prevPtr->next = currentHeadPtr->next;
if( currentHeadPtr->next != NULL ){
currentHeadPtr->next->prev = prevPtr;
}
prevPtr->size = prevPtr->size + currentHeadPtr->size + sizeof(blockHead_t);
}
}




至此就完成簡單的 malloc和free了

當然這只是很基礎的演算法

要怎麼配置和回收記憶體

就看個人要怎麼實作了

2012年3月14日

【C】簡單解釋 void 和 void *



在學校老師一定都會教void是無型態的返回值
例如

void swap(int *a, int *b){
      int temp = *a;
      *a = *b;
      *b = temp;
}

這樣的函數結束之後
不必返回任何數值
就在函數名稱前面加上void

但是void *呢?
既然void是不需要返回值
那void *又代表什麼?


void 既然是無型態
那void *就是無型態的指標了
意思是可指向任何類型的指標
有點難想像
因為我們習慣把void當成只有在無返回值時要用的
但是也可以當作無型態的指標
也就是可以指向任何型態
例如:


void *a;
int *b;
*b = 10;
a = b;
printf("a: %d \n", *(int *)a);

a = b 的時候 a 還是一個無型態的指標
b 所指向的位置 assign 給 a 之後 a 仍然是無型態的指標
但已經指向 b 的位置
所以只要將 a 轉為 int 指標就可以print出a指向位置的值


2012年3月11日

手機程式開發心得(Android SDK, Xcode, Titanium, phongap 的比較)



過去要寫手機程式是非常難入門的
Objective-C 的複雜語法讓很多人怯步
要寫得好不只要很懂物件導向觀念
也要有很厲害的C語言概念
為什麼兩者都要很厲害呢?
如果只懂什麼是物件導向
那可能可以很快了解如下的轉換


但是要面對幾百頁的Document就會開始頭暈傳向
不知從何開始看起

就算慢慢K到懂了
也無法寫出屬於自己的物件
如果只懂一點的C語言
那可能很熟悉C statement
if else for while switch case
但是對記憶體管理就很陌生
常常讓一隻可以很乾淨的app
跑沒多久就crash


不過這一切在Android出來之後就被改變了
Android採用類似(幾乎就是)Java Virtual Machine的方式
讓已經懂java的人好上手
之外也不必再擔心記憶體的動態管理了
因為Java有garbage collection機制
就算什麼都不懂的人
也只要專注在Java就能把Android app寫出來

但是Google一定沒想到
好入門也是個重大缺點
Android Market上App數量暴增的情況下
有哪些是可使用的App?
哪些是真的有顧慮到使用者體驗的App?
充斥著多少奇怪甚至高危險性的App?
而且因為採用JVM
讓Android效能大為降低
要使用高階硬體配備才能跑得夠順暢
高階硬體配備的背後
就是高耗電量也就是手機無法使用很久
雖然說電池是所有智慧型手機的通病
但我敢說,在同樣常駐程式下 Android 絕對比 iOS 耗電

扯遠了XD
在手機App興起之際
還有個東西也在慢慢起步
就是HTML5
但其實HTML5雖然有WebGL
但目前手機瀏覽器都還不支援
就算有效能也很差
所以無法寫遊戲
只能寫出一些簡單的Table view, Tab view 的App
而且有個很嚴重的問題就是無法直接存取手機功能
例如 GPS, Camera
也無法丟上Market或Store上販賣
所以出現了PhoneGap和Titanium
雖然這兩套好像是差不多的
但其實本質上差異很大

PhoneGap讓你可以跟底層裝置溝通
也可以包成Native App讓你丟上Market
但她只是用WebView來顯示你的畫面
所以要自己打造符合使用者體驗的UI
或是使用現成套件,例如 Jquery mobile, JQtouch, Sencha Touch等
而且使用WebView的方式所以整體架構就變成
Android OS -> JVM -> Webview -> App
由此可見整體效能又被拖累
但未來若Google能對WebKit做記憶體優化
也許可以讓PhoneGap產生的App跟Native App有同樣效果

而Titanium則是使用Javascript打造原生UI的Native App
可以想成是 原本用Java、Objective-C寫Native App
現在改成用Javascript寫
所以不再需要學習兩種語言
就能寫兩種平台的App
而且產生出來的App不是用WebKit來呈現畫面
而是原生UI,也就是直接跟SDK溝通產生的UI
所以在效能上會優於PhoneGap
但缺點是產生的App會非常肥大
目前原因不明
/*------2012/03/19------*/
實際測試用Titanium SDK 1.8.2
產生一個簡單的Table View的App
在Android理實際佔用空間:11.21 MB
移至SD卡後佔1.21 MB
/*--------------------------*/

PhoneGap和Titanium這兩大Framework
要比較的話也沒有誰好誰不好
一切看自己的需求而定
PhoneGap是真的只需要寫一次跨各種平台
但效能會是個問題
而且在Android上實體按鍵的反應也要顧慮到
而Titanium效能勝出,但不能只寫一次
還要對各種平台做UI優化
算要寫一次半(兩種平台不需要寫到兩次)
缺點是產生出的App會很肥大

目前個人比較偏向Titanium但也在關注PhoneGap
未來PhoneGap應該會是趨勢

2012年3月8日

【DIY Kernel】環境建置



這學期修了一門 "作業系統工程" 的課
教的是自己製作一個OS Kernel
這是一門有趣又好玩但是很難的課程!!
以後每個禮拜都要把筆記寫下來
免得太困難的部分未來會忘掉

先補上第一次上課的內容
也就是環境建置
這篇有可能會用到的工具可以在此下載

首先要熟悉電腦開機流程
可參考
http://people.cs.nctu.edu.tw/~huangmc/works/web/Boot_x86/Boot_x86.html

實際上
不可能真的修改boot loader
一直重開機會很耗時
所以用虛擬的環境bochs來模擬x86環境
http://bochs.sourceforge.net/
選擇GRUB當boot loader

並且使用VFD(Virtual Floopy Disk)來製作與修改磁碟片映像檔

還需要一個已經具有GRUB開機管理程式的印象檔
來製作自己的印象檔(這步驟可省略,可直接拿已有寫入MBR的Image來修改)

並使用cygwin模擬Linux環境
並選擇安裝
gcc
nasm
automake
用來編譯kernel

工具都準備好之後
就可以開始建置開發環境了

製作自己的可開機印象檔

首先將bochs和vfd解壓縮與安裝好
開啟vfd(Vista以上請使用管理員身分執行)
建立一個空的image檔案


這時可以打開A磁碟
發現裡面是空的
之後將GRUB解壓縮丟進去


然後存檔


之後開啟Bochs
Edit Option 選擇 Disk & Boot
再選旁邊的Edit

Floopy Option 選擇已經可開機 Image (floopy.img)


然後按OK和Start
順利的話可看見grub commend line


按左上角A磁碟的圖示
修改掛載的A磁碟 Image 為我們剛製作的Image


就可以看見bochs出現掛載的訊息
這時我們在grub輸入 setup (fd0)
這樣就完成自己製作的可開機印象檔了




製作menu.lst

menu.lst可說是grub的開機選單
方便我們可以製作多個kernel並選擇要用哪個kernel啟動
首先掛載剛剛製作好的Image
並在A:\boot\grub
下建立一個純文字檔案
並輸入
title MyOS
root (fd0)
kernel /boot/kernel.elf
boot

title就是要顯示的名稱
root 是kernel 所在的根目錄
kernel 是以root為基礎的 kernel 相對位置

可以依據自己的喜好來設定

編譯Kernel

老師對我們很好
提供了一個最基礎的kernel
以後就方便在上面加東西啦
用管理員身分打開cygwin(這樣就等於有root權限)
進入放著原始kernel的資料夾


發現已經有Makefile
所以直接輸入指令 make
即可編譯
(kernel的source code細節略過)
編號成功後發現多了一個檔案叫kernel.elf
把這個kernel.elf丟進掛載好的Image裡
對應menu.lst的路徑
重新啟動bochs就可以用自己編譯的kernel開機了!

建立自動執行檔

目前為止,整個流程大致如下
編譯完kernel
複製kernel.elf到印象檔裡
然後儲存印象檔
開啟bochs
選擇disk&boot
選擇我們的印象檔
開機

如果每次編譯完都要執行這一串步驟
肯定會很累
所以要寫一個屬於自己的自動執行檔
簡化以上的步驟
首先在make完之後直接將kernel.elf複製到A磁碟
所以修改Makefile
在objcopy下一行加入
cp -a kernel.elf /cygdrive/a/boot/kernel.elf

再另外寫一個run.sh
要做的事有
1. 存檔
2. 用bochs開啟印象檔

#!/bin/bash
/your/vfd/path/vfd.exe save
/your/bochs/path/bochs -f bochsrc.bxrc -q

而bochsrc.bxrc則是從bochs選擇完你的Image
之後左邊有個Save
可以把設定儲存起來

這樣就完成自動執行的腳本了