3.1 引言

文件I/O函数:打开文件,读文件,写文件

常用到五个函数:open, read, write, lseek, close.

本章描述的函数都是:不带缓冲的I/O(unbuffered I/O),属于不带缓冲 是指每个read和write都是调用内核中一个系统调用

3.2 文件描述符

对于内核而言,所有打开的文件都是通过文件描述符引用的

当读或写一个文件的时候,使用open或creat返回的文件描述符标示该文件,将其参数传给read或write

通常文件描述符0与标准输入关联,1与标准输出关联,2与标准错误关联,用STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO来提高可读性

3.3 函数open和openat

open和openat函数返回的文件描述符一定是最小未用的文件描述符

使用open或者openat创建或者打开一个文件

#include<fcntl.h>
int open(const char *path, int oflag, .../*mode_t mode*/);
int openat(int fd, const char *path, int oflag, .../*mode_t mode*/)

oflag参数可以用来 说明此函数的多个选项,用下列一个或多个常量进行"或"运算构成oflag参数

O_RDONLY  只读打开 

O_WRONLY 只写打开
O_RDWR 读写打开  通常分别为0,1,2

O_EXEC 只执行打开

O_SEARCH 只搜索打开(应用与目录)

还有好多在APUE  P50

fd参数把open和openat函数去分开

(1) path参数指绝对路径,fd   可以被忽略open=openat

(2) path指相对路径,fd参数指出了相对路径名在文件系统中的起始地址.fd参数是通过打开相对路径文件名所在的文件目录获取的

(3) path指相对路径,fd参数具有特殊值AT_FDCWD, 这种情况下路径名再当前目录下获得

openat函数希望解决两个问题

(1) 让线程可以使用相对路径名打开目录中的文件,而不是只能打开当前工作目录

(2) 避免time-of-check-to-time-of-use(TOCTTOU)错误:该错误的基本思想是如果两个基于文件的函数调用,其中第二个调用依赖第一个调用的结果那么程序是脆弱的,因为一旦文件有变化,第二个调用的结构可能就不对了

文件名与路径名截断

如果NAME_MAX是14,而存在的文件名恰好是14个字符的文件,那么以路径名作为参数的任意函数讲无法知道文件的原始名是什么.其原因是这些函数无法判断函数是否被拦截过

现代文件系统支持文件名长度可以为255,因此对于绝大多数应用程序没有出过这样的问题

3.4 函数creat

可以使用creat创建一个新的文件

#include<fcntl.h>
int creat(const char *path, mode_t mode);

此函数等效于

open(path, O_ERONLY|O_CREAT|O_TRUNC, mode);

O_CREAT:此函数不存在的时候创建它

O_TRUNC:如果次文件存在,而且为只写或者读写成功打开,则将其长度截断为0

早期的UNIX系统版本中,open的第二个参数只能为0,1,2.无法打开一个尚未存在的文件,因此需要系统调用creat来创建一个文件

creat的一个不足之处是它只能以只写的方式创建文件.在提供open的新版本之前,如果想创建一个临时文件,然后再读这个临时文件,必须先调用creat,close然后再调用open,现在可以直接调用

open(path, O_RDWR|O_CREAT|O_TRUNC, mode);

这样用open可以直接创建一个可以读写的文件

3.5 函数close

可以调用close函数关闭文件

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

关闭一个文件时还会释放该进程加再该文件上的所有记录锁

当一个进程关闭时,内核将自动关闭进程所有打开的文件,很多程序都利用了这一功能而不显示的关闭文件

3.6 函数lseek

每个打开文件都有一个与其相关联的"当前文件偏移量"(current file offset),用来度量从文件开始初的字节数

通常读写操作都是从当前文件偏移量处开始

按系统默认的情况,当打开一个文件的时候,除非制定O_APPEND选项,否则文件偏移量处被设置为零

可以调用lseek显示地为一个打开文件设置文件偏移量

#include<unistd.h>
off_t lseek(int fd, off_t offset, int whence);

对参数offset的解释与参数whence的值有关,若执行成功返回新的文件偏移量,出错返回-1说明该文件不能设置偏移量

(1) whence 是SEEK_SET,将文件的偏移量设置为距文件开始处offset

(2) whence 是SEEK_CUP,将文件的偏移量设置为当前值加offset

(3) whence是SEEK_END,讲文件的偏移量设置为文件长度加上offset, offset可为正可为负

某些设备是允许偏移量为负数的,所以比较lseek返回值的时候必须

<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">要谨慎,不要测试是否小于0,而是是否等于-1</span>

lseek仅将当前的文件偏移量记录在内核中,它并不引起任何的I/O操作.偏移量用于下一个读写操作

