C3 文件IO:APUE 笔记
C3:文件IO
1 引言
本章描述的函数被成为不带缓冲的IO,涉及5个函数:open、read、write、lseek、close。
文件控制:dup、sync、fsync、fdatasync、fcntl、ioctl。
2 文件描述符
文件描述符为非负整数,取值范围为0 ~ OPEN_MAX - 1,调用open、create返回参数路径的文件描述符,可以作为传递给read、write。
  可使用shell命令查询系统对OPEN_MAX定义,grep -rn --col OPEN_MAX /usr/include
linux三个特殊的文件描述符:
0:标准读,STDIN_FILENO,该常亮在unistd.h中定义
1:标准写,STDOUT_FILENO
2:标准错误,STDERR_FILENO
3 函数open与openat
该方法可以打开或者创建一个文件,头文件 fctnl.h。成功返回文件描述符,失败返回-1。
/* Open FILE and return a new file descriptor for it, or -1 on error.
OFLAG determines the type of access used. If O_CREAT or O_TMPFILE is set
in OFLAG, the third argument is taken as a `mode_t', the mode of the
created file. This function is a cancellation point and therefore not marked with
__THROW. */
#ifndef __USE_FILE_OFFSET64
extern int open (const char *__file, int __oflag, ...) __nonnull (());
#else
# ifdef __REDIRECT
extern int __REDIRECT (open, (const char *__file, int __oflag, ...), open64)
__nonnull (());
# else
# define open open64
# endif
#endif #ifdef __USE_ATFILE
/* Similar to `open' but a relative path name is interpreted relative to
the directory for which FD is a descriptor. NOTE: some other `openat' implementation support additional functionality
through this interface, especially using the O_XATTR flag. This is not
yet supported here. This function is a cancellation point and therefore not marked with
__THROW. */
# ifndef __USE_FILE_OFFSET64
extern int openat (int __fd, const char *__file, int __oflag, ...)
__nonnull (());
# else
# ifdef __REDIRECT
extern int __REDIRECT (openat, (int __fd, const char *__file, int __oflag,
...), openat64) __nonnull (());
# else
# define openat openat64
# endif
# endif
# ifdef __USE_LARGEFILE64
extern int openat64 (int __fd, const char *__file, int __oflag, ...)
__nonnull (());
# endif
#endif
各参数释义如下:
path:打开或者创建文件的名字。
oflag:文件状态标志,如下:
| 常量 | 释义 | 
| O_RDONLY | 只读打开 | 
| O_WRONLY | 只写打开 | 
| O_RDWR | 读写打开 | 
| O_EXEC | 只执行打开 | 
| O_SEARCH | 只搜索打开(仅应用于目录) | 
| O_APPEND | 写文件时,追加到文件末尾 | 
| O_CLOEXEC | 把FD_CLOEXEC设置为文件描述符标志 | 
| O_CREATE | 创建文件,参数mode用于指定访问权限 | 
| O_DIRECTORY | 如果path不是目录,则出错 | 
| O_EXCL | 测试文件是否存在。不能与O_CREAT公用,否则会报错 | 
| O_NOCTTY | 如果path是终端设备,则不降该设计分配为此进场的控制终端 | 
| O_NOFOLLOW | 如果path是符号链接,则出错 | 
| O_NONBLOCK | 如果path时FIFO、块特殊文件、字符特殊文件,则将本次打开操作及后续IO操作设置为非阻塞 | 
| O_SYNC | 每次write等待IO操作完成 | 
| O_TRUNK | 文件存在,且为写打开(包括只写、读写),则将文件长度截断为0。即写指针移位到0 | 
| O_TTY_INIT | |
| O_DSYNC | write等待IO操作完成,如果写操作不影响读取刚写入的数据,则不需要等待文件属性被更新 | 
| O_RSYNC | read操作等待,直至所有对文件同一部分挂起的写操作都完成 | 
openat与open区别如下:
- 如果openat的path是绝对文件名,则fd参数被忽略,openat等同于open
 - path参数是相对文件名,fd指定了相对路径名的文件描述符,fd参数通过打开相对路径名所在的目录获取文件。
 - path指定相对路径名,fd参数具有特殊值AT_FDCWD,则路径名表示为当前目录获取,在操作上与open函数类似。
 
