文件系统实验
【预备知识】
1.文件系统的文件类型
为了便于用户利用终端进行输入和输出,UNIX系统做了专门安排。UNIX系统自动为用户打开3个文件:标准输入、标准输出和标准错误输出文件,文件描述符分别为0、1、2,缺省时,这些文件是重定向到终端的。
LINUX系统问价有如下的分类
宏
文件类型
S_IFREG
普通文件
S_IFDIR
目录文件
S_IFCHR
字符设备文件
S_IFBLK
块设备文件
S_IFIFO
有名管道文件
S_IFLNK
符号链接文件
S_IFSOCK
网络套接字文件
普通文件、目录文件、符号链接文件、有名管道文件、字符设备和块设备文件等。文件的分类主要由其I节点中的i_mode给出。为了能够检查每个文件的类型,Linux系统在sys/stat.h中定于了文件类型struct stat,并用宏定义来解决文件的类型,每个宏的参数就是结构stat中的st_mode的值。表1给出sys/stat.h中定义的文件类型的宏:
Linux系统在sys/stat.h中定义了文件类型struct stat,其定义如下:
表1 宏
struct stat
{
dev_t st_dev; /*文件所在设备;主次设备号*/
ino_t st_into; /*inode*/
mode_t st_mode; /*protection mode*/
nlink_t st_nlink; /*number of hard links*/
uid_t st_uid; /*user ID of owner*/
gid_t st_gid; /*group ID of ower*/
dev_t st_rdev; /*device type(if inode device)*/
off_t st_size; /*total size, in bytes*/
blksize_t st_blksize; /*blocksize for filesystem I/O*/
blkcnt_t st_blocks; /*number of blocks allocated*/
time_t st_atime; /*time of last access*/
time_t st_mtime; /*time of last modification*/
time_t st_ctime; /*time of last change*/
}
2.文件系统的有关系统调用
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char * file_name,struct stat * buf);
int fstat(int filedes,struct stat*buf);
int lsrant(const char * name,struct stat * buf);
这些函数返回有关指定文件的信息。使用这些命令,不需要对文件有任何权限,只需要对指定目录有检索权限。
stat()函数统计由文件名指定的文件信息,并填充到以steuct stat为结构的buf中;
lstat()函数与stat()函数功能一样,只是仅仅统计文件的符号链接时有所不同。
fstat()函数与stat()函数功能一样,只是仅仅统计被打开的文件的类型。
3.创建新文件creat()
LINUX系统把所有文件都看成无结构的字符流式的文件,如果特定的应用中使用了某种类型的数据结构,只能由程序员对数据增加相应的结构。这样使得UNIX对文件的接口变得特别简单。
创建一个新文件要使用如下的语句序列:
#include<sys/ types.h >
#include<sys/ stat.h >
#include<sys/ fcntl.h >
Int open(const char * pathname ,int oflg[,mode_t mode]);
返回值:如果正确创建,返回文件的描述符;否则返回-1。
pathname是要创建文件的路径名。创建文件时,文件只能以只写方式打开,mode用来规定该文件的拥有者,小组用户及其他用户的访问权限。要求用按位逻辑加对下列符号常量(这些符号常量定义在sys/stat.h中)进行所需的组合;
S_IRUSR /*文件拥有者的读权限位,即0400*/
S_IWUSR /*文件拥有者的写权限位,即0*200*/
S_IXUSR /*文件拥有者的执行权限位,即0100*/
S_IRGRP /*小组用户的读权限位,即0040*/
S_WGRP /*小组用户的写权限位,即0200*/
S_IXGRP /*小组用户的执行权限位,即0010*/
S_IROTH /*其他用户的读权限位,即0004*/
S_IWOTH/*其他用户的写权限位,即0002*/
S_IXOTH /*其他用户的执行权限位,即0001*/
3个有用的按位加组合定义如下:
S_IRWXU定义为(S_IRUSR|S_IWUSR|S_IXUSR)/*即0700*/
S_IRWXG定义为(S_IRGRP|S_IWGRP|S_IXGRP)/*即0070*/
S_IRWXO定义为(S_IROTH|S_IWOTH|S_IXOTH)/*即0007*/
创建文件时,若文件已经存在,只要执行进程对文件所在目录有执行权,并对该文件有写权,系统使原文件的长度置0,释放文件占用的磁盘块,同时保持原文件的mode不变。
4.删除文件unlink()
一个文件可以有多个路径,也即一个inode可能链接多个文件的目录项。系统调用unlink()的作用就是删除指定的目录项。也即将inode中的链接计数nlink减1.若链接计数减1后为0,且无进程正在使用,立即删除该文件(即释放文件的目录项,inode和文件占用的磁盘块),此后,文件不再存在;若链接计数减1后为0,且有进程正在使用,则立即返回,等进程使用完后,再删除;若链接计数减1后不为0,则立即返回。
5.打开文件open()
打开文件使用和creat()相同的头文件序列;
#include<sys/ types.h >
#include<sys/ stat.h >
#include<sys/ fcntl.h >
Int open(const char * pathname ,int oflg[,mode_t mode]);
返回值:如果正确打开,返回文件的描述符;否则返回-1.
其中,参数pathname是待打开的文件路径名;参数oflg规定了打开文件方法;仅当打开的文件不存在,需创建时,才使用mode这个参数规定文件的拥有者、小组用户、其他用户对新文件的访问方式。参数oflg可能的取值为下列3个值之一;
O_RDONLY : 以只读方法打开;
O_WRONLY:以只写方法打开;
O_RDWR;以读写方法打开。
该系统调用还允许将oflg与下列标志值进行按位逻辑加,以扩充其功能;
O_APPEND;当为(O_WRONLY | O_APPEND)时,打开文件允许向文件尾追加写;否则为覆盖写,即从文件头写。
O_CREAT;当为(O_WRONLY | O_CREAT)时,若打开的文件不存在,则创建文件,且必须带有参数mode,以规定文件的拥有者、小组用户、其他用户对新文件的访问方式。
O_EXCL;当为(O_WRONLY | O_CREAT | EXCL)时,若打开的文件不存在,则创建文件,且必须带有参数mode,以规定文件的拥有者、小组用户、其他用户对新文件的访问方式。当打开的文件存在时,则出错。使用这个参数可用来检查文件是否已经存在。
O_TRUNC;当为(O_RDWR | O_TRUNC) 或(O_WRONLY | O_TRUNC)时,将文件的长度截成0,文件的属性不变。
O_SYNC:文件以同步方式打开。若文件以同步方式打开时,进程每次进行write()调用时,都立即修改文件,即进程等待write()调用完成后才能继续执行。
6.文件关闭close()
文件关闭的系统调用格式如下:
# include <unistd.h>
int close( int fd);
其中,fd为文件的文件描述符。
返回值:成功时为0,失败时为-1.
7.读文件read()和写文件write()
任何文件在读写之前,必须先打开。文件被成功打开时,返回文件的描述符。只要文件是用O_RDONLY或O_RDWR或O_WRONLY标志打开,就可以用read()或write()系统调用从文件中读或向文件中写若干字节。这两个系统调用可能使用的语句序列如下:
# include <unistd.h>
ssize_t read( int fd, void * buf, size_t nbytes);
或
ssize_t write( int fd, conts void * buf, size_t nbytes);
返回:正确是为0或读写的字节数;错误时为-1.
其中,fd为文件的描述符,buf为读出货写入文件数据的字节数组,第三个参数是要传送的字节个数。例如:
Internet fd,n,nread,nwrite;
char buf[size];
nread=read(fd,buf,n)
或
nwrite=write(fd,buf,n);
进行读时,nread为读函数执行完后返回的字节数。返回的字节数可以少于要求读的字节数。当文件为终端时,一般read只读到下一个换行符为止。通常要少于请求的字节数。当返回值为0时,表明文件结束。-1表明出现了错误。如果文件尺寸不是buf的整倍数,某次read命令将返回一个较小的数,这一数即为write命令写入的字节数;下一次调用read时,将返回0。
进程写时,返回值为实际写的字节数;如果返回值与实际写的字节数不相等时,则出现错误。
磁盘缓冲块尺寸为512字节或1024字节(可参看stdio.h中的定义)。
7.文件的随机存取lseek(int fd,long off,int sbase)
#include<unistd.h>
#include<sys/types.h>
Int lseek(int fd,long off,int sbase);
Sbase的可能取值对文件读写指针的影响如下:
SEEK_SET=0,文件的读写指针置为off;
SEEK_CUR=1,文件的读写指针置为off+当前文件的读写
编程实现例<一>
【任务】
利用表1给出的宏来检查给定文件的类型。
【程序】
#include <sys/types.h> #include <sys/stat.h> #include “unistd.h” int main(int argc,char * argv[]) { int I; struct stat bf; char * ptr; for (i=1,i<argc;i++) { printf(“%s:”,argv[i]); if (lstat(argv[i],&bf)<0)//统计符号链接本身的信息 { printf(“lstat error”); Continue; } If ((S_IFMT & bf.st_mode)==S_IFREG) Ptr=“regular”; /*else if ((S_IFMT&bf.st_mode)==S_IFDIR) Ptr=“directory”; else if ((S_IFMT&bf.st_mode)==S_IFCHR) Ptr=“character special device”; else if ((S_IFMT&bf.st_mode)==S_IFBLK) Ptr=“block special device”; else if ((S_IFMT&bf.st_mode)==S_IFFIFO) Ptr=“pipe special file”; */ Else Ptr=“****unknown file mode***”; Printf(“%s:\n”,ptr); } Exit(0); }
修改后代码:
【运行结果】
编程实现例<二>
【任务】
cat命令的简单实现,完成从键盘复制到终端显示器。
【程序】
# include <unistd.h> # define SIZE 512 main() { char buf[SIZE]; int n; while((n = read(0,buf,sise of buf))>0) write(1,buf,n); exit(0); }
修改后代码:
【运行结果】
【分析】
编程实现例<三>
【任务】
open和create系统调用的使用。一个用C语言的复制文件的例子。
【程序】
#include <stdio.h> #include <sys/types.h> #defin PERM 0644 /*rw for owner,r for group and other*/ char * progname; main(argc,argv) int argc char * argv[]; { int f1,f2,n; char buf[BUFSIZ]; progname = argv[0]; if (argc! = 3) printf(“Usage:%s from to”,progname); if(f1 = open(argv[1],0)==-1) printf(“can’t open %s”, argv[1]); if((f2=creat(argv[2],PERM)—1) printf(“can’t creat %s”,argv[2]); while((n=read(f1,buf,BUFSIZ))>0) if (write(f2,buf,n)! =n) printf(“write error”,(char * )0); close(f1);close(f2); exit(0); }
修改后代码:
【运行结果】
编程实现例<四>
【任务】
请编写一个程序,采用随机读写文件的系统调用,从文件的某个位置开始对文件进行读或写。
【程序】
#include<unistd.h> #include<sys/types.h> Main(int argc,char * argv[]) { If((fd=open(argv[1],O_RDONLY))==-1) exit(1); lseek(fd,184L,0); where=lseek(fd,0L,1); printf(“NOW new pointer value is %d.\n”,where); while((where=read(fd,&cl,1==1) { printf(“ %c,”,cl); } Printf(“.\n”); }
修改后代码:
【运行结果】
总结:
在实验中,我利用代码模拟了文件系统的基本操作,包括文件的创建、打开、读写、关闭和删除等。通过深入使用系统调用,如open、read、write、close等,我得以深刻体验到Linux内核与文件系统之间的交互过程。这一过程中,我不仅了解到了这些基本操作在内核中的实现细节,还感受到了Linux系统对于文件管理的严谨性和高效性。在文件随机访问的实验环节,我使用lseek系统调用来移动文件指针,从而实现了对文件任意位置的读写操作。