zoukankan      html  css  js  c++  java
  • [Linux]APUE读书笔记:File I/O

    文件描述符:

    1. 进程通过文件描述符来操作文件,文件描述符可以通过open, openat, creat系统调用返回;
    2. shell和其他应用默认打开标准输入(STDIN_FILENO),标准输出(STDOUT_FILENO),标准错误(STDERR_FILENO)三个文件描述符。

    open和openat函数:

    1. 文件可以通过调用open或者openat函数打开或者创建;
    2.  新文件描述符使用最小未使用原则;
    3. TOCTTOU(time of check of time of use)的基本含义:程序是脆弱的,如果它调用两个基于文件的函数,并且后一个调用依赖于前一个调用的结果。因为这两个调用不是原子操作,在两个调用之间文件是可以被改变的,导致前一个调用的结果失效,错误发生。

    creat函数:

    1. creat函数以只写的方式创建文件;
    2. creat函数完全可以用open函数代替:open(path, O_WRONLY | O_CREAT | O_TRUNC, mode)。

    lseek函数:
      1. 用于显式指定被打开文件的偏移量,返回当前文件的新偏移量;
      2. 测试标准输入是否可以seek:

     1 #include "apue.h"
     2 
     3 int main(void)
     4 {
     5     if (lseek(STDIN_FILENO, 0, SEEK_CUR) == -1) {
     6         printf("canot seek
    ");
     7     }
     8     else {
     9         printf("seek OK
    ");
    10     }
    11     exit(0);
    12 }
    View Code

      3. 由于文件当前偏移量可能为负数,lseek的返回值应该和 -1 比较,而不是测试是否小于0;
      4. 设置的当前文件偏移量大于文件长度时,文件中允许形成空洞,空洞不需要存储空间来存储;
      5. 在文件中创建一个空洞:

     1 #include "apue.h"
     2 #include <fcntl.h>
     3 
     4 char buf1[] = "abcdefghij";
     5 char buf2[] = "ABCDEFGHIJ";
     6 
     7 int main(void)
     8 {
     9     int fd;
    10 
    11     if ((fd = creat("file.hole", FILE_MODE)) < 0) {
    12         err_sys("creat error");
    13     }
    14 
    15     if (write(fd, buf1, 10) != 10) {
    16         err_sys("buf1 write error");
    17     }
    18     /*offset now = 10*/
    19 
    20     if (lseek(fd, 16384, SEEK_SET) == -1) {
    21         err_sys("lseek error");
    22     }
    23     /*offset now = 16384*/
    24 
    25     if (write(fd, buf2, 10) != 10) {
    26         err_sys("buf2 write error");
    27     }
    28 
    29     exit(0);
    30 }
    View Code

    read/write函数:

    1. read函数被用于从打开的文件中读取数据,返回读取的字节数;
    2. write函数被用于向打开的文件中写数据,返回写入的字节数;

    I/O效率:
      1. 利用缓冲将标准输入复制到标准输出:

     1 #include "apue.h"
     2 
     3 #define BUFFSIZE 4096
     4 
     5 int main(void)
     6 {
     7     int n;
     8     char buf[BUFFSIZE];
     9 
    10     while ((n = read(STDIN_FILENO, buf, BUFFSIZE)) > 0) {
    11         if (write(STDOUT_FILENO, buf, n) != n) {
    12             err_sys("write error");
    13         }
    14     }
    15     if (n < 0) {
    16         err_sys("read error");
    17     }
    18 
    19     exit(0);
    20 }
    View Code

      2. Linux ext4文件系统,块大小是4096,缓冲大小最佳值是4096。
    文件共享:
      1. 内核使用三个数据结构表示打开的文件:

    • 每个进程在进程表中都有一个记录项,记录项中包含一张打开文件描述符的表,与每个文件描述符相关联的是:文件描述符标志和指向文件表项的指针;
    • 内核维护一张所有已打开文件的文件表,每个文件表包括:文件状态标志,当前偏移量,指向文件V节点表项的指针;
    • 每个已打开文件(或者设备)都有一个V节点结构,Linux只有i节点;

    原子操作:

    1. 任何需要多个函数的操作都不是原子操作;
    2. 调用pread相当于调用lseek后再调用read, 调用pwrite相当于调用lseek后再调用write,但是调用pread/pwrite是原子操作,且当前文件偏移量不更新;

    dup和dup2函数:

    1. 一个已经存在的文件描述符可以用dup和dup2复制;
    2. dup返回最小可用文件描述符,dup2用fd2指定新文件描述符,如果fd2已经打开,则先关闭,如果fd2和fd1相等,则直接返回fd2不关闭它;
    3. dup/dup2总是清除新文件描述的close-on-exec标志;
    4. dup(fd)/fcntl(fd, F_DUPFD, 0),dup2(fd1, fd2)/close(fd2);fcntl(fd, F_DUPFD, fd2);

    sync, fsync和fdatasync函数:

    1. sysc函数只将所有修改过的缓冲块排在写队列,然后返回,不等写磁盘发生;
    2. fsync只对fd单一文件起作用,它等磁盘写完成后再返回;
    3. fdatasync和fsync类似,但是它不同步更新文件属性,fsync同步更新;

    fcntl函数:
      1. fcntl函数可以改变一个已打开文件的属性;
      2. fcntl函数有5种不同功能:

    • 复制一个已经存在的描述符(cmd = F_DUPFD or F_DUPFD_CLOEXEC);
    • 获取/设置文件描述符标志(cmd = F_GETFD or F_SETFD);
    • 获取/设置文件状态标志(cmd = F_SETFL or F_GETFL);
    • 获取/设置异步I/O所有权(cmd = F_GETOWN or F_SETOWN);
    • 获取/设置记录锁(cmd = F_GETLK, F_SETLK, F_SETLKW);

      3. 打印具体描述符的文件标志:

     1 #include "apue.h"
     2 #include <fcntl.h>
     3 
     4 int main(int argc, char *argv[])
     5 {
     6     int val;
     7     if (argc != 2) {
     8         err_quit("usage: a.out <descriptor#>");
     9     }
    10 
    11     if ((val = fcntl(atoi(argv[1]), F_GETFL, 0)) < 0) {
    12         err_sys("fcntl error for fd %d", atoi(argv[1]));
    13     }
    14     switch(val & O_ACCMODE) {
    15     case O_RDONLY:
    16         printf("read only");
    17         break;
    18     case O_WRONLY:
    19         printf("write only");
    20         break;
    21     case O_RDWR:
    22         printf("read write");
    23         break;
    24     default:
    25         err_dump("unknow access mode");
    26     }
    27     if (val & O_APPEND) {
    28         printf(",append");
    29     }
    30     if (val & O_NONBLOCK) {
    31         printf(",nonblocking");
    32     }
    33     if (val & O_SYNC) {
    34         printf(",synchronous writes");
    35     }
    36 #if !defined(_POSIX_C_SOURCE) && defined(O_FSYNC) && (O_FSYNC != O_SYNC)
    37     if (val & O_FSYNC) {
    38         printf(",synchronous writes");
    39     }
    40 #endif
    41     putchar('
    ');
    42 
    43     exit(0);
    44 }
    View Code

      4. 打开文件描述符的一个或者多个文件状态标志:

     1 #include "apue.h"
     2 #include <fcntl.h>
     3 
     4 void set_fl(int fd, int flags)
     5 {
     6     int val;
     7     if ((val = fcntl(fd, F_GETFL, 0)) < 0) {
     8         err_sys("fcntl F_GETFL error");
     9     }
    10     val |= flags;
    11     if (fcntl(fd, F_SETFL, val) < 0) {
    12         err_sys("fcntl F_SETFL error");
    13     }
    14 }
    View Code

    ioctl函数:

    1. ioctl函数是I/O操作的万能工具,尤其是对终端设备;

    /dev/fd:

    1. 假如n是打开的,打开/dev/fd/n相当于复制描述符n;
  • 相关阅读:
    SQL Server中游标的使用
    SQL Server之内连接 左连接 右连接 全连接 交叉连接
    C#后台格式化JSON字符串显示
    使用反射、特性简化代码
    JQuery方法扩展
    .NET强制进行即时垃圾回收
    .NET中的Queue和Stack
    如何解决firefox下window.event的问题
    【JS对象、JSON字符串】之间的相互转换
    Javascript模块化编程(一):模块的写法
  • 原文地址:https://www.cnblogs.com/skycore/p/4035856.html
Copyright © 2011-2022 走看看