4 函数creat
创建一个新文件,头文件fcntl.h。成功返回文件描述符,失败返回-1
/* Create and open FILE, with mode MODE. This takes an `int' MODE
argument because that is what `mode_t' will be widened to. This function is a cancellation point and therefore not marked with
__THROW. */
#ifndef __USE_FILE_OFFSET64
extern int creat (const char *__file, mode_t __mode) __nonnull (());
#else
# ifdef __REDIRECT
extern int __REDIRECT (creat, (const char *__file, mode_t __mode),
creat64) __nonnull (());
# else
# define creat creat64
# endif
#endif
#ifdef __USE_LARGEFILE64
extern int creat64 (const char *__file, mode_t __mode) __nonnull (());
#endif
等价于open(path, O_WRONLY | O_CREAT | O_TRUNK, mode)
5 函数close
关闭一个打开的文件,头文件 unistd.h。成功返回0,失败返回-1
/* Close the file descriptor FD.
This function is a cancellation point and therefore not marked with
__THROW. */ extern int close (int __fd);
关闭一个文件会释放该进程加在文件上的所以记录锁。
当一个进场终止时,内核会自动关闭它打开的所有文件,很多程序利用这一功能而不显示调用close关闭文件。
6 函数lseek
操作文件偏移量,头文件 unistd.h。成功返回文件偏移量,失败返回-1
/* Move FD's file position to OFFSET bytes from the
beginning of the file (if WHENCE is SEEK_SET),
the current position (if WHENCE is SEEK_CUR),
or the end of the file (if WHENCE is SEEK_END).
Return the new file position. */
#ifndef __USE_FILE_OFFSET64
extern __off_t lseek (int __fd, __off_t __offset, int __whence) __THROW;
#else
# ifdef __REDIRECT_NTH
extern __off64_t __REDIRECT_NTH (lseek,
(int __fd, __off64_t __offset, int __whence),
lseek64);
# else
# define lseek lseek64
# endif
#endif
参数whence释义如下:
- 取值为SEEK_SET,则将文件偏移量设置为距离文件开始处offset个字节
 - 取值为SEECK_CUR,则将文件偏移量设置为当前值加上offset,offset可正可负
 - 取值为SEEK_END,则将文件偏移量设置为文件长度加上offset,offset可正可负
 
如果文件描述符是FIFO、pipe、socket,则返回-1,errno设置为ESPIPE。
注意
文件偏移量可能是负值,测试返回结果要测试 -1 == lseek( ... )
lseek不引发任何IO操作
文件偏移量可以大于文件当前长度,但是文件中没有写过的字节都会被设置为0。这部分文件数据被称为文件空洞
7 函数read
从打开的文件中读数据,头文件 unistd.h。返回读到的字节数,0表示文件末尾EOF,-1表示出错
/* Read NBYTES into BUF from FD. Return the
number read, -1 for errors or 0 for EOF. This function is a cancellation point and therefore not marked with
__THROW. */
extern ssize_t read (int __fd, void *__buf, size_t __nbytes) __wur;
8 函数write
向打开的文件中写数据,头文件 unistd.h。返回值应当于写入的数据长度相同,否则就表示出错。
 /* Write N bytes of BUF to FD.  Return the number written, or -1.
     This function is a cancellation point and therefore not marked with
     __THROW.  */
  extern ssize_t write (int __fd, const void *__buf, size_t __n) __wur;
write出错的常见原因是磁盘满,或者超过一个给定进程的文件长度限制。
9 UNIX的IO数据结构
.
内核使用三种数据结构表示打开文件,它们之间的关系决定文件共享方面一个进程对另外一个进程可能产生的影响。
- 进程表项:列表,每项包含一个文件描述符标志,以及一个指向文件表项的指针
 - 文件表:包含文件状态标志、当前文件偏移量、指向文件v节点的指针
 - v节点:v节点包含文件类型、对此文件进行各种操作函数的指针。v节点还包含文件的i节点,i节点包含文件的所有者、文件长度、文件在磁盘上的指针等
 
understanding v-node(virtual node) : https://www.ibm.com/support/knowledgecenter/en/ssw_aix_72/kernelextension/virtual_nodes.html
understanding i-noe : https://www.ibm.com/support/knowledgecenter/en/ssw_aix_72/kernelextension/generic_inodes.html
图3-1表示进程打开了2个不同类型的文件,标准输入0,标准输出1。
如果不同进程打开同一个文件,则有下图所示关系。

