最全的李慧芹APUE-文件IO笔记

打印 上一主题 下一主题

主题 881|帖子 881|积分 2643

文件 IO / 系统调用 IO

: 李慧芹老师的视频课程请点这里, 本篇为系统IO一章的笔记, 课上提到过的内容基本都会包含, 上一章为标准IO
文件描述符(fd)是在文件IO中贯穿始终的类型
本节内容


  • 文件IO操作: open, close, read, write, lseek
  • 文件IO与标准IO的区别
  • IO的效率问题
  • 文件共享问题
  • 原子操作
  • 程序中的重定向: dup, dup2
  • 同步: sync, fsync, fdatasync
  • 管家: fcntl(), ioctl()
FILE 与 fd

stdio中, 可以调用fopen()(依赖于sysio的open())获得FILE结构体(结构如下表)指针:
字段说明pos文件位置fd文件描述符......磁盘上的每个文件有唯一的标识inode, 而每次调用open()时, 都会产生一个结构体, 该结构体包含了要打开的文件的所有信息(包括inode)
进程维护了一个数组(大小为1024), 存储所有通过open()产生的结构体的首地址
文件描述符fd表示了某一结构体的首地址在上述数组中的下标位置, 因此, fd实际上就是int类型变量!

fd优先使用当前可用范围内下标值最小的数组位置
设进程维护的数组为A, close()函数就相当于:
  1. free(A[fd]);
  2. A[fd] = NULL;
复制代码
当发生如下图所示情况(数组中的两个指针同时指向同一个结构体)时:

close(4)并不会导致A[6]变为野指针, 这是由于结构体中包含引用计数器(counter)字段, 只有当该字段变为0时, 该结构体占用的空间才会被释放
打开与关闭操作


  • 打开
  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include <fcntl.h>
  4. int open(const char *pathname, int flags);
  5. int open(const char *pathname, int flags, mode_t mode);
  6. int creat(const char *pathname, mode_t mode);
复制代码
参数flags是一个位图, 必须包含一个状态选项:
模式权限O_RDONLY只读O_WRONLY只写O_RDWR读写可以包含零或多个创建选项:
模式说明O_CREAT有则情况, 无则创建O_EXCL必须打开一个新文件O_APPEND追加O_TRUNC截断O_ASYNC信号驱动IOO_DIRECT最小化cache作用O_DIRECTORY必须打开目录O_LARGEFILE打开的是大文件(该方法不如设置_FILE_OFFSET_BITS为64)O_NOATIME不需要更新文件最后读的时间(节省文件更新时间)O_NOFOLLOW如果文件是符号链接, 那么不打开它O_NONBLOCK非阻塞O_SYNC同步
cache vs buffer:
cache代表"读的缓冲区"
buffer代表"写的缓冲区"
open()和creat()执行成功时返回文件描述符, 失败则返回-1
下标为fopen()的参数mode与open()的参数flags的比对:
modeflagsrO_RDONLYr+O_RDWRwO_WRONLY|O_CREAT|O_TRUNCw+O_RDWR|O_TRUNC|O_CREAT当flags & O_CREAT != 0时, 则open()必须传入mode, 创建的文件的权限服从:
  1. mode & ~umask
复制代码

  • 关闭
  1. #include <unistd.h>
  2. int close(int fd);
复制代码
成功返回0, 失败返回-1; 一般认为close()不会失败, 因此极少校验返回值
读写与定位操作



  1. #include <unistd.h>
  2. // 尝试从fd中读取count个字节到buf中
  3. // 如果成功, 返回读到的字节数, 读到文件尾, 返回0, 失败返回-1
  4. ssize_t read(int fd, void *buf, size_t count);
复制代码


  1. // 如果成功, 返回写入的字节数(返回0表示未写入任何内容), 失败返回-1
  2. // 且会设置errno
  3. ssize_t write(int fd, const void *fd, size_t count);
复制代码

  • 定位
  1. #include <sys/types.h>
  2. #include <unistd.h>
  3. // 从whence位置偏移offset个字节
  4. // whence选项: SEEK_SET(文件首), SEEK_CUR(当前位置), SEEK_END(文件尾)
  5. off_t lseek(int fd, off_t offset, int whence);
复制代码
重写 mycpy

mycpy.c:
  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include <unistd.h>
  4. #include <sys/types.h>
  5. #include <sys/stat.h>
  6. #include <fcntl.h>
  7. #define BUFSIZE 1024
  8. int main(int argc, char **argv)
  9. {
  10.         int sfd, dfd;
  11.         char buf[BUFSIZE];
  12.         ssize_t rs, ws, pos;
  13.         int flag = 1;
  14.         if (argc < 3)
  15.         {
  16.                 fprintf(stderr, "Usage: %s <src_file> <dst_file>\n", argv[0]);
  17.                 exit(1);
  18.         }
  19.         sfd = open(argv[1], O_RDONLY);
  20.         if (sfd < 0)
  21.         {
  22.                 perror("open()");
  23.                 exit(1);
  24.         }
  25.         dfd = open(argv[2], O_WRONLY|O_CREAT|O_TRUNC, 0600);
  26.         if (dfd < 0)
  27.         {
  28.                 close(sfd);
  29.                 perror("open()");
  30.                 exit(1);
  31.         }
  32.         while (1)
  33.         {
  34.                 rs = read(sfd, buf, BUFSIZE);
  35.                 if (rs < 0)
  36.                 {
  37.                         perror("read()");
  38.                         break;
  39.                 }
  40.                 if (rs == 0)
  41.                         break;
  42.                 pos = 0;
  43.                 while (rs > 0)
  44.                 {
  45.                         ws = write(dfd, buf+pos, rs);
  46.                         if (ws < 0)
  47.                         {
  48.                                 perror("write()");
  49.                                 flag = 0;
  50.                                 break;
  51.                         }
  52.                         rs -= ws;
  53.                         pos += ws;
  54.                 }
  55.                 if (!flag)
  56.                         break;
  57.         }
  58.         close(dfd);
  59.         close(sfd);
  60.         exit(0);
  61. }
