发信人: 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.*]