文件偏移量可以大于文件长度形成空洞,空洞不占用存储区

3.7 函数read

调用read函数从打开文件中读取数据

#include <unistd.h>
ssize_t read(int fd, void *buf,size_t nbytes);

带符号整数(ssize_t)  不带符号型整数(sszie)

3.8 函数write

用write函数向打开的文件写数据

#include<unistd.h>

ssize_t write(int fd, const void *buf, size_t nbytes);

3.9 I/O的效率

系统cpu的最小值差不多出现再BUFFSIZE为4096及以后的位置,继续增加缓冲区长度对此事件几乎没有影响

系统cpu时间:系统调用内核接口所用的时间

用户cpu时间:运行用户代码所用的时间例如for循环

时钟时间:运行总时间包括cpu等待时间

大多数文件系统为了改善性能都采用某种预读(read ahead)的技术,预读的效果可以从图中看出,缓冲区长度再32字节以后的时钟时间(Clock time) 与拥有较大缓冲区长度的时钟时间几乎是一样的

3.10 文件共享

内核使用3中数据结构标示打开的文件

(1)每个进程在进程表中都有一个纪录项(entry)

a.文件描述符标志(file descriptor flag)

b.指向文件表项的指针(pointer to file table entry)

(2)内核为所有打开的文件维护一张文件表

a.文件状态标志(the file status flag)(读,写,添写,同步和非阻塞)

b.当前文件偏移量(the current file offset)

c.指向该文件v节点表项的指针(the pointer to v-node table entry)

(3)每个打开文件都有一个v节点结构。v节点包含了文件类型以及对文件进行各种操作的指针还有文件的I节点(文件的所有者,文件长度指向文件实际数据块再磁盘所在的位置)

这些信息都是打开文件之后,从磁盘读入内存的

两个进程共享一个文件

如果两个独立的进程各自打开同一个文件

两个进程都打开了该文件,获得各自的文件表项因为每个进程有自己的file offset,但每个给定的文件只有一个v-node表项

Notice: 文件描述符标志(file descriptor flag)和文件状态标志(file status flag),文件描述符是对一个进程的文件描述符,后者是指向该文件表项(file entry)的任何进程的所有描述符

3.11 原子操作

再写文件的时候,“先定位到文件尾,然后再写”,两个函数分开是有问题的。所以可以把两个功能当做一个原子操作
函数pread和pwrite
#include<unistd.h>
ssize_t pread(int fd, void *buf, size_t nbytes, off_t offset);
ssize_t pwrite(int fd, const void *buf, size_t nbytes, off_t offset);
调用pread相当于调用lseek后调用read,但pread是原子操作
创建文件的时候,检查文件是否存在和写操作也应该是原子操作

3.12 函数dup和dup2

下面这两个函数都是用来复制现有的文件描述符
#include<unistd.h>
int dup(int fd);
int dup(int fd, int fd2);

dup返回的新的file descriptor一定是当前可用文件描述符最小数值

dup2由fd2参数制定新的descriptor,如果fd2已经打开,将其关闭,如果fd==fd2则返回fd2
这些函数返回的新的描述符与参数fd共享一个文件表项(file entry)

3.13 函数sync, fsync,和fdatasync

大多数的磁盘I/O都是通过缓冲区进行的,党我们向文件写入数据时,内核通常先将数据复制到缓冲区,然后加入队列,晚些时候在写入磁盘。这种方法叫做延迟写(delayed write)
UNIX系统提供了sync, fsync, fdatasync三个函数
#include<unisted.h>
int fsync(int fd);
int fdatasync(int fd);
void sync(void);

sync是将所有修改过的块缓冲区排入写队列,通常成为updata的系统守护进程周期性地调用sync函数,这就完成了定期冲洗(flush)内核的块缓冲区

fsync函数只是对文件描述符fd指定的文件起作用
fdatasync的函数类似于fsync,它只影响了文件的部分数据

3.14 函数fcntl

fcntl函数可以改变已经打开文件的属性
#include<fcntl.h>
int fcntl(int fd, int cmd, ....);

p65 介绍cmd

3.15 小结

本章说明了UNIX系统提供的基本的I/O函数。
(1)read和write都在内核执行,所以成这些函数为不带缓冲的I/O函数
(2)只用read和write情况下,我们观察了不同的I/O长度对读文件所需的影响。
(3)介绍了将已经写入的数据冲洗到磁盘的方法
(4)介绍了内核用来共享打开文件信息的数据结构

习题

3.1 所有的磁盘I/O都要经过内核的块缓冲区,术语“不带缓冲的I/O”指的是用户的进程中这两个函数不会自动缓冲