复制代码
Makefile:
  1. CFLAGS+=-D_FILE_OFFSET_BITS=64 -Wall
复制代码
执行以下命令:
  1. make mycpy
  2. ./mycpy /etc/services ./out
  3. diff /etc/services ./out
复制代码
如果什么也没输出, 则说明mycpy已正确执行
系统 IO 与标准 IO 比较

区别:
系统调用IO: 每调用一次, 会从user态切换到kernel态执行一次(实时性好)
标准IO: 数据先写入缓冲区, 在某一事件(如: 强制刷新/缓冲区满/换行, 详见上一章对行缓冲/全缓冲/无缓冲的描述)发生时才会将缓冲区内数据写入文件/设备(吞吐量大)
提醒:
fileno()可以拿出FILE *的fd字段
fdopen()可以将fd封装到FILE *中
但是, 绝不能将标准IO与系统调用IO混用!
绝大多数情况下, FILE结构体中的pos字段与存储文件所有信息的结构体的pos字段值不相等! 如:
  1. FILE *fp;
  2. fputc(fp) // pos ++
  3. fputc(fp) // pos ++
复制代码
只代表FILE中的pos加二, 文件结构体的pos没有增加, 该pos只会在各种事件后发生改变; 因此, 标准IO与系统调用IO混用基本就会导致错误, 如ab.c:
  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include <unistd.h>
  4. int main()
  5. {
  6.     putchar('a');
  7.     write(1, "b", 1);
  8.     putchar('a');
  9.     write(1, "b", 1);
  10.     putchar('a');
  11.     write(1, "b", 1);
  12.     exit(0);
  13. }
复制代码
该程序会打印"bbbaaa", 可以用strace命令跟踪系统调用IO的发生:
  1. strace ./ab
复制代码
该命令输出的最后几行表示系统调用IO发生的过程:
  1. write(1, "b", 1b)                        = 1
  2. write(1, "b", 1b)                        = 1
  3. write(1, "b", 1b)                        = 1
  4. write(1, "aaa", 3aaa)                      = 3
  5. exit_group(0)                           = ?
  6. +++ exited with 0 +++
复制代码
IO 效率问题

在重写mycpy的案例中, BUFSIZE为$2^n$, 问n为多少时, 效率最高
程序:
  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include <unistd.h>
  4. #include <sys/types.h>
  5. #include <sys/stat.h>
  6. #include <fcntl.h>
  7. #include <math.h>
  8. long long BUFSIZE;
  9. int main(int argc, char **argv)
  10. {
  11.         int sfd, dfd;
  12.         char *buf;
  13.         ssize_t rs, ws, pos;
  14.         int flag = 1, n = 0;
  15.         if (argc < 4)
  16.         {
  17.                 fprintf(stderr, "Usage: %s <src_file> <dst_file> <n>\n", argv[0]);
  18.                 exit(1);
  19.         }
  20.         n = atoi(argv[3]);
  21.         if (n <= 0)
  22.                 exit(1);
  23.         BUFSIZE = 1LL << (n-1);
  24.         buf = malloc(BUFSIZE * sizeof(char));
  25.         if (buf == NULL)
  26.         {
  27.                 perror("malloc()");
  28.                 exit(1);
  29.         }
  30.         sfd = open(argv[1], O_RDONLY);
  31.         if (sfd < 0)
  32.         {
  33.                 perror("open()");
  34.                 exit(1);
  35.         }
  36.         while (1)
  37.         {
  38.                 rs = read(sfd, buf, BUFSIZE);
  39.                 if (rs < 0)
  40.                 {
  41.                         perror("read()");
  42.                         break;
  43.                 }
  44.                 if (rs == 0)
  45.                         break;
  46.                 pos = 0;
  47.                 while (rs > 0)
  48.                 {
  49.                         ws = write(dfd, buf+pos, rs);
  50.                         if (ws < 0)
  51.                         {
  52.                                 perror("write()");
  53.                                 flag = 0;
  54.                                 break;
  55.                         }
  56.                         rs -= ws;
  57.                         pos += ws;
  58.                 }
  59.                 if (!flag)
  60.                         break;
  61.         }
  62.         close(dfd);
  63.         close(sfd);
  64.         exit(0);
  65. }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

耶耶耶耶耶

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表