什么是VFS(Virtual File System)?
异构文件系统之上的软件粘合层。通过VFS可以为访问文件系统的系统调用提供统一的抽象接口,实现无缝使用多个不同类型的文件系统
虚拟文件系统的特点
不同文件系统不是通过设备标识符访问,而是连成一个单一树型结构,用一个根目录表示文件系统;文件系统按需挂载在Linux虚拟文件系统;采用标准的UNIX(POSIX)系统调用读、写位于不同物理介质上的文件系统;使用户很容易地在不同文件系统之间进行数据交换和管理,并在Linux上任意挂载多种不同类型文件系统。虚拟文件系统举例 例1:cdrom在linux中的挂载
mount –t iso9660 /dev/cdrom /mnt/cdrom
例2:nfs在linux中的挂载
mount –t nfs 127.0.0.1:/mnt/nfs /mnt/nfs
文件的访问方式
系统调用——非缓冲文件操作 基于POSIX(Portable Operating System Interface)标准,采用C语言,实现了为应用程序提供与操作系统内核通信的服务的API 为用户提供有关操作系统的设备管理、I/O系统、文件系统和进程控制、通信及存储管理等方面的功能。
ANSI C库函数——缓冲文件操作 程序员在编程时,利用C库让操作系统为其提供文件访问等服务。
系统调用、C库的联系
POSIX 文件访问调用
文件流和文件描述符文件流:stdin、stdout、stderr 功能1:实现不同输入输出格式转换。 功能2:缓冲功能,将数据读写集中,减少系统调用次数。(查看—isbuff.c、设置—setbuff.c) 全缓冲:在缓冲区满或者调用刷新函数后,进行I/O系统调用。 行缓冲:遇到换行符或缓冲区满,才I/O系统调用。 无缓冲:例如标准出错流。文件描述符:0、1、2isbuff.c #include <stdio.h> void pr_stdio(const char *,FILE *); int main(void) { FILE *fp; fputs("enter any character\n",stdout); if (getchar() == EOF) printf("getchar error"); fputs("one line to standard error \n",stderr); pr_stdio("stdin",stdin); pr_stdio("stdout",stdout); pr_stdio("stderr",stderr); if ((fp = fopen("/etc/motd","r")) == NULL) { printf("fopen error"); } if (fgetc(fp) == EOF) printf("getc error"); pr_stdio("/etc/motd",fp); return 0; } void pr_stdio(const char *name,FILE *fp) { printf("stream=%s,",name); if (fp->_flags & _IO_UNBUFFERED) printf("unbuffered"); else if (fp->_flags & _IO_LINE_BUF) printf("line buffered"); else printf("full buffered"); printf(",buffer size=%d\n",fp->_IO_buf_end-fp->_IO_buf_base); } setbuff.c #include <stdio.h> #include <error.h> int main(int *argc,char **argv) { int i; FILE *fp; char msg1[]="hello,world\n"; char msg2[]="hello \n world"; char buf[128]; if ((fp = fopen("no_buf1.txt","w")) == NULL) { perror("file open failure"); return -1; } setbuf(fp,NULL); memset(buf,'\0',128); fwrite(msg1,7,1,fp); printf("test setbuf(no buf)!check no_buf1.txt\n"); printf("now buf data is : buf = %s\n",buf); printf("press enter to continue\n"); getchar(); fclose(fp); if ((fp = fopen("no_buf2.txt","w")) == NULL) { perror("file open failure"); return -1; } setvbuf(fp,NULL,_IONBF,0); memset(buf,'\0',128); fwrite(msg1,7,1,fp); printf("test setbuf(no buf)!check no_buf2.txt\n"); printf("now buf data is : buf = %s\n",buf); printf("press enter to continue\n"); getchar(); fclose(fp); if ((fp = fopen("l_buf.txt","w")) == NULL) { perror("file open failure"); return -1; } setvbuf(fp,buf,_IOLBF,sizeof(buf)); memset(buf,'\0',128); fwrite(msg2,sizeof(msg2),1,fp); printf("test setbuf(line buf)!check l_buf.txt,because line buf,only data before enter send to file\n"); printf("now buf data is : buf = %s\n",buf); printf("press enter to continue\n"); getchar(); fclose(fp); if ((fp = fopen("f_buf.txt","w")) == NULL) { perror("file open failure"); return -1; } setvbuf(fp,buf,_IOFBF,sizeof(buf)); memset(buf,'\0',128); fwrite(msg2,sizeof(msg2),1,fp); printf("test setbuf(full buf)!check f_buf.txt\n"); printf("now buf data is : buf = %s\n",buf); printf("press enter to continue\n"); getchar(); fclose(fp); }文件描述符和文件流的相互转换—fileno
函数名:fileno所在头文件:stdio.h原型声明: int fileno(FILE *stream);作用: 获取文件流底层文件描述符。形参: stream :文件流返回值: 调用成功,返回文件流所使用的文件描述符;不成功,返回-1。相关例子:fileno.c #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> int main(int argc,char **argv) { int fp1,fp2; printf("stdin is:\t %d\n",fileno(stdin)); printf("stdout is:\t %d\n",fileno(stdout)); printf("stderr is:\t %d\n",fileno(stderr)); if ((fp1 = open("/root/1",O_WRONLY)) == -1) { perror("open"); exit(EXIT_FAILURE); } if ((fp2 = open("/root/2",O_WRONLY)) == -1) { perror("open"); exit(EXIT_FAILURE); } printf("1 file is :\t %d\n",fp1); printf("2 file is : \t %d\n",fp2); close(fp1); close(fp2); return 0; }文件描述符和文件流的相互转换—fdopen
函数名:fdopen
所在头文件:stdio.h
原型声明: FILE *fdopen(int fildes, const char *mode);
作用: 在一个已打开的文件描述符的基础打开一个文件流。
形参: fildes :文件描述符 mode:打开文件流的模式与fopen一样。
返回值: 调用成功,返回一个新的文件流指针;不成功,返回NULL。
相关例子:fdopen.c
#include <stdio.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> int main(void) { int fd; FILE *stream; unlink("text.txt"); fd = open("text.txt",O_CREAT|O_WRONLY,S_IREAD|S_IWRITE); stream = fdopen(fd,"w"); if (stream == NULL) printf("fdopen failed\n"); else { fprintf(stream,"hello world\n"); fclose(stream); } printf("the content of the text.txt is :\n"); system("cat text.txt"); return 0; }ANSI C标准文件I/O操作 fopen、fclose、fflush函数 fgetc、getc和getchar函数 fputc、putc和putchar函数 fgets和gets函数 fputs和puts函数 fread、fwrite函数 feof、ferror函数 fseek、ftell函数 格式化处理 printf、fprintf和sprintf函数 scanf、fscanf和sscanf函数
open系统调用 close系统调用 write系统调用 read系统调用 fcntl系统调用 lseek系统调用 lstat、fstat和stat系统调用 dup、dup2系统调用
open函数
系统调用名:open
头文件:unistd.h
功能:打开path所指的文件。 int open(const char *path,int oflags) int open(const char *path,int oflags,mode_t mode)
参数 path:文件所在路径 oflags :设置open文件操作类型 mode :设置open文件模式
返回值:打开成功,返回一个唯一的文件描述符;不成功,返回-1,并设置相应的errno变量以指明出错的原因。
oflags的说明 mode的说明
文件类型宏定义和宏测试 文件类型宏测试定义
文件类型掩码定义
close函数
系统调用名:close头文件:unistd.h功能:将与fildes文件描述符相关联的文件断开。 int close(int fildes) fildes:文件描述符返回值:成功,返回值为0;不成功,返回值为-1。fcntl函数
头文件: #include <fcntl.h>
原型声明:int fcntl(int fildes, int cmd, …);
作用:修改某个文件描述符的特性
形参:fd—需要修改属性的文件描述符; cmd—常用操作 F_DUPFD:复制文件描述符 F_GETFD、F_SETFD:获取或者设置与文件描述符关联的close-on-exec标志。 F_GETFL、F_SETFL:获取或者设置文件状态标志和访问模式
fcntl函数举例 例1、如何使用? fcntl(fildes, F_DUPFD, newfd); fcntl(fildes, F_GETFD); fcntl(fildes, F_SETFD, flags); fcntl(fildes, F_GETFL); fcntl(fildes, F_SETFL, flags); 例2、实际应用 fcntl_dup.c
#include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> int main(int argc,char **argv[]) { char *ptr = "hello world\n"; int fd_open,fd_dup; if ((fd_open = open("tmp1",O_WRONLY|O_CREAT,0644)) == -1) { perror("open"); exit(EXIT_FAILURE); } fd_dup = fcntl(fd_open,F_DUPFD); printf("fd_open = %d , fd_dup = %d\n",fd_open,fd_dup); printf("write %d bytes to fd_open\n",write(fd_open,ptr,strlen(ptr))); printf("write %d bytes to fd_dup\n",write(fd_dup,ptr,strlen(ptr))); close(fd_open); close(fd_dup); printf("now content of file:\n"); system("cat tmp1"); return 0; }write函数
系统调用名:write头文件:unistd.h功能:将buf中的前nbytes个字节的数据写入到与fildes相关联的文件描述符中。 size_t write(int files,const void *buf,size_t nbytes)参数: files:文件描述符 buf:数据的来源 nbytes:数据的长度(以字节为单位)返回值:写入成功,返回值的范围0~nbytes;不成功,返回-1,对应错误代码保存在全局变量 errno中.write系统调用举例 fcntl_getfl.c #include <sys/types.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> int main(int argc,char **argv[]) { int accmode,val; if (argc != 2) printf("the argc must equal to 2\n"); if ((val = fcntl(atoi(argv[1]),F_GETFL,0)) < 0) { perror("fcntl"); exit(EXIT_FAILURE); } accmode = val & O_ACCMODE; if (accmode == O_RDONLY) printf("read only \n"); else if (accmode == O_WRONLY) printf("write only \n"); else if (accmode == O_RDWR) printf("read write \n"); else if (accmode == O_RDONLY) printf("read only \n"); else printf("unknown mode\n"); if (val & O_APPEND) printf(",append"); if (val & O_NONBLOCK) printf(",nonblocking"); return 0; }read函数
系统调用名:read头文件:unistd.h功能:将与fildes文件描述符相关联的文件的前nbytes个字节的数据读入buf中 size_t read(int fildes,const void *buf,size_t nbytes)参数: fildes:文件描述符 buf:数据的来源 nbytes :数据的长度(以字节为单位)返回值:读取成功,返回值的范围0~nbytes;不成功,返回-1,对应错误代码保存在全局变量errno中。read函数举例lseek函数
头文件:unistd.h功能:设置与文件描述符fildes相关的文件或设备的读写指针位置 off_t lseek(int fildes,off_t offset,int whence)参数: fildes:文件描述符 offset:设置的参照位置 SEEK_SET:文件开头位置; SEEK_CUR:当前位置; SEEK_END:文件末尾位置; whence:相对于参照位置的偏移量返回值:成功,返回值为从文件头到设置的位置的偏移量(以字节为单位);不成功,返回值为-1。读取文件属性:stat、lstat、fstat 用户名与UID转换:getpwuid、getpwnam 组名与GID转换:getgrgid、getgrnam 读取软链接源文件:readlink 内存映射:mmap、munmap
#include<stdio.h> #include<pwd.h> #include<stdlib.h> int main(int argc,char *argv[]) { struct passwd *ptr; uid_t uid; uid=atoi(argv[1]); ptr=getpwuid(uid); printf("name:%s\n",ptr->pw_name); printf("passwd:%s\n",ptr->pw_passwd); printf("home_dir:%s\n",ptr->pw_dir); }*stat函数
系统调用名—fstat/stat/lstat所在头文件—unistd.h、sys/stat.h、sys/types.h作用—获取文件的相关状态信息。 int fstat(int fildes,struct stat *buf) int stat(const char *path,struct stat *buf) int lstat(const char *path,struct stat *buf)参数: fildes:文件描述符 path:文件的路径 buf:结构体struct stat的大小struct stat的内容读取文件属性举例 读取文件类型 lstat_example.c
#include <sys/types.h> #include <sys/stat.h> #include <stdlib.h> #include <stdio.h> int main(int argc, char *argv[]) { int i; struct stat buf; char *ptr; for (i = 1; i < argc; i++) { printf("%s: ", argv[i]); if (lstat(argv[i], &buf) < 0) { perror("lstat"); continue; } if(S_ISREG(buf.st_mode)) ptr = "regular file"; else if (S_ISDIR(buf.st_mode)) ptr = "directory file"; else if (S_ISCHR(buf.st_mode)) ptr = "character special file"; else if (S_ISBLK(buf.st_mode)) ptr = "block special file"; else if (S_ISFIFO(buf.st_mode)) ptr = "fifo file"; #ifdef S_ISLNK else if (S_ISLNK(buf.st_mode)) ptr = "symbolic link"; #endif #ifdef S_ISSOCK else if (S_ISSOCK(buf.st_mode)) ptr = "socket"; #endif else ptr = "** unknown mode **"; printf("%s\n", ptr); } return 0; }读取文件访问权限 stat_example.c
#include<stdio.h> #include<unistd.h> #include<stdlib.h> #include<sys/types.h> #include<sys/stat.h> #define N_BITS 3 int main(int argc,char *argv[]) { unsigned int i,mask=0700; struct stat buff; static char *perm[]={"---","--x","-w-","-wx","r--","r-x","rw-","rwx"}; if(argc>1) { if((stat(argv[1],&buff)!=-1)) { printf("permissions for %s\t",argv[1]); for(i=3;i;--i) { printf("%3s",perm[(buff.st_mode&mask)>>(i-1)*N_BITS]); mask>>=N_BITS; } putchar('\n'); } else { perror(argv[1]); exit(1); } } else { fprintf(stderr,"Usage:%s file_name\n",argv[0]); } return 0; }用户名与UID的转换
头文件 #include <pwd.h> #include <sys/types.h>
声明 struct passwd *getpwnam(const char *name); struct passwd *getpwuid(uid_t uid);
struct passwd { char *pw_name; char *pw_passwd; uid_t pw_uid; gid_t pw_gid; char *pw_gecos; char *pw_dir; char *pw_shell; };
用户名与UID的转换举例
#include<stdio.h> #include<pwd.h> #include<stdlib.h> int main(int argc,char *argv[]) { struct passwd *ptr; uid_t uid; uid=atoi(argv[1]); ptr=getpwuid(uid); printf("name:%s\n",ptr->pw_name); printf("passwd:%s\n",ptr->pw_passwd); printf("home_dir:%s\n",ptr->pw_dir); }组名与GID的转换
声明 struct group* getgrgid(_gid_t _gid); struct group* getgrnam(_const_char *_name);struct group{ char *gr_name; char *gr_passwd; _gid_t gr_gid; Char **gr_mem; }获取符号连接文件的源文件名
头文件: #include <unistd.h>定义函数:int readlink(const char *path, char *buf, size_t bufsiz);函数说明:readlink()会将参数path的符号链接内容到参数buf所指的内存空间,返回的内容不是以NULL作字符串结尾,但会将字符串的字符数返回。若参数bufsiz小于符号连接的内容长度,过长的内容会被截断。返回值 :执行成功则传符号连接所指的文件路径字符串,失败返回-1, 错误代码存于errno举例:symlink_exp.c #include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<string.h> #define BUFSIZE 128 int main(int argc,char *argv[]) { char buf[BUFSIZE]; memset(buf,'\0',BUFSIZE); //unlink("sym_link_test"); if((symlink(argv[1],"sym_link_test"))==-1) { perror("symlink"); exit(EXIT_FAILURE); } if((readlink("sym_link_test",buf,BUFSIZE))==-1) { perror("readlink"); exit(EXIT_FAILURE); } printf("sym_link_test is the symbol link of %s\n",argv[1]); system("ls -l sym_link_test"); }mmap、munmap函数
头文件:#include <sys/mman.h>原型 void *mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off); int munmap(void *addr, size_t len);功能 mmap函数的作用是将虚拟地址映射到物理地址,或者映射到文件和文件位置。 munmap函数的作用是释放内存段。参数 addr:内存起始地址。 len:内存分配长度。 prot:用来设置内存段的访问权限。 flags:控制着程序对该段内存的访问方式。 fildes:文件描述符。举例 #include <stdio.h> #define NUM (10) typedef struct { int no; char data[30]; }RECORD; int main() { RECORD record; int i; FILE *fp = fopen("records.dat","w+"); if(fp == NULL) { printf("Can't open the file : records.dat\n"); return 1; } for(i=0; i < NUM; i++) { record.no = i; sprintf(record.data, "Record-%d\n",i); fwrite(&record, sizeof(record), 1, fp); } fclose(fp); return 0; } #include <stdio.h> typedef struct { int no; char data[30]; }RECORD; int main() { RECORD record; FILE *fp = fopen("records.dat", "r+"); if(fp == NULL) { printf("Can't open the file : records.dat\n"); return 1; } fseek(fp, 5*sizeof(record), 1, SEEK_SET); fread(&record, sizeof(record), 1, fp); record.no = 2008; sprintf(record.data, "Record-%d\n", record.no); fseek(fp, 5*sizeof(record), 1, SEEK_SET); fwrite(&record, sizeof(record), 1, fp); fclose(fp); return 0; } #include <stdio.h> #include <unistd.h> /*close函数*/ #include <sys/mman.h> #include <fcntl.h> /*open函数*/ #define NUM (10) typedef struct { int no; char data[30]; }RECORD; int main() { RECORD *mapped; int filde = open("records.dat", O_RDWR); mapped = (RECORD *)mmap( 0, NUM * sizeof(RECORD), PROT_READ | PROT_WRITE, MAP_SHARED, filde, 0); mapped[5].no = 8888; sprintf(mapped[5].data, "Record-%d\n", mapped[5].no); msync((void*)mapped, NUM * sizeof(RECORD), MS_ASYNC); munmap((void*)mapped, NUM * sizeof(RECORD)); close(filde); return 0; }opendir函数 closedir函数 readdir函数 telldir函数、seekdir函数 mkdir函数、rmdir函数
opendir函数
函数名:opendir所在头文件:sys/types.h dirent.hDIR *opendir(const char *name);作用:打开一个子目录并建立一个子目录流。如果成功,它将返回一个指向DIR结构体的指针,以后对目录数据项的读操作将通过这个指针来完成的。形参 name :子目录名返回值: 调用成功返回打开的子目录流指针,不成功返回NULL。 相关函数:closedirclosedir函数
函数名:closedir所在头文件:sys/types.h dirent.h原型声明: int closedir(DIR *dirp);作用: 关闭一个子目录流,并释放与之关联的资源。形参: dirp :子目录流指针返回值: 调用成功返回0,不成功返回-1。相关函数:opendirreaddir函数
函数名:readdir所在头文件:sys/types.h dirent.hstruct dirent *readdir(DIR *dirp)作用:返回一个指针,该指针指向的结构保存着子目录流dirp中下一个目录数据项的有关资料。后续的readdir返回的是后续的目录数据项。如果发生错误或达到子目录尾,readdir将返回NULL。POSIX兼容的系统在到达子目录尾时,只返回NULL,但不改变errno的值;当发生错误的时侯才修改errno的值。形参 dirp :子目录流指针返回值: 调用成功返回目录数据项指针,不成功返回NULL相关函数:telldir seekdir结构telldir函数
函数名:telldir所在头文件:sys/types.h dirent.h原型声明: long int telldir(DIR *dirp);作用: 获取当前子目录流中的当前位置。形参: dirp :子目录流指针返回值: 返回当前子目录流中的当前位置相关函数:seekdirseekdir函数
函数名:seekdir所在头文件:sys/types.h dirent.h原型声明: void seekdir(DIR *dirp, long int loc);作用: 改变子目录流中的目录数据项指针的指向,通过loc来指定目录数据项指针所指向的位置。这个位置可通过telldir函数来获取。形参: dirp :子目录流指针 loc:设置目录数据项指针的位置返回值: 无相关函数:telldirmkdir函数
函数名:mkdir所在头文件:sys/stat.h原型声明: int mkdir(const char *path, mode_t mode);作用: 将创建一个子目录,子目录名字将由path指出。形参: path :需要创建的子目录名 mode :与open系统调用中使用O_CREAT时的设置一样返回值: 调用成功返回0,不成功返回-1。相关函数:rmdirrmdir函数
函数名:rmdir所在头文件:sys/stat.h原型声明: int rmdir(const char *path);作用: 删除子目录,但要求子目录为空。形参: path :需要删除的子目录名返回值: 调用成功返回0,不成功返回-1。相关函数:mkdirchdir函数
函数名:chdir所在头文件:unistd.h原型声明: int chdir(const char *path);作用: 改变当前工作目录。形参: path :需要切换的工作目录。返回值: 调用成功返回0,不成功返回-1。相关函数:getcwdgetcwd函数
函数名:getcwd
所在头文件:unistd.h
原型声明: char *getcwd(char *buf, size_t size);
作用: 把当前子目录名字写到指定的缓冲区buf中。
形参: buf :保存绝对路径的缓冲区。 size:至少比返回值大1.
返回值: 调用成功返回指针buf,不成功返回NULL。
相关函数:chdir
举例