注:本文来自“网易”博主
第三章:文件系统
这回我们来说一下UNIX的文件系统。由于一般情况下UNIX机的硬盘会很大,所以一般你可以给它分成几个区,而每个分区又都可以有独立的文件系统。如果你是UNIX系统,你有可能看见/dev/sd/c0t0d0s0/dev/sd/c0t0d0s1
这样的文件。
如果你是linux你有可能看见
/dev/hda0/dev/hda1
等等。这些文件,每一个就可以看成是一个分区。当计算机启动时,系统先找到/(根目录),然后系统就像挖土豆子一样找到一个带出来一串似的,顺着根目录一带就带出来挂载在根目录下的所有目录(详细的请查一下/etc/fstab这个文件)。在UNIX下叫目录(directory)的感念,就像是windows下的文件夹(folder),但与windows最大的区别在于UNIX没有A盘,C盘这样的概念。它所有的文件都要挂载到根目录下的某个子目录底下,另外UNIX把所有设备都看成是文件。比如A盘就有可能是/dev/fd0这个文件。那比如我们想使用软盘怎么办呢,以为软盘是个临时文件,所以在开机时一般不会设为自动挂载。所以我们要手动挂载它在这个某一个目录下。有可能我说得大家有点糊涂,不要紧我们举个例子。文件系统就像一棵大树,树干只有一个那就是根目,树干往上是树杈就是一个一个的子目录,树杈往上有可能还是树杈,那就是这个子目录的子目录,也有可能是树叶,树叶就是文件。现在你手里有一根树枝上面有几个叶子,可你拿手攥着它,它是不会得到来自这颗大树的养分的,除非你把它嫁接在大树的某一个树杈上。就是这个道理。
回到刚才的话,具体怎么能使用软盘呢。
$mount /dev/fd0 /mnt/floppy
这样你就把这个小树杈,嫁接到了/mnt/floppy上。等你用完了软盘输入
$umount /mnt/floppy
就等于把这个树枝又掰折了。
说了这么多,换换口味看一个程序,让我们来调查一下分区的剩余空间有多少
#include <stdio.h>#include <sys/types.h>#include <sys/statvfs.h>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;}
编译执行: $./a.out36.7 % free
这里用到了statvfs()这个系统调用。它是用来得到文件系统总体信息的系统调用。得到的信息它会放在一个叫做statvfs的结构体里。这时候结构体里的f_bfree表示的就是空闲的block数,f_block表示的就是所有block数。他们相除就得到了使用比,是不是很简单呢。这里注意,statvfs系统调用返回的是一个叫做statvfs的结构体,名字相同别弄混了。下面把statvfs()的格式写一下。
#include<sys/types.h>
#include<sys/statvfs.h>int statvfs( char *path , struct statvfs *buf );返回值: 成功时:0失败时:-1
结构体statvfs里还有好多关于这个分区的信息,感觉就像windows下查看C盘属性的功能差不多,但还要强大,大家可以自己上机man一下。我就不一一说了。
上面另一个系统调用sync(),是用来将内存上的硬盘信息回写到硬盘上而用的。这时因为UNIX系统为了能快速查找硬盘信息,而将super block(下面要讲)信息放在内存里。它是一个没有参数也没有返回值的系统调用。
另外还有一个命令也叫sync,作用和sync()是一样的。
上面说到了一个概念叫super block,下面我们就说说它。
一般,一个分区包含有这么几个部分:boot block,super block,i node block,data block。我们一个一个来说说。
boot block :
它总是在每个分区的最前面,用来存放开机引导程序。大小是512或1024字节。LINUX下我们常用的LILO等就放在这里。
super block :
super block用来存放这个分区全体的管理信息。我说说关于i-node的信息,别的大家可以查查任何一本关于UNIX文件系统的书,都会有的。super block里有一个存放空i-node号的数组。如果我们建立新文件时,系统就可以知道现在哪些i-node号是可用的。上面我们说了,super block的信息是常驻内存的,这样系统建立新文件时就不用去读盘,而直接从内存里调出来就可以了,提高了系统运行速度。
i node block :
i node block就是存放i-node的部分。i-node即index node是简称。里面装的关于每一个文件的属性,就好像windows里选文件点右键,查看属性里显示的东西。比如,文件的所有者,权限,大小,修改日期,硬盘上的位置等等。
data block :
这里存放就是货真价实的数据了。没什么好说的了
我们再看一个程序。它是用来将i-node号及文件名输出来。
#include <stdio.h>#include <dirent.h>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;}
执行
$./a.out /96673 sbin966721 dev998945 root11 lost+found2 ..2 ...等等..1031169 lib
好下面我们来说说上面的几个系统调用
opendir()
作用:打开一个目录#include <dirent.h>#include <sys/types.h>DIR *opendir( char *dirname );返回值 成功时返回DIR结构体的地址,失败返回NULL
readdir()
作用:读取目录信息到DIR结构体。DIR结构体是什么大家也可以查一下man 3 readdir#include <linux/types.h>#include <linux/dirent.h>struct dirent *readdir(DIR *dir);返回值:成功是返回DIR的地址,失败返回NULL
closedir()
作用:关闭目录#include <sys/types.h>#include <dirent.h>int closedir(DIR *dir);返回值:成功时为0,失败为1
书接上回,上次我们说到了UNIX文件系统的结构,知道了有哪几个block,各是干什么用的,这回我们说说怎样对文件进行操作。 我们一般对文件进行操作时不是直接指定i-node号的,而是指定文件的路径的。然而,在内核对文件的操作却是通过i-node来进行的。那么问题就来了,系统又是如何将路径和i-node联系起来从而又找到硬盘上数据存放的具体位置的呢。
我来做一下解释,比如你要访问/home/user1/test.c这个文件。系统从根目录开始查找,根目录的i-node号是2这个是确定的。系统就去i-node block里找2号i-node,看过上次贴的朋友应该知道,它会告诉系统根目录信息数据存放在硬盘上的具体位置,里面会有根目录下的文件与i-node的对应表,就像我们上次那个程序里看到的那样。系统从而得到了home目录的i-node号然,然后再去硬盘看home目录下的文件与i-node的对应关系,这样也就找到了test.c在硬盘上的具体位置。
其实如果test.c这个文件很大那它在硬盘上也不是放在一起的。data block里其实是一个一个的小数据块,数据就放在这里。但这个块的大小是一定的有1024字节的也有2048,4096的这根据你的分区方法不同也不太一样。如果你的文件比较大那么一个数据块里就放不下它,这就需要更多的块。打个比方,你有130万RMB(好比一个文件),这么多钱可别被贼偷了去,要把它们放到保险柜(好比数据块)里,但保险柜太小一个只能放50万。那你需要几个保险柜呢。(好像是小学时候做的应用题)答案是3个,最后一个装了只装了30万。浪费了20万的空间。那我接着要问这时我正好有20万(好比另一个文件)也要放进保险柜,那我们一共要多少个保险柜呢。如果你说还是3个那你就错了。虽然你有一个保险箱浪费了20万的空间,但其他人的钱(其他文件)也是不能放进去的,否则谁也分不清里面哪些钱是你的哪些钱是我的了。所以我的20万也好即使是一块钱也需要一个新的保险柜。
下面我们举个例子来看看如何用i-node里面的信息。这个程序是将我们指定的两个文件名中输出更新时间晚的一个
#include <sys/types.h>#include <sys/stat.h>#include <stdio.h>int main( int argc , char *argv ){ struct stat buf[2] , *p ; if( argc!=3 ) { fprintf( stderr , "Usage : %s file1 file2\n" , argv[0] ); exit(1); } p=buf; if( stat(argv[1],p)!=0 ) { fprintf( stderr , "%s not found !\n" , argv[1] ); exit(1); } p++; if( stat(argv[2],p)!=0 ) { fprintf( stderr , "%s not found !\n" , argv[2] ); exit(1); } if( buf[0].st_mtime > buf[1].st_mtime ) //比较更新时间 printf( "%s\n" , argv[1] ); else printf( "%s\n" , argv[2] ); return 0;}
大家可以自己执行一下看看结果。
我们来说说stat(),使用它可以得到一个叫stat的结构体,它包含了i-node里面的一部分信息。诸如,文件的权限,i-node号,链接数,所有者id,组id,文件大小等等。具体的大家可man stat查看一下。
作用:得到一个stat结构体。 #include <sys/types.h>#include <sys/stat.h>int stat( char *path , struct stat *buf );返回值: 成功时:0 失败时:-1
为了简洁,我们的程序里没有考虑到更新时间相同的文件。大家可以自己补上。
下面我们做一个改变文件权限的程序。
#include <sys/types.h>#include <sys/stat.h>#include <stdio.h>#define MASK 0555 //设置掩码int main( int argc , char *argv[] ){ struct stat buf[1]; mode_t mode; if( argc!=2 ) { fprintf( stderr , "Usage : %s file\n" , argv[0] ); exit(1); } if( stat(argv[1],buf)!=0 ) { fprintf( stderr , "Cannot read i-node\n" ); exit(1); } mode = ( buf[0].st_mode & MASK ); if ( chmod(argv[1],mode)!=0 ) //改变文件的权限 { fprintf( stderr , "Cannot change mode\n" ); } return 0;}
怎么样这个程序不能我直接说里面的系统调用
chomd()
作用:改变文件的权限(关于权限下面要说)#include <sys/types.h>#include <sys/stat.h>int chmod( char *path , mode_t mode );返回值: 成功时:0 失败时:-1
那什么是文件的权限呢?你可以用
$ls –l /
命令来查看根目录下所有子目录的权限。你会看见类似下面这样的
drwxr-xr-x 2 root root 4096 11-04 22:43 bindrwxr-xr-x 4 root root 1024 09-27 02:15 bootdrwxr-xr-x 10 root root 3640 11-11 15:08 devdrwxr-xr-x 88 root root 12288 11-11 15:08 etcdrwxr-xr-x 3 root root 4096 09-27 02:40 homedrwxr-xr-x 11 root root 4096 10-31 21:49 libdrwx------ 2 root root 16384 09-27 10:51 lost+founddrwxr-xr-x 2 root root 4096 09-27 20:27 mediadrwxr-xr-x 2 root root 4096 2006-02-11 miscdrwxr-xr-x 2 root root 4096 2006-02-12 mntdrwxr-xr-x 2 root root 0 11-11 15:08 netdrwxr-xr-x 2 root root 4096 2006-02-12 opt
我们看见的每行最前面的那一串字符就是表示权限的。其中第一位表示文件的类型(d:目录,-:一般文件,l:链接,c:字符设备,b:块设备,最后在/dev目录中很常见,是表示设备的)。其后有九位,三位一组,第一组(2-4位)是说明文件所有者的权限的,第二组(5-7)位是说明与文件所有者同group其他用户对这个文件的权限,第三组(8-10)是所有人对这个文件的权限。每组的第一位是r表示读,第二位是w表示写,第三位是x表示可执行,如果某一位上是-说明就没有该位的相应权限。比如说/bin与root同group的人就没有写的权限因为它的是r-x,中间本应是w的那一位是-。
我们说的在详细点比如一个文件有这样的一个权限
rwxr-xr--
我们把它先分组
rwx r-x r--所有者 同group者 其他人
这样我们就可以清楚地看见谁有什么样的权限了。接着我们把有权限的为写成1,-位写成0的话会变成
111 101 100
这种形式,我们把它们化成十进数就变成了
7 5 4
所以我们也可以说这个文件的权限是754了。这回你应该明白为什么有时候说某个文件的权限是777,444,654等等了吧。下回看见了别再说它是777老虎机了。