發信人: gdtyy (gdtyy), 信區: Embedded
標 題: 第七講 文件系統
發信站: 水木社區 (Mon Jun 25 23:32:53 2007), 站內
*******************
* 第七講 文件系統 *
*******************
2007/01/06 [email protected] 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.*]