10 原子操作
10.1 写操作
以下两种操作并不等价
//1: lseek + write
lseek(fd, ,SEEK_END);
write (fd, buf, bufsize); //2: APPEND + write
open (path, O_WRONLY | O_APPEND);
write (fd, buf, bufsize);
第一种操作在每次write之前调用lseek将文件偏移量设置到文件末尾,再执行写操作。使用2个函数将无法保证操作的原子性。
第二种操作使用APPEND,内核在每次写操作之前将偏移量设置到该文件末尾,原子性由操作系统保证。
10.2 创建文件
open函数的O_CREAT(创建)和O_EXCL(检测文件是否存在)互斥,都是原子操作。
11 函数dup和dup2
用于复制一个现有的文件描述符,头文件 unistd.h。成功返回新的文件描述符,失败返回-1
/* Duplicate FD, returning a new file descriptor on the same file. */
extern int dup (int __fd) __THROW __wur; /* Duplicate FD to FD2, closing FD2 and making it open on the same file. */
extern int dup2 (int __fd, int __fd2) __THROW;
由dup返回的新文件描述符一定是当前可用文件描述符的最小数值。对于dup2,如果fd2已经打开,则先将其关闭。如果fd等于fd2,则直接返回fd2。
12 函数sync、fsync、fdatasync
当linux向文件写入数据时,内核通常先将数据复制到缓冲区,再排入队列,晚些时候再写入磁盘,这种方式被称为延迟写。为保证磁盘上实际文件与缓冲区一致,UNIX提供了sync、fsync、fdatasync三个函数。
头文件 unistd.h。成功返回0,失败返回-1。
/* Make all changes done to all files actually appear on disk. */
extern void sync (void) __THROW; /* Make all changes done to FD actually appear on disk.
This function is a cancellation point and therefore not marked with __THROW. */
extern int fsync (int __fd); /* Synchronize at least the data part of a file with the underlying media. */
extern int fdatasync (int __fildes);
区别:
- sync只是将所有修改的块缓冲区排入写队列,然后就返回,不等待写磁盘操作完成。通常,称为update的系统守护进程周期性的调用sync函数。
 - fsync函数只对文件描述符指定的文件起作用,等待磁盘操作完成后返回。可用于数据库操作
 - fdatasync只影响文件的数据部分
 
13 函数fcntl
可以改变打开的文件属性,头文件fcntl.h。成功,依赖cmd返回。失败,返回-1。
/* Do the file control operation described by CMD on FD.
The remaining arguments are interpreted depending on CMD. This function is a cancellation point and therefore not marked with
__THROW. */
extern int fcntl (int __fd, int __cmd, ...);
fcntl函数有以下5个功能:
- 复制一个文件描述符(cmd = F_DUPFD 或者 F_DUPFD_CLOEXEC)。函数dup包含close和fcntl,是原子操作。fcntl非原子操作。
 - 获取、设置文件描述符标志 (cmd = F_GETFD 或 F_SETFD)。F_GETFD,当前只定义了一个文件描述符标志FD_CLOEXEC
 - 获取、设置文件状态标志 (cmd = F_GETFL 或 F_SETFL)。文件状态标志为open函数的flag参数,见第三节
 - 获取、设置异步IOS所有权 (cmd = F_GETOWN 或 F_SETOWN)
 - 获取、设置记录锁 (cmd = F_GETLK、F_SETLK、F_SETLKW)
 
