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 环境高级编程(APUE) 之 系统数据文件和信息

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

  3. 《UNIX环境高级编程》第七章进程环境

    7.2 main函数 1.C程序总是从main函数开始执行的,原型:int main(int argc,char *argv[]);argc是命令行参数的个数argc是指向参数的各个指针所构成的数组2 ...

  4. UNIX环境高级编程(5):文件I/O(1)

    UNIX系统中的大多数文件I/O仅仅须要用到5个函数:open.read.write.lseek以及close.本章说明的函数常常称为"不带缓冲的I/0",术语不带缓冲指的是每一个 ...

  5. 《UNIX环境高级编程》学习心得 四 文件I/O(一)

    这里说的文件I/O是相对标准I/O来说的.主要介绍在UNIX系统中常用的五个文件I/O函数:open.read.write.lseek.以及close. 一.open和opennat #include ...

  6. UNIX环境高级编程(6):文件I/O(2)

    文件共享: UNIX系统支持在不同进程间共享打开的文件. 内核使用三种数据结构表示打开的文件.他们之间的关系决定了在文件共享方面一个进程对还有一个进程可能产生的影响: (1)每一个进程在进程表中都有一 ...

  7. (十三) [终篇] 一起学 Unix 环境高级编程 (APUE) 之 网络 IPC:套接字

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

  8. (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO

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

  9. (五) 一起学 Unix 环境高级编程 (APUE) 之 进程环境

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

随机推荐

  1. [ HAOI 2010 ] 最长公共子序列

    \(\\\) \(Description\) 求两个长度\(\le5000\)的大写字母串的\(LCS\)长度及个数,定义两\(LCS\)中某一字符在两序列出现位置有一处不同就视为不同. \(\\\) ...

  2. 将电脑浏览器User-Agent识别改成手机浏览器UA几种简单方法

    第一种方法:修改浏览器的快捷方式 右击桌面上的Chrome浏览器图标,在弹出的右键菜单中选择“复制”,复制一个图标副本到桌面.右击该副本,选择“属性”,打开相应的对话框,在“目标”文本框的字符后面添加 ...

  3. JS——tab函数封装

    1.为li标签添加index属性,这个属性正好就是span标签数组的index值 2.函数封装适合页面有多个tab切换,需要注意的在获取的li标签和span标签对象时,必须将对应div对象作为参数传入 ...

  4. JSP学习笔记 - 内置对象 Request

    1.主要掌握以下5个内置对象及其所属类,必须学会在java docs里根据类名查找相应的方法 request     javax.servlet.http.HttpServletRequest res ...

  5. layer:好看的弹出窗口

    layer是一款web弹层组件,只需在调用时简单地配置相关参数,即可轻松实现丰富与便捷的操作体验. 这是layer的官方地址,里面的使用介绍非常详细(http://layer.layui.com/) ...

  6. redis查看数据

    目前Redis缓存数据库在许多行业平台大量应用,有效解决了高并发等特定场景的应用性能瓶颈,Redis数据的查看.维护,性能监控有没有好用的工具呢,目前TreeSoft数据库管理系统可以满足实现需求. ...

  7. 观察者模式之Golang实现

    观察者模式的具体概念原理,参见https://baike.baidu.com/item/%E8%A7%82%E5%AF%9F%E8%80%85%E6%A8%A1%E5%BC%8F/5881786?fr ...

  8. CentOS7下安装ELK三件套

    ELK用于分布式收集,然后elasticsearch用于分析数据,在Kibana中可以查看数据.报表. 目前公司日志数据量暂时不使用elasticsearch集群,只是用的elasticsearch单 ...

  9. svn 使用TortoiseSVN server搭建本地SVN服务器

    使用TortoiseSVN server搭建本地SVN服务器

  10. 腾讯云&搭建微信小程序服务

    准备域名和证书 任务时间:20min ~ 40min 小程序后台服务需要通过 HTTPS 访问,在实验开始之前,我们要准备域名和 SSL 证书. 域名注册 如果您还没有域名,可以在腾讯云上选购,过程可 ...