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
可以把設定儲存起來

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