發信人: gdtyy (gdtyy), 信區: Embedded
標 題: 第七講 檔案系統
發信站: 水木社區 (Mon Jun 25 23:32:53 2007), 站內
*******************
* 第七講 檔案系統 *
*******************
2007/01/06 asdjf@163.com www.armecos.com
檔案系統的本質是“按名存取”,把檔案名稱字和資料對應起來,比如webserver裡需要
按檔案名稱提取檔案資料(各種圖片gif/bmp等,html,cgi等)。你可以用各種方法實現這個目
的,只要能夠“按名存取”就叫檔案系統,比如:用數組儲存檔案,用鏈表結構體,用比較
複雜完備的FAT等。完成名字映射,可以給應用編程帶來極大的便利,你不必親自管理資料
儲存,所有細節操作交由檔案系統處理,只需要統一地用名字訪問即可。檔案系統屏蔽底層
細節,使用者可以在ROM、RAM、網路、硬碟、SD卡、CF卡、USB等很多種介質上實現“按名存
取”。演算法根據需要可以簡單也可以複雜,核心思想就是一個------“按名存取”。
前面講過ecos將串口抽象為串口裝置檔案,將中斷抽象為一個通用的虛擬中斷系統,而
這裡講的檔案系統就是特指對存放裝置的抽象。存放裝置利用半導體、磁性、光學等物理介
質材料,把所有資訊(視頻、音頻、映像、資料等)以“0”或“1”編碼的形式儲存起來。用
戶通過將(檔案)名字掛裝到物理介質上,並在其上使用某個檔案系統(即資料結構),實現名
字映射。
常見的儲存介質有:ROM、RAM、硬碟、SD卡、CF卡、USB、網路.....
常見的檔案系統有:RAMFS、ROMFS、FAT、NTFS、NFS、EXT3......
有的檔案系統適用於多種介質,比較通用,而有的則是專門為某種介質設計。檔案系統
可以看成是某種資料群組織結構,不同的資料結構用在不同的介質上可靠性、效率不同,一般
有首選。
檔案系統的核心思想很簡單,不過,考慮到速度、效率、容量、簡單性、安全性、可靠
性、擴充性、並發訪問、儲存介質多樣等,“按名存取”的實現有一定的挑戰性。
ecos實現了POSIX標準的檔案和目錄函式,主要包括檔案系統的安裝和卸載、目錄操作
、檔案操作等幾個方面。檔案系統根據安裝點(mount point)的名字,將具有根的檔案名稱(以
“/”開始的檔案名稱)指向正確的檔案系統。當使用以“/”開始的檔案名稱時,系統將其與安
裝表中所有有效表項名字進行比較,表項名字在字元“/”出現之前或字串結束之前與
檔案名稱具有最長相符的表項即為該檔案所屬的檔案系統安裝表表項。檔案名稱的剩餘部分、該
安裝表表項的指標以及作為目錄指標的root值一起被當作參數傳送到檔案系統表表項中的相
應函數。
例如,假設一個安裝表具有如下的表項內容:
{ "/" , "msdos" , "/dev/hd0" , ... }
{ "/fd" , "msdos" , "/dev/fd0" , ... }
{ "/rom" , "romfs" , "" , ... }
{ "/rom/tmp" , "ramfs" , "" , ... }
{ "/tmp" , "ramfs" , "" , ... }
{ "/dev" , "devfs" , "" , ... }
其中順序為:{ 目錄名(掛裝點),檔案系統,物理裝置... }
當試圖開啟“/rom/yy”時,該檔案被定向到ROM檔案系統(romfs),而“/rom/tmp”卻
被定向到RAM檔案系統(ramfs)。開啟“/bar/bundy”將被定向到硬碟MSDOS檔案系統
(msdos),開啟“/dev/tty0”的操作將被定向到裝置管理檔案系統(devfs)的裝置表中的
lookup函數。不帶根的檔案名稱(不以“/”開始的檔案名稱)將直接定向到包含目前的目錄的檔案
系統。目前的目錄是由一個安裝表表項和一個目錄指標組合起來表示的。
下面我們以ROMFS檔案系統為例來說明檔案系統的用法,ROMFS檔案系統是最簡單的,對
檔案系統設計感興趣的讀者可以閱讀其源碼,如果想移植FAT作業系統,只要以ROMFS為藍本
,修改對應的介面函數實現體即可。
我們的例子程式是一個通用的靜態頁面發行伺服器,發行就緒任意目錄下的靜態頁面(
支援長檔名)、pdf檔案、圖片(jpg/bmp/gif等)......ZLG原來的例子程式只能發布一個簡
單頁面,也不能更換髮布目錄,下面給出的程式可以任意更換髮布目錄,可以做為靜態web
server使用。範常式序將發布“Linux From Scratch”,LFS安裝指導書將講解如何通過編
譯從網上下載的源碼包,來建立一個LINUX系統。當然,你也發行就緒自己的靜態個人首頁
,或者GNU的資料,或者英語學習資料等等,把這個web server掛在區域網路上,就可以從這
個伺服器上尋找常用資料了,比PC機省電,速度足夠了。如果嫌2M flash太小,可以考慮把
發布目錄掛接在2G SD卡/CF卡上,或者300G硬碟上。只要更改mount掛裝點即可,其餘部分
不用改動。如果想發布在互連網上,那麼實現了PPPOE和動態網域名稱以後就可以了。在發布目
錄下還增加了a.pdf和b.jpg檔案,以便測試pdf和jpg的發布。
Let's go!
web server的工作原理是:每當點選連結時,IE瀏覽器會發出GET請求“GET 檔案名稱(從
根開始) HTTP/1.0”,我們只要發回請求的檔案資料即可完成服務,IE瀏覽器會自動將頁面
中的圖片分別請求,每個請求對應一個TCP串連,傳送完畢不保留該次TCP串連。GET請求中
的檔案名稱是絕對路徑,即使在頁面中使用相對路徑,IE瀏覽器也會翻譯成絕對路徑發給服務
器,目錄由瀏覽器自己維護,伺服器只負責應答請求,不記憶串連資訊。可以在程式中增加
列印語句輸出請求資訊,對於深入理解HTTP協議是個很好的方法。由此可見ecos增值包用於
學習各種TCP/IP協議非常方便有效!該程式還利用了一個合理的調試技巧,完全漏掉了
HTTP頭,這種大多數(並非全部)情況下瀏覽器仍正確工作。雖然不提倡這種違背協議的行為
,但它在起初的開發和調試中確實有用。副檔名和HTTP資料類型之間的關係更為複雜。
當檔案從磁碟裝入時,瀏覽器必須判斷文件類型,它判斷的依據僅僅是副檔名,操作系
統檔案關聯,或兩者都作為依據。從網上下載時,檔案通常帶有副檔名和資料類型,後
者優先。不過並不強制WEB伺服器提供HTTP資料類型,雖然缺少它容易引起混淆,於是一些
瀏覽器根據副檔名衍生類別型,其他的則假定預設值(HTML)。
下面剖析器,大部分與ZLG程式相同,只分析不同部分。
讀到瀏覽器請求後,提取帶根的檔案名稱,如果是“/”,那麼就用預設檔案index.htm替
換(或者你可以預設選擇default.htm)。開啟此檔案,如果返回-1,說明檔案不存在,就關
閉TCP串連準備下一次應答,否則,讀取檔案並發送,一次讀不完就反覆這個過程,直至讀
完發完。這樣,不管多大的檔案,也能用有限的緩衝發送。由此可見,改動的地方並不大,
而且看起來更簡潔明了,檔案大小和類型也沒有限制了。使用檔案系統並不會使應用變複雜
,而是變得更容易了。
ROMFS檔案系統有兩種實現方法:1、用程式標頭檔實現;2、用ROM映像實現。
我要發布的目錄是lfs,其結構如下:
aboutdebug.htm
.
.
index.htm
.
.
zlib.htm
zlib-1.htm
a.pdf
b.jpg
在cygwin中使用$ mk_romfs -v ./lfs romfs.img將lfs目錄製作成ROMFS檔案系統映像
romfs.img,
在redboot中用lo -b 0x81010000 -r -h 192.168.0.1 romfs.img下載映像到RAM中,
在redboot中用fis create -b 0x81010000 -l 0x40000 romfs將RAM中的映像燒寫到
flash中,
用fis list查看到redboot把此映像自動分配到了0x80080000地址。
在程式中定義CYGNUM_FS_ROM_BASE_ADDRESS為0x80080000,就可以使用這個ROMFS了。
如果你要發布別的目錄,只要製作新的映像並替換這個位置的ROMFS檔案即可,不用改動程
序。
如果想用標頭檔的方式實現,只要用file2c.tcl就可以轉換為C標頭檔,如下:
sh file2c.tcl romfs.img romfs.h
把這個標頭檔包含在C應用程式裡,並將ROMFS掛裝在這個數組上即可。不過這樣每次更
改發布目錄/檔案都要重新編譯器。
redboot可以引導程式自動運行,用fconfig配置開機檔案,5秒鐘不按鍵自動執行應用
程式(這裡指web server),如果按鍵就進入redboot,此時可以寫入ROMFS檔案系統。這樣,
這個靜態頁面伺服器就比較實用了,上電5秒後自動發佈頁面,可以更換髮布目錄,可以掛
裝不同存放裝置,便攜省電。
使用192.168.0.6/a.pdf可以看pdf檔案,使用192.168.0.6/b.jpg可以看圖片。其實支
持ASP、JavaScript、資料庫、公網動態發布也是可以的,以後再說吧。
//此程式配合IE瀏覽器
#include <network.h>
#include <pkgconf/system.h>
#include <pkgconf/net.h>
#include <cyg/fileio/fileio.h>
#include <cyg/infra/testcase.h>
#define CYGNUM_FS_ROM_BASE_ADDRESS 0x80080000
MTAB_ENTRY( romfs_mte1,
"/",
"romfs",
"",
(CYG_ADDRWORD) CYGNUM_FS_ROM_BASE_ADDRESS );
#ifdef CYGBLD_DEVS_ETH_DEVICE_H // Get the device config if it exists
#include CYGBLD_DEVS_ETH_DEVICE_H // May provide
CYGTST_DEVS_ETH_TEST_NET_REALTIME
#endif
#ifdef CYGPKG_NET_TESTS_USE_RT_TEST_HARNESS // do we use the rt test?
# ifdef CYGTST_DEVS_ETH_TEST_NET_REALTIME // Get the test ancilla if it exists
# include CYGTST_DEVS_ETH_TEST_NET_REALTIME
# endif
#endif
#define STACK_SIZE (CYGNUM_HAL_STACK_SIZE_TYPICAL + 0x1000)
static char stack[STACK_SIZE],stack1[STACK_SIZE];
static cyg_thread thread_data,thread_data1;
static cyg_handle_t thread_handle,thread_handle1;
#define BUF_LEN 10000
void
pexit(char *s)
{
CYG_TEST_FAIL_FINISH(s);
}
void
webserver_test(struct bootp *bp)
{
//struct protoent *p;
//struct timeval tv;
struct sockaddr_in host,client;
int s,sa,e_source,len;
unsigned char buf[BUF_LEN];
unsigned char * p;
int err,fd;
err = mount("","/","romfs");
s = socket(AF_INET, SOCK_STREAM, 0);
if (s < 0) {
pexit("socket");
return;
}
// Set up host address
host.sin_family = AF_INET;
host.sin_len = sizeof(host);
host.sin_addr.s_addr = INADDR_ANY;
host.sin_port = ntohs(80);
if(bind(s, (struct sockaddr *) &host, sizeof(host)) < 0) {
pexit("bind /source/ error");
}
listen(s, SOMAXCONN);
while(true){
//memset(buf, 0, sizeof(buf));
if ((sa = accept(s, (struct sockaddr *)&client, &len)) < 0) {
printf("Accept ERROR!/n");
continue;
}
printf("SERVER : HTTP request arrived from %s:%d/n",
inet_ntoa(client.sin_addr),ntohs(client.sin_port));
len = read(sa, buf, sizeof(buf));
p = &buf[4];
while(*p++ != ' ');
*(p-1) = '/0';
p = &buf[4];
printf("/n%s/n/n",p);
if(strcmp(p,"/") == 0)
{
strcpy(p,"/index.htm");
}
fd = open(p,O_RDONLY);
if(fd == -1)
{
close(fd);
close(sa);
printf("File open error!/n");
continue;
}
len = read(fd,buf,BUF_LEN);
while(len != 0){
len = write(sa, buf, len);
len = read(fd,buf,BUF_LEN);
}
close(fd);
close(sa);
}
}
void
net_test(cyg_addrword_t p)
{
diag_printf("Start Networking Test.../n");
init_all_network_interfaces();
#ifdef CYGHWR_NET_DRIVER_ETH0
if (eth0_up) {
cyg_thread_create(10, // Priority - just a number
webserver_test, // entry
(cyg_addrword_t)ð0_bootp_data, // entry parameter
"Network tcp test", // Name
&stack1[0], // Stack
STACK_SIZE, // Size
&thread_handle1, // Handle
&thread_data1 // Thread data structure
);
cyg_thread_resume(thread_handle1); // Start it
}
#endif
}
void
cyg_start(void)
{
// Create a main thread, so we can run the scheduler and have time 'pass'
cyg_thread_create(10, // Priority - just a number
net_test, // entry
0, // entry parameter
"Network test", // Name
&stack[0], // Stack
STACK_SIZE, // Size
&thread_handle, // Handle
&thread_data // Thread data structure
);
cyg_thread_resume(thread_handle); // Start it
cyg_scheduler_start();
}
--
※ 來源:·水木社區 http://newsmth.net·[FROM: 61.149.56.*]