zoukankan      html  css  js  c++  java
  • Unix文件 I/O(不带缓冲区的)上

    简介

    Unix系统大多数文件i/o只需要:open、read、write、lseek、close这几个函数。但是某些时候我们也需要fcntl、ioctl、sync等函数配合使用。这些函数都是不带缓冲区的I/0(unbuffered I/O),

    术语不带缓冲的指的是每个read和write都是内核中的一个系统调用。这些函数都是POSIX和single UNIX Specification的组成部分。

    这样说可能有点迷糊,举个例子说明

    不带缓存的I/O:       read,write,open......
    标准(带缓存的)I/O: fgets,fread,fwrite.....

    这里使用两个对应的函数进行比较:

    ssize_t write(int filedes, const void *buff, size_t nbytes)
    size_t fwrite(const void *ptr, size_t size, size_t nobj, FILE *fp)

    上面的buff和ptr都是指应用程序自己使用的buffer,实际上当需要对文件进行写操作时,都会先写到内核所设的缓冲存储器。如果该缓存未满,则并不将其排入输出队列,直到缓存写满

    或者内核再次需要重新使用此缓存时才将其排入磁盘I/O输入队列,再进行实际的I/O操作,也就是此时才把数据真正写到磁盘,这种技术叫延迟写。

    1、如果我们直接用非缓存I/O对内核的缓冲区进行读写,会产生许多管理不善而造成的麻烦(如一次性写入过多,或多次系统调用导致的效率低下)。

    2、标准(带缓存的)I/O为我们解决了这些问题,它处理很多细节,如缓冲区分配,以优化长度执行I/O等,更便于我们使用。

    文件描述符

    一切皆是文件”是 Unix/Linux 的基本哲学之一。不仅普通的文件,目录、字符设备、块设备、 套接字等在 Unix/Linux 中都是以文件被对待;它们虽然类型不同,但是对其提供的却是同一套操作界面。

     

    对内核而言,所有打开的文件都是通过文件描述符引用的,文件描述符是一个非负的整数,当打开一个现有文件或创建一个新文件,内核向进程返回一个文件描述符。按照惯例,unix系统shell把文件描述符0与进程的标准的输入关联,

    文件描述符1与标准的输出关联,文件描述符2与标准错误关联。幻数0,1,2虽然已被标准化,我们在使用的时候应该用STDIN_FILENO、STDOUT_FILENO、STDERR_FILENO符号代替以提高可读性

    文件共享(摘抄UNIX环境编程)

    unix系统支持在不同进程间共享打开的文件。

     内核使用三种数据结果表示打开的文件。

    (1)每个进程在进程表中都有一个记录项,记录项中包含有一张打开文件的描述符表,可将其视为一个矢量,每个描述符占用一项。

       每个文件描述符相关连的是:

    •        文件描述符标志
    •       指向一个文件表项的指针。

    (2)内核为所有打开文件维持一张文件表。每个文件表项包含:

    •         文件状态标志(包含读写,填写,同步,非阻塞等)
    •         当前文件偏移量
    •         指向该文件v节点表项的指针。

    (3)每个打开的文件都有一个v节点结构。v节点包含了文件类型和对此文件进行各种操作的函数的指针。对于大多数文件,v节点还包括了该文件的i节点。这些信息是再打开文件时从磁盘上读入内存的。这些文件都是从磁盘读入内存的,所以可以快速使用这些参数。

    note:Linux没有使用v节点,而是使用了通用的i节点。

    此图为打开文件的内核数据结构,该进程有两个不同的打开文件,一个文件打开为标准输入,另一个打开为标准输出。

    如果两个独立进程各自打开同一个文件,则有如图所示

     我们假设第一个进程在文件描述符3打开该文件,而另一个进程在文件描述符4上打开该文件。打开该文件的每个进程都得到一个文件表项。但对一个给定的文件只有一个v节点表项。每个进程都有自己的一个文件表项,理由是每个进程都要有对一个文件的偏移量。

    下面描述上图的产生事件:

    • 在完成每个write后,在文件表项中的当前文件偏移量增加所写的字节数,如果这是当前文件偏移量超过了当前的文件长度,则在i节点表项忠的当前文件长度被设置为当前文件偏移量。
    • 如果用O_APPEND标志打开一个文件,则相应标志也被设置到文件表现的文件状态标志中。每次对这种具有填写标志的文件执行写操作时候,在文件表项中的当前文件偏移量标志中,首先被设置为i节点表项忠的文件长度,这就似的每次写的数据都添加到文件的当前尾端。
    • 若一个文件用lseek函数丁文到文件的当前的尾端。则文件表项中的当前文件偏移量被设置为i节点表项中的当前文件长度。
    • lseek函数只修改文件表系那个忠的当前文件偏移量。没有进行任何io操作

    可能有多个文件描述符指向同一个文件表项

    fork 后也会发生同样的情况,此时父子进程对于每个打开文件描述符共享同一个文件表项。

    note:文件描述符标志和恩见状态标志在作用域方面的区别。潜质只用与一个进程的一个进程描述符。而后者则使用语指向改给定文件表项的任何进程中的所有描述符。

     当我们看到多个进程进行对一个文件同时操作的时候,这时候就产生了冲突,如何避免呢,那么就是下一节原子操作~

     2017/4/5(2天一博客连载Unix环境编程)加油

  • 相关阅读:
    mysql_wp_replication_tutorial
    Procedure execution failed 2013
    [Err] 1136
    innodb_flush_log_at_trx_commit和sync_binlog 参数说明
    mysql没有oracle 那样一次性把data buffer 数据写入磁盘
    Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field
    MyEclipse之Widget is disposed
    Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Query was empty
    An internal error occurred during: "Building workspace". GC overhead limit exceeded
    Oracle 内部复制文档解读
  • 原文地址:https://www.cnblogs.com/smartxuchao/p/6671231.html
Copyright © 2011-2022 走看看