UNIX环境高级编程之第3章:文件I/O相关推荐

  1. UNIX环境高级编程之第4章:文件和文件夹-习题

    4.1 stat函数是尾随符号链接的,所以用stat替换lstat不会显示符号链接的信息 4.2 在一个目录下先再shell中输入umask shell进程再进行创建文件的操作.其权限抖都会被屏蔽 4 ...

  2. unix环境高级编程之 read与write 函数详解

    学习记录:unix环境高级编程之 read 与write  函数详解 备注:本博文非本人所写,本人觉得此文讲的非常地道通俗易懂,所以摘录在此以方便以后再次查看 read函数从打开的设备或文件中读取数据 ...

  3. UNIX环境高级编程源码

    UNIX环境高级编程源码 一.下载源码 利用wget命令下载源码,源码地址http://apuebook.com/src.3e.tar.gz 可以在服务器的根目录下创建文件夹,mkdir 1.mkdi ...

  4. UINX环境高级编程笔记 第3章 文件I/O

    UNIX环境高级编程 第三章 文件I/O 3.1 引言 3.2 文件描述符 3.3 函数open和openat 3.4 函数creat 3.5 函数close 3.6 函数lseek 3.7 函数re ...

  5. UNIX 环境高级编程总结——第五章 标准I/O 库

    5.1 流和 FILE 对象 对于标准 I/O 库,它们的操作则是围绕流(stream)进行的. 当打开一个流时,标准 I/O 函数 fopen 返回一个指向 FILE 对象的指针. 为了引用一个流, ...

  6. 《Unix环境高级编程》Note——第一章基础知识

    文章目录 第一章 Unix基础知识 1.引言 2.Unix体系结构 3.登录 4.文件和目录 5.输入和输出 6.程序和进程 7.出错处理 8.用户标识 9.信号 10.时间值 11.系统调用和库函数 ...

  7. 《UNIX环境高级编程》六系统数据文件和信息读书笔记

    UNIX系统的正常运作需要使用大量与系统有关的数据文件,例如,口令文件/etc/passwd和组文件/etc/group等. 1.口令文件 口令文件的各字段包含在pwd.h>中定义的passwd ...

  8. UNIX环境高级编程(屏幕打印和inet_ntoa输出异常问题)

    一.来回在屏幕和文件之间打印 在网上没找到在屏幕和文件来回打印的方法,翻了下UNIX环境高级编,里面有个freopen用于在一个指定的流上打开一个指定的文件,如果要将标准输出打印到文件,需要调用该函数 ...

  9. (三) 一起学 Unix 环境高级编程 (APUE) 之 文件和目录

    . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...

  10. python flask高级编程之restful_('Python Flask高级编程之RESTFul API前后端分离精讲',),全套视频教程学习资料通过百度云网盘下载...

    资源详情 r n t某课网好评度100%的Python Flask高级编程之RESTFul API前后端分离精讲 r n t t t第1章 随便聊聊 r n t t t聊聊Flask与Django,聊 ...

最新文章

  1. nginx之 nginx-1.9.7 编译安装、理论简介
  2. CSS2020聚焦新基建 腾讯发布云原生安全体系 助力客户备战云上“主战场”
  3. P3723-[AH2017/HNOI2017]礼物【FFT】
  4. 无限轮播图片的实现原理
  5. ATS push cache 测试
  6. tensorflow入门指南
  7. Puppet之基础篇
  8. Tampermonkey油猴教程及Greasyfork脚本使用
  9. 请实现数组按照数组项中userName中的姓名拼音排序
  10. 基因表达式编程(GEP)自学 第【7】天 Python 实现
  11. 打开EXCEL运行脚本,报无法运行宏问题
  12. 执行方案(Command)
  13. 欧文工学院计算机,UCI的EECS「加州大学欧文分校电气工程与计算机科学系」
  14. 解决 Xshell6|Xftp6 强制升级问题
  15. 位图排序算法优化篇-永无止境
  16. 海贼王经典语录(转)
  17. 超大数据10进制转2进制详解(可推广到其他进制)/ Codeup 100000579 问题 C: 进制转换
  18. Android电池功耗BatteryHistorian数据分析
  19. 人件札记:管理者要学会不加班
  20. Python - 文件基础操作

热门文章

  1. for循环使用后contains方法失去效果
  2. Ubuntu18.04安装
  3. [贪心][模拟] Jzoj P5811 简单的填数
  4. 第六节 静态的(static)和单例模式
  5. Linux安装后的基本配置
  6. samba服务器常用指令
  7. [第5天]nginx环境下404问题
  8. findpeaks 寻找峰值函数
  9. BricsCAD 21 for Mac(CAD建模软件)多语言版
  10. TeamViewer 远程设备的画面黑屏怎么办?