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 笔记的更多相关文章

  1. APUE学习笔记3_文件IO

    APUE学习笔记3_文件IO Unix中的文件IO函数主要包括以下几个:open().read().write().lseek().close()等.这类I/O函数也被称为不带缓冲的I/O,标准I/O ...

  2. 《嵌入式linux应用程序开发标准教程》笔记——6.文件IO编程

    前段时间看APUE,确实比较详细,不过过于详细了,当成工具书倒是比较合适,还是读一读这种培训机构的书籍,进度会比较快,遇到问题时再回去翻翻APUE,这样的效率可能更高一些. <嵌入式linux应 ...

  3. (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO

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

  4. 树莓派学习笔记——使用文件IO操作GPIO SysFs方式

    0 前言     本文描写叙述假设通过文件IO sysfs方式控制树莓派 GPIO端口.通过sysfs方式控制GPIO,先訪问/sys/class/gpio文件夹,向export文件写入GPIO编号, ...

  5. APUE 文件IO

    文件 IO 记录书中的重要知识和思考实践部分 Unix 每个文件都对应一个文件描述符(file descriptor),为一个非负整数,一个文件可以有多个fd, 后面所有与文件(设备,套接字等)有关操 ...

  6. Android学习笔记--存储方案(SharedPreference、文件IO)

    1. SharedPreference SharedPreference可以很容易的保存key-value对,通常用于保存配置信息.保存的步骤 1. 获得SharedPreferences对象 (最后 ...

  7. Linux 0.11源码阅读笔记-文件IO流程

    文件IO流程 用户进程read.write在高速缓冲块上读写数据,高速缓冲块和块设备交换数据. 什么时机将磁盘块数据读到缓冲块? 什么时机将缓冲块数据刷到磁盘块? 函数调用关系 read/write( ...

  8. Java IO学习笔记:概念与原理

    Java IO学习笔记:概念与原理   一.概念   Java中对文件的操作是以流的方式进行的.流是Java内存中的一组有序数据序列.Java将数据从源(文件.内存.键盘.网络)读入到内存 中,形成了 ...

  9. Java IO学习笔记总结

    Java IO学习笔记总结 前言 前面的八篇文章详细的讲述了Java IO的操作方法,文章列表如下 基本的文件操作 字符流和字节流的操作 InputStreamReader和OutputStreamW ...

随机推荐

  1. 关于DOM事件操作

    事件的三要素: 事件源.事件.事件驱动程序. 事件源.: 引发后续事件的html标签   document.getElementById(“box”) document.getElementsByCl ...

  2. send/receive h264/aac file/data by rtp/rtsp over udp/tcp

    一.安装一些必要的调试工具 1.vlc安装sudo apt-get install vlcsudo apt-get install vlc-nox 2.ffmpeg安装,带ffplay,ffplay依 ...

  3. 简明python教程十一----更多Python的内容

    特殊的方法 __init__(self,...):这个方法在新建对象恰好要被返回使用之前被调用 __del__(self):恰好在对象要被删除之前调用 __str__(self):我们对对象使用pri ...

  4. PAT 1099 Build A Binary Search Tree[BST性质]

    1099 Build A Binary Search Tree(30 分) A Binary Search Tree (BST) is recursively defined as a binary ...

  5. JVM之基本概念

    1.类加载子系统:负责从文件系统或者网络中加载Class信息,加载的信息存放在一块称之为方法区的内存空间. 2.方法区:就是存放类信息.常量信息.常量池信息.包括字符串字面量和数字常量等.方法区是辅助 ...

  6. pycharm使用技巧。(mac版本)

    一.pycharm使用中的一些快捷键 1.cmd  + b 跳转到声明处(cmd加鼠标) 2.option + c 复制光标当前行,剪切同理 3.option + v 粘贴复制的行 4.option ...

  7. PKU 1035 Spell checker(Vector+String应用)

    题目大意:原题链接 1输入一个字符串,如果字典中存在该字符串,直接输出,否则; 2.删除,替换,或插入一个字母.如果使得输入字符串==字典中已经有的单词,输出该单词,否则. 3.直接输入下一个字符串, ...

  8. Jenkins的权限控制和Rundeck的远程认证

    1.权限控制的基本设置 1.1选择基于角色权限的分配策略 1.2 配置全局权限和项目权限 具体的权限对应关系见下表: Overall(全局) Credentials(凭证) Slave(节点) Job ...

  9. ImageNet历年冠军和相关CNN模型

    ImageNet 是一个超过15 million的图像数据集,大约有22,000类. 是由李飞飞团队从2007年开始,耗费大量人力,通过各种方式(网络抓取,人工标注,亚马逊众包平台)收集制作而成,它作 ...

  10. Postman 把response的值自动放到变量里

    @ 1里面定义个变量2 3这里加上postman.setEnvironmentVariable("MatchID",JSON.parse(responseBody)); 这样rep ...