转载自http://club.it.sohu.com/read_elite.php?b=program&a=186956
第四章:文件系统
UNIX所管理的机器一般是大型机而不是PC。所管理的硬盘一般也非常大。所以一般分成几个区,每个区都有其单独的文件系统。比方说你能大概能找到这样的一些文件
/dev/sd/c0t0d0s0
/dev/sd/c0t0d0s1
... ...
当UNIX启动的时候,分区被挂装(mount)并统一成一个树状的文件系统
分区的物理构造咱们暂且放在一边,先写个程序,读一下分区信息试试。
/* ndf.c 计算参数所指定的分区的剩余空间比率 */
#include
#include
#include
int main(int argc , char *argv[])
{
struct statvfs buf[1];
sync();
if( statvfs(argv[1],buf)!=0 )
{
fprintf(stderr , Cannot read super block !\n);
exit(1);
}
fprintf(stderr , %4.1f %% free\n,
(float)buf[0].f_bfree / buf[0].f_blocks*100 );
return 0;
}
编译执行:
%./ndf /
49.8 % free
这里用了一个statvfs函数。其具体如下:
#include
#include
int statvfs( char *path , struct statvfs *buf );
返回值: 成功时: 0
失败时: -1
还有一个sync()函数。用来更新分区的super block;
void sync();
UNIX系统为了加快处理速度,将分区的super block信息读到内存中保存。sync()函数就是把在内存中保存的super block信息再写回到硬盘上去。
UNIX系统使用好几种文件系统。有S5,ufs,VxFS等等。虽然这些文件系统的构造非常不同,但一通百通,咱们在这几篇贴子里只讨论一下比较容易理解,而且“经典的”S5文件系统。(别的我也不会。呵呵)
S5: 文件名最长14字节,构造简单
ufs: 文件名最长255字节,BSD所用的文件系统。
VxFS: Veritas Softwave公司开发的文件系统。出现错误时可以快速恢复。由于这种文件系统保证文件在硬盘上连续存放,所以处理速度很快。
现在说一下S5分区的构造
一个分区包含如下四部分(按顺序):
[ boot block ]
[ super block ]
[ i node block ]
[ data block ]
boot block :
这个部分在分区的最开始处,用来存放引导程序。就算是不能引导的分区一样有boot block,这个时候这部分就没有用了。不过一般这部分也不大。大多数只有512或者1024字节。
super block :
super block在boot block之后,用来存放这个分区全体的管理信息。上面那个ndf.c就是读的这部分所存储的信息。里边存放了i node block的大小,free block数组等等。根据这些信息可以得知data block的开始位置。
i node block :
i node是index node的缩写。i node block就是存放i node的部分
UNIX把一切都看成是个文件。包括目录以及设备等等的所有的文件都有一个i node号,作为这个文件的管理信息。文件本身存在于数据区,但是i node号存在i node block里。主要包含文件的模式,链接数,文件所有者,文件大小,在硬盘上的位置,最后读写时间,最后更新时间等信息。
为了加快存储速度,系统会把一定数量的i node存至内存。UNIX系统不一样,存多少也就不一样。
data block :
这部分就是存放数据本身的了。这部分被分成一定大小的块,如同DOS的扇区一样。一般大小是1024字节,分到4096的也有。
解说到这里,我们再来写个程序。打开一个目录,然后把这个目录下所有的文件的i node号及文件名输出来。
/* nls.c */
#include
#define DIRSIZ 14
int main( int argc , char *argv[] )
{
struct dir
{
int i_no;
char f_name;
};
struct dir dir_data[1];
FILE *fp;
fp=fopen(argv[1],r);
while( fscanf(fp,%i%s,&(dir_data[0].i_no),dir_data[0].f_name)!=EOF )
{
printf(%i %s\n , dir_data[0].i_no , dir_data[0].f_name );
}
fclose(fp);
return 0;
}
%./nls /
... ...
2048 usr
2049 home
... ...
别忘了,在UNIX下,目录也当成文件。
最近,为了使目录的格式变得通用而不再依赖于操作系统,程序中大多使用统一的格式。这种情况下,我们最好就不直接用fopen()打开目录,而使用opendir(),readdir()等函数比较好。重写一下上面的程序。
/* nls2.c */
#include
#include
int main( int argc , char *argv[] )
{
DIR *fp;
struct dirent *p;
fp=opendir(argv[1]);
while( (p=readdir(fp))!=NULL )
{
printf(%i %s\n, p->d_ino , p->d_name );
}
closedir(fp);
return 0;
}
执行结果和上面一样。函数说明如下:
#include
DIR *opendir( char *dirname ); 打开一个目录,成功时返回DIR结构体的地址,失败返回NULL
struct dirent *readdir( DIR *dirp ); 取得打开目录中下一个文件的信息,成功时返回下一个文件信息的地址,失败时,或者读至末尾时返回NULL
int closeidr( DIR *dirp ); 关闭目录,成功时返回0,失败时返回-1
注意:readdir在成功地读出一项之后,会自动指向下一项。假设你有如下程序:
struct dirent *p;
p=readdir(fp);
p++; //千万不要像这行这样写。你无法保证这目录里的文件信息是连续存放的。
你只要一遍一遍地用readir()这个函数就行了。它帮你全搞定