• LINUX(UNIX)文件I/O学习


     一、文件描述符

      文件描述符是当打开或创建一个文件时,由内核向相应进程返回的标识(非负数,为int类型)。相应进程可以引用该标识对打开或创建的文件进行操作。

      在unistd.h中定义了3个文件描述符:

        标准输入  STDIN_FILENO  0

        标准输出  STDOUT_FILENO 1

        标准错误  STDERR_FILENO 3

    二、打开/创建文件

      通常用open、creat函数来打开和创建一个文件   

     1 #include <sys/types.h>
     2 #include <sys/stat.h>
     3 #include <fcntl.h>
     4 
     5 //打开一个文件
     6 int open(const char *pathname, int flags);    
     7 返回:若成功为文件描述符,若出错为- 1
     8 //创建一个文件
     9 int open(const char *pathname, int flags, mode_t mode); 
    10 返回:若成功为文件描述符,若出错为- 1
    11 //以只写的方式创建一个文件
    12 int creat(const char *pathname, mode_t mode);
    13 返回:若成功为文件描述符,若出错为- 1

      open函数可以用来打开一个文件,同时也可以用来创建一个文件,返回文件描述符,并且该文件描述符一定为当前未用的最小的。

      pathname:将要打开或将要创建文件的名字

      flags:可以是一下的值或运算后的结果(注:前三个只能选一个)

        O_RDONLY    --以只读的方式打开文件

        O_WRONLY    --以只写的方式打开文件

        O_RDWR     --以可读可写的方式打开文件

        O_APPEND    --以在文件尾追加的方式打开文件

        O_CREAR     --若文件不存在则创建它,并且指明第3个参数mode

        O_EXCL      --若同时指明了O_CREAT时,若文件已存在则报错,若不存在则创建文件(检测与创建绑定在一起为原子操作)

        O_TRUNC    --如果此文件存在,而且为自读或只写打开,则将其长度截断为0  

        O_NOCTTY     --如果p a t h n a m e指的是终端设备,则不将此设备分配作为此进程的控制终端。  

        O_NONBLOCK  -- 如果p a t h n a m e指的是一个F I F O、一个块特殊文件或一个字符特殊文件,则此选择项为此文件的本次打开操作和后续的 I / O操作设置非阻塞方式。

        O_SYNC     --使每次w r i t e都等到物理I / O操作完成。
        O_SYNC    --选择项不是P O S I X . 1的组成部分,但S V 保证在一个给定的描述符

      mode:

      对于creat函数,由于creat函数是已只写方式创建一个文件所以

      creat(pathname, mode)等于open(pathname, O_WRONLY | O_CREAT | O_TRUNC, mode)  

    三、关闭文件

      用close函数关闭文件  

    1 #include <unistd.h>
    2 
    3 int close(int fd);

      指定相应的文件描述符就可以关闭打开的文件

    四、当前文件位移量(针对打开过的文件)

      打开的文件都有一个与该文件相关联的当前文件位置,当进行I/O操作时,就从该位置开始。lseek用来重新定位文件中的当前位置(也可以返回当前文件的位置)。

      由于lseek的以前的返回类型以及offset都是long类型,因此为lseek

    1 #include <sys/types.h>
    2 #include <unistd.h>
    3 
    4 off_t lseek(int fd, off_t offset, int whence);

      fd:相应文件描述符

      offset:在文件中相对与whence的位移量

      whence:标识在文件中的特定位置,有3个值

        SEEK_SET:指文件开头

        SEEK_CUR:指当前文件的位置

        SEEK_END:指文件的尾

      lseek(fd, 0, SEEK_CUR)用来返回文件的当前位置。

      对与刚刚打开的文件一般是返回0,当以O_APPEND打开时,返回的为非零,为文件尾。

      如果用lseek函数修改的当前位移量超过文件的当前长度时,该文件会形成文件空洞,文件空洞是不占用磁盘空间的,但是当读文件时,会将文件空洞读作0。

    五、I/O操作

      I/O操作包括对文件的读写操作,因此用到了read,write函数

    1 #include <unistd.h>
    2 
    3 ssize_t read(int fd, void *buf, size_t count);
    4 
    5 ssize_t write(int fd, const void *buf, size_t count);

      fd:相应文件的文件描述符

      buf:读/写的缓冲区

      count:对read,为要读的字节数,对write,为buf的大小  

      对read:  

      当读普通文件时,在读到要求字节数之前已到达了文件尾端。例如,若在到达文件尾端之前还有3 个字节,而要求读 10个字节,则read返回3,下一次再调用re ad时,它将返回0 (文件尾端)。也就是说当读到文件尾是,最后一次读一定是返回0。

      当从终端设备读时,通常一次最多读一行。
      当从网络读时,网络中的缓冲机构可能造成返回值小于所要求读的字节数。
      某些面向记录的设备,例如磁带,一次最多返回一个记录。

      对write:

      write出错原因:

        1.磁盘满

        2.超出了文件长度的限制

      write,read后都会修改文件的当前位移

    六、内核中与文件相关的3个数据结构

      在操作系统内核中维护这文件的相关信息,他们的关系如下:

      操作系统为维护进程,设定了一个进程表,每一个进程在进程表中都单独占一个记录项

      每个进程为维护打开的文件,设定一个文件描述符表(1),没一个文件描述符在该表中单独占一个记录,并且一个记录由两项组成:

        1.文件描述符标志

        2.指向文件表的指针

      文件表中又包括了3项

        1.文件状态标志(O_RDONLY | O_WRONLY...)

        2.文件的当前位移(参见四、文件的当前位移量

        3.指向v节点的指针(v节点,文件的虚拟文件系统)

      v节点包括的信息

        1.v节点的信息

        2.i节点的信息

        3.文件的长度

      当两个相互独立的进程打开同一个文件时,他们各自会分别获得一个文件描述符表,一个文件表,但是文件表中的v节点指针会指向同一个v节点。

      此时当值进行文件读时,两个进程各不相干,都能正常进行,这是因为两个进程都有一个独立的文件表(包含一个文件当前的位移量,这个仅属于当前进程)。当进行文件写时,可能发生问题,例如,当打开文件时两个进程(A,B)的都将文件的当前位移量置为0,此时进程A先对该文件写如1000个字节,写过后,更改文件的当前位移量为1000,以及文件的长度为1000,此时进程B对该文件进行写(注意此时对B进程来说文件的当前位移量并没有变还是0),此时B进程写过1000个字节后恰好覆盖了A进程写入的,造成了写覆盖。

      当然这个问题是可以解决的,只要在open文件时添加O_APPEND就能解决,因为这个标志规定将这两个操作(1.将文件表的文件当前位移量设为文件尾2.对文件写)作为原子操作,即每次写文件时,都会先修改文件的当前位移量为文件尾。这就是进程件文件共享的一个例子。

      另外原子操作指的是将多个步骤的操作看成一个步骤,要么全做,要么不做。

    七、改变打开文件的性质

      要该变文的性质要用到fcntl,或者dup,dup2)(fcntl可以替代dup,dup2)

    1 #include <unistd.h>
    2 #include <fcntl.h>
    3 
    4 int fcntl(int fd, int cmd, ... /* arg */ );

      fd:相应文件描述符

      cmd:用来确定fcntl的功能可以为下面的值

        F_DUPFD:复制文件描述符

        F_GETFD/F_SETFD:获得/设置文件描述符标志

        F_GETFL/F_SETFL:获得/设置文件状态标志

        还有其他,不过先学这几个

      ...:在ANSCI C中表示可以更多的参数

      fcntl的功能:

      正如cmd列出的,已个标志代表一个功能

      1.复制文件描述符

        复制描述符可以用dup,dup2,fcntl。

        复制文件描述符后,使得新文件描述符和原有的文件描述符共享一个文件表,但是各自独占已个文件描述符表

    1 #include <unistd.h>
    2 
    3 int dup(int oldfd);
    4 int dup2(int oldfd, int newfd);

        newfd = dup(oldefd);   等同于  newfd = fcntl(oldfd, F_DUPFD);

        dup2(oldfd, newfd);  等同于

        close(newfd);

        fcntl(oldfd, F_DUPFD, newfd);

        只是dup2为原子操作

        dup2操作是,制定newfd为新的文件描述符,如果newfd已经打开了则先关闭在复制。 

       2.获得/设置文件描述符标志(目前仅仅有已一个标志位close_on_exec)及FD_CLOEXEC    

        用dup,dup2,fcntl复制文件描述符时,该值会被清除。

        设置F_GETFD会获得FD_CLOEXEC的值(以函数返回值返回)

        设置F_SETFD会将...参数的值设置给FD_CLOEXEC位(用到第3个参数)

       3.获得/设置文件状态标志

        设置F_GERFL会以函数返回值的形式返回文件的状态标志

        由于O_RDONLY,O_WRONLY,O_RDWR只能设置一个,所以要先将返回值与O_ACCMODE相与再来判断是3个值的哪一个

        而其他标志这不需要

        设置F_SETFL会将函数的第三个参数的值赋给文件的状态标志

  • 相关阅读:
    LINQ to Entities 查询中的标准查询运算符
    LINQ to Entities 基于方法的查询语法
    ajax 与 form 提交的区别
    i++ & ++i 区别
    sizeof 数据类型大小 32位&64位
    标准数据类型宏定义
    long & int 区别
    类函数修饰 const
    指针
    数组
  • 原文地址:https://www.cnblogs.com/wmllz/p/5023782.html
Copyright © 2020-2023  润新知