函数文件系统文件I/O编程(一)基于Linux系统

本文纯属个人见解,是对前面学习的总结,如有描述不正确的地方还请高手指正~

    Linux文件I/O系统概述

    虚拟文件系统(VFS)

    Linux系统胜利的关键因素之一就是拥有与其他操纵系统和谐共存的能力。Linux系统的文件系统由两层结构构建:第一层是虚拟文件系统(VFS),第二层是各种不同的具体的文件系统。

    VFS就是把各种具体的文件系统的公共部份抽取出来,构成一个抽象层,是系统内核的一部份,它位于用户程序和具体的文件系统之间。它对用户供给了标准的文件系统调用接口,对具体的文件系统(如EXT2、FAT32等),它通过一系列的对不同文件系统公用的函数指针来实际调用具体的文件系统函数,实现实际的各有差异的操纵。任何使用文件系统的程序必须经过这层接口来使用它。通过这样的方法,VFS就对用户屏蔽了底层文件系统的实现细节和差异。

    VFS的作用:①对具体的文件系统的数据结构停止抽象,以一种统一的数据结构停止管理;②接受用户层的系统调用,如open()、read()、write()、stat()、link()等;③支持多种具体文件系统之间的相互拜访,接受内核其他子系统的操纵请求,例如,内存管理和进程调度。

    VFS在linux系统中的位置如下图1所示:

    函数和文件系统

    通过命令:cat /proc/filesystems 可以查看系统中支持哪些文件系统

    函数和文件系统

    第一列说明文件系统是否需要挂接在一个块设备上。nodev表明前面的文件系统不需要挂接在块设备上。

    第二列是内核支持的文件系统。

    Linu中的文件及文件描述符

    与windows不同,Linux操纵系统都是基于文件概念的(这个很很主要啊),文件是以字符序列构成的信息载体。根据这一点,可以把I/O设备当做文件来处置。因此,与磁盘上的一般文件停止交互所用的统一系统调用可以直接用于I/O设备。这样大大简化了系统对不同设备的处置,提高了效率。

    Linux中的文件主要分为4种:一般文件、目录文件、链接文件和设备文件,如下图:

    函数和文件系统

       内核如何区分和引用特定的文件呢?这里用到了一个主要的概念-------文件描述符。

       在Linux中,全体对设备和文件的操纵都是使用文件描述符来停止的。文件描述符是一个非负的整数,它是一个索引值,并指向在内核中每个进程打开文件的记载表。当打开一个现存文件或创立一个新文件时,内核就向程返回一个文件描述符;当需要读写文件时,也需要把文件描述符作为参数传递给响应的函数。(咱可以这样理解,只有当对文件停止操纵时,该文件才会有文件描述符,进程没有用到的文件齐备不给描述符)

      通常,一个进程启动时,都市打开3个文件:标准输入、标准输出和标准出错处置。这3个文件分别对应描述符为0、1和2(也就是宏替换 STDIN_FILENO、STDOUT_FILENO、STDERR_FILENO)。

     基于文件描述符的I/O操纵虽然不能直接移植到类Linux以外的系统上(如Windows),但它往往是实现某些I/O操纵的独一门路,如Linux中底层文件操纵函数(下边会讲)、多路I/O、TC[/IP套接字编程接口等、同时,它们也很好地兼容Posix标准,因此,可以很方便地移植到任何Posix平台上。基于文件描述符的I/O操纵是Linux中最常用的操纵之一,下面就讲讲它。

    底层文件I/O操纵

    此次主要介绍文件I/O操纵的系统调用,主要用到5个函数:open()、read()、write()、lseek()和close()。这些函数的特点不带缓存,直接对文件(包含设备)停止读写操纵。

    1、基本文件操纵

    函数说明

    ● open()函数用于打开或创立文件,在打开或者创立文件时可以指定文件的属性及用户的权限等各种参数。

    ● close()函数用于关闭一个被打开的文件。当一个进程终止时,全体被它打开的文件都由内核自动关闭,很多程序都使用这一功能而不显示地关闭一个文件。

    ● read()函数用于将从指定的文件中读出的数据放到缓存区中,并返回实际读入的字节数。若返回0,则表现没有数据可读,即已到达文件尾。读操纵从文件的以后指针位置开始。当从设备文件中读出数据时,通常一次最多读一行。

    ● write()函数用于向打开的文件写数据。写操纵从文件的以后指针位置开始,对磁盘文件停止写操纵,若磁盘已满或者超出该文件的长度,则write()函数返回失败。

    ● lseek()函数用于在指定的文件描述符中将文件指针定位到响应的位置。每一个已打开的文件都有一个读写位置,当打开文件时,其读写位置通常指向文件扫尾;若是以附加的方法打开文件(如O_APPEND),则读写位置会指向文件尾。当read()或者write()时,读写位置会随之增长,lseek()便是用来控制该文件的读写位置的。它只能用在可定位(可随机拜访)文件操纵中。管道、套接字和大部份字符设备文件是不可定位的,所以在这些文件的操纵中没法使用lseek()调用。

    函数格式

    下面我以表格的情势将这5个函数的格式写出来,接下来再加上我的基础试验。

    函数和文件系统

     在open()函数中,flag参数可通过 “|” 组合构成,但前3个标记常量(O_RDONLY、O_WRONLY及O_RDWR)不能相互组合。perms是文件的存取权限,既可以用宏定义表现法,也可以用八进制表现法。

     函数和文件系统

    函数和文件系统

      在读一般文件时,若读到要求的字节数前已到达文件的尾部,则返回的字节数会小于希望读书的字节数。

       关于size_t和ssize_t的区别,如果有不懂的请看:http://blog.csdn.net/mybelief321/article/details/8992052,也可以简略的记住:ssize_t是有符号整形,size_t是无符号整形。

    每日一道理
生活的无奈,有时并不源于自我,别人无心的筑就,那是一种阴差阳错。生活本就是矛盾的,白天与黑夜间的距离,春夏秋冬之间的轮回,于是有了挑剔的喜爱,让无奈加上了喜悦的等待。

      函数和文件系统

   在写一般文件时,写操纵从文件的以后指针位置开始。

  函数和文件系统

   下面是lseek较特别的使用

    ☆ 欲将读写位置移到文件扫尾时:lseek(int fd,0,SEEK_SET)

    ☆ 欲将读写位置移到文件尾时:lseek(int fd,0,SEEK_END)

    ☆ 想要获得目前文件位置时:lseek(ind fd,0,SEEK_CUR)

    注意:Linux系统不允许lseek()对tty装置作用,此动作会令lseek()返回ESPIPE。

    另外,其实还有一个文件创立函数creat(),它的函数原型是int creat(const char *pathname,int perms),它相当于使用以下的调用方法调用open():

    open(const char *pathname,(O_CREAT|O_WRONLY|O_TRUNC));

    基础试验1:

    试验说明:主要是为了演示open()函数的使用方法。首先在自己的目录下使用命令:vi open.c创立一个文件,如下图,我在路径/home/song/lianxi文件夹下创立的:
    函数和文件系统

    然后编写open.c文件内容,内容如下:

    函数和文件系统

    编辑并保存open.c后的文件夹所含全体文件,如下:

    函数和文件系统

    使用命令:gcc open.c -o open编译c文件,如下:

    函数和文件系统

    执行命令:./open 可以看到咱们的试验胜利输出了:

    函数和文件系统

   使用命令:more temp查看一下temp的文件,里边有咱们使用write()写的内容:

   函数和文件系统

    我将这个文件内容上传到了:点此下载,可以自行下载

    基础试验2

    试验说明:基本功能是从一个文件(源文件)中读取最后2KB数据并复制到另一个文件(目标文件)。在实例中源文件是以只读方法打开的,目标文件是以只写方法(可以使读/写方法)打开的。若目标文件不存在,可以创立并设置权限的初始值为644,即文件全体者可读可写,文件所属组合其他用户只能读。

    首先先后使用命令:vi copy_file.c和 vi src.c 在自己的试验目录下创立文件,如下图

   函数和文件系统

   然后再编辑copy_file.c的文件内容,如下

    

    #include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>

    #define Buffer_Size 1024   /*每次读写缓存大小为1KB,大小不同,运行效率不同*/
#define Src_File_Name  "/home/song/lianxi/src.c"    /*源文件名,建议使用宏定义*/
#define Dest_File_Name "/home/song/lianxi/dest.c"      //目标文件名*/
#define Offset     1024*2      //复制的数据大小,这里为2KB*/

    int main()
{
 int src_fd,dest_fd;  /*文件描述符*/
 unsigned char buff[Buffer_Size];   /*定义用于缓冲数据的数组*/
 int real_read_len;     /*read()函数实际读取到的字节*/
 /*以只读方法打开源文件*/
 src_fd=open(Src_File_Name,O_RDONLY);
 /* 以只写方法打开目标文件,若此文件不存在则创立该文件,拜访权限为644*/
 dest_fd=open(Dest_File_Name,O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
 /*如果打开文件出错,则退出程序*/
 if(src_fd<0||dest_fd<0)
 {
  printf("Open file error\n");
  exit(1);   
 } 
 /* 将源文件的读/写指针移到最后2KB的起始位置*/
 lseek(src_fd,-Offset,SEEK_END);
 /* 读取源文件的最后2KB数据并写到目标文件中,每次读写1KB*/
 while((real_read_len=read(src_fd,buff,sizeof(buff)))>0)
 {
  write(dest_fd,buff,real_read_len); 
 }
 /* 关闭文件,释放资源*/
 close(dest_fd);
 close(src_fd);
 return 0;
}

    

     其中src.c的文件内容你可以自己随便的放内容,但是要注意文件内容要大于2KB。

    这两个文件我上传到资源网站,可以自行下载:点此下载

    编辑完之后,使用命令: gcc copy_file.c -o copy_file 编译文件,然后执行命令:./copy_file,可以看到自动生成了dest.c文件

    函数和文件系统

    使用命令:more dest.c可以看到文件的内容

    使用命令:ls -l dest.c可以看到该文件的大小正好为2KB(2048)

     函数和文件系统

     

   

文章结束给大家分享下程序员的一些笑话语录: 一个合格的程序员是不会写出 诸如 “摧毁地球” 这样的程序的,他们会写一个函数叫 “摧毁行星”而把地球当一个参数传进去。

--------------------------------- 原创文章 By
函数和文件系统
---------------------------------

原文地址:https://www.cnblogs.com/jiangu66/p/3109048.html