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. 《FLASH PROGRAMMING 那些事》总结

    注明来自 http://www.ssdfans.com/?p=5589 以MLC为例: 对FGF(Floating Gate Flash)技术的,MLC programming一般分两步走:先prog ...

  2. centos 解决error: rpmdbNextIterator问题 (转)

    昨天重装RedHat4以后出现了下面这个问题 [root@RedHat4 ~]# rpm -qa |grep vimerror: rpmdbNextIterator: skipping h#      ...

  3. a=a+1背后的内存模型和CPU高速缓存

    学过JAVA的人都知道,程序运行过程中的临时数据,都是从外部存储设备调入内存(物理内存)中,再进行读写操作的.而计算机在执行程序时,对程序的每条指令都是在CPU中执行的,而指令的执行,势必涉及到对数据 ...

  4. 推荐系统第4周--- 基于频繁模式的推荐系统和关联规则挖掘Apriori算法

    数据挖掘:关联规则挖掘

  5. python16_day25【crm】

    一.CRM模拟admin功能 1.过滤功能 2.显示数据分页 3.动态菜单 项目:https://github.com/willianflasky/growup/tree/master/s16/hom ...

  6. android 带CheckBox对话框

    package com.example.dialog4; import android.os.Bundle;import android.app.Activity;import android.app ...

  7. Zen cart 根据数量打折插件

    Quantity Discounts 可以根据顾客购买多少来打折,很不错. 假如顾客买了3个以上的产品,就给他10%折扣,设置如下: Turn On Quantity Discount 1. In t ...

  8. tensorflow(二)----线程队列与io操作

    一.队列和线程 1.队列: 1).tf.FIFOQueue(capacity, dtypes, name='fifo_queue') 创建一个以先进先出的顺序对元素进行排队的队列 参数: capaci ...

  9. Andriod实现推送的解决方案(转)

    Andriod上实现消息推送的一般解决策略 第一种解决方案:C2DM云端推送功能 在Android手机平台上,Google提供了C2DM(Cloudto Device Messaging)服务,该服务 ...

  10. 开发者不可不知的五款DIY快速开发工具,你造吗

    对于非专业的移动开发者,弱化编程能力的快发开发工具实用性够强,无需编程只要借助工具提供的各种功能模块,就能开发出属于自己的应用,而支持DIY更能使应用开发锦上添花,借助快速开发工具开发出属于自己的“能 ...