14 函数ioctl
控制IO操作的函数,第三章其余函数功能的补集。头文件unistd.h和sys/ioctl.h。
/* Perform the I/O control operation specified by REQUEST on FD.
One argument may follow; its presence and type depend on REQUEST.
Return value depends on REQUEST. Usually -1 indicates error. */
extern int ioctl (int __fd, unsigned long int __request, ...) __THROW;
15 /dev/fd
打开文件 /dev/fd/n 等同于复制描述符 n。
例如:fd = open("/dev/fd/0“, mode)等价于 fd = dup(0)。
/dev/stdin == /dev/fd/0
/dev/stdout == /dev/fd/1
/dev/stderr == /dev/fd/2
如下图,利用cat显示,输入为/dev/stdin ,输出到/dev/stdout
    
C3 文件IO:APUE 笔记的更多相关文章
- APUE学习笔记3_文件IO
		
APUE学习笔记3_文件IO Unix中的文件IO函数主要包括以下几个:open().read().write().lseek().close()等.这类I/O函数也被称为不带缓冲的I/O,标准I/O ...
 - 《嵌入式linux应用程序开发标准教程》笔记——6.文件IO编程
		
前段时间看APUE,确实比较详细,不过过于详细了,当成工具书倒是比较合适,还是读一读这种培训机构的书籍,进度会比较快,遇到问题时再回去翻翻APUE,这样的效率可能更高一些. <嵌入式linux应 ...
 - (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO
		
. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...
 - 树莓派学习笔记——使用文件IO操作GPIO SysFs方式
		
0 前言 本文描写叙述假设通过文件IO sysfs方式控制树莓派 GPIO端口.通过sysfs方式控制GPIO,先訪问/sys/class/gpio文件夹,向export文件写入GPIO编号, ...
 - APUE 文件IO
		
文件 IO 记录书中的重要知识和思考实践部分 Unix 每个文件都对应一个文件描述符(file descriptor),为一个非负整数,一个文件可以有多个fd, 后面所有与文件(设备,套接字等)有关操 ...
 - Android学习笔记--存储方案(SharedPreference、文件IO)
		
1. SharedPreference SharedPreference可以很容易的保存key-value对,通常用于保存配置信息.保存的步骤 1. 获得SharedPreferences对象 (最后 ...
 - Linux 0.11源码阅读笔记-文件IO流程
		
文件IO流程 用户进程read.write在高速缓冲块上读写数据,高速缓冲块和块设备交换数据. 什么时机将磁盘块数据读到缓冲块? 什么时机将缓冲块数据刷到磁盘块? 函数调用关系 read/write( ...
 - Java IO学习笔记:概念与原理
		
Java IO学习笔记:概念与原理 一.概念 Java中对文件的操作是以流的方式进行的.流是Java内存中的一组有序数据序列.Java将数据从源(文件.内存.键盘.网络)读入到内存 中,形成了 ...
 - Java IO学习笔记总结
		
Java IO学习笔记总结 前言 前面的八篇文章详细的讲述了Java IO的操作方法,文章列表如下 基本的文件操作 字符流和字节流的操作 InputStreamReader和OutputStreamW ...
 
随机推荐
- Flask上下文管理
			
一.一些python的知识 1.偏函数 def add(x, y, z): print(x + y + z) # 原本的写法:x,y,z可以传任意数字 add(1,2,3) # 如果我要实现一个功能, ...
 - rabbitMQ基本概念
			
一.网页登录方法 http://127.0.0.1:15672/ 用户名和密码默认为guest/guest 用java代码去连接rabbitmq用的端口是5672 二.rabbitMQ基本概念 Rab ...
 - JSP页面实现自动跳转!
			
JSP页面实现自动跳转!一.页面自动刷新: 把如下代码加入<head>区域中<meta http-equiv=”refresh” content=”5″>注:content=” ...
 - mysql 提示符显示用户,数据库等信息
			
命令: mysql -uroot -p --prompt="\\u@\\h:\\d \\r:\\m:\\s>" 效果: root@localhost:(mysql) 02:2 ...
 - Java 面试总结  面试常问的关键字总结
			
文章出处http://www.cnblogs.com/IUbanana/p/7116520.html 关键字: final finalize finally throws和throw static关键 ...
 - 基于HTML5 FileSystem API的使用介绍(转)
			
FileSystem提供了文件夹和文件的创建.移动.删除等操作,大大方便了数据的本地处理, 而且所有的数据都是在沙盒(sandboxed)中,不同的web程序不能互相访问,这就保证了数据 的完整和安全 ...
 - 安装java项目开发环境
			
搭建java 查看本机是否已有java 如果有,请卸载. 下载jdk 复制到服务器中 临时配置你在shell里面改,只是做了临时更改啊,一重启就没了 配置到系统的环境变量里 export JAVA_H ...
 - 哪个地图API 好用
			
之前我们能用的地图软件还寥寥无几,而且一个地图包动辄就上百M,还不支持GPS,没有实时路况,没有卫星图,一年也未必更新一次.现如今的地图功能已经极大丰富了,开发者的项目选择性也很大,地图哪个受众比较多 ...
 - 【AngularJS】通过jsonp与webmethod交互,实现ajax验证
			
服务端配置 1:新建一个asp.net的网站 2: 创建一个服务文件:LoginService.asmx 注意:记得取消[System.Web.Script.Services.ScriptServic ...
 - ubuntu中mysql版本升级到5.7
			
0 前言 前几天图书馆说服务器(Ubuntu14.04)有安全漏洞,不按时修复会关停. 看了一下漏洞清单,主要是ssh和mysql的版本问题. 把mysql升级了一下,升到了5.7,升级之前还备份了数 ...