热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

APUE读书笔记(3)第三章文件IO

第三章文件IO一:主要内容:  本章主要讲述UNIX系统中的文件系统,包括文件信息的记录方式;文件的函数;文

第三章 文件IO

一:主要内容:
  本章主要讲述UNIX系统中的文件系统,包括文件信息的记录方式;文件的函数;文件的共享等。

二:文件描述符
  对于内核来说,所有打开的文件都是用文件描述符来标识的,文件描述符是一个非负整数。每当一个文件打开或者创建时,内核都会想进程返回一个文件描述符来标识该文件,这个描述符可以用来进行其他操作,说白了就是内核区别多个打开文件的一个flag。
  值得一提的是,UNIX已经提前将三个文件描述符与标准输入输出和错误相关联,这三个文件描述符就是0、1、2.其中0对应标准输入,1对应标准输出,2对应标准错误
  文件描述符存在一个变化范围:0-OPEN_MAX-1。早期的系统上限值为19,现在很多系统都将其增加值63.
三:函数open和openat
  这两个函数可以打开或者创建一个文件,函数声明如下:

int open(const char *path, int oflag, ...);
int openat(int fd, const char *path, int oflag, ...);

  path代表文件路径,oflag代表了文件的一些说明,如是否为只读打开,是否为追加模式,是否对path为非目录是返回错误等等。
  值得一提的是,这两个函数所返回的文件描述符一定是未使用的文件描述符值最小的那个
  open函数和openat函数区别不大,当path为绝对路径的是否这两个函数功能完全一样;当path为相对路径的时候且fd为AT_FDCWD时,openat中的path是以当前目录为基准;当path为相对路径且fd不是AT_FDCWD,openat中的path以fd为基准。
  在这里我们还需要考虑一下文件名称和路径名称截断的问题。有的系统会将超过文件名称的部分阶段,有的会报错。一般来说现在Linux系统文件名称的最大长度为255,在windows下面,单个文件名的长度限制是255,完整的路径长度(如E:\test\aaa.txt这样限制是260)。

四:函数create
  函数create的声明如下:

int create(const char *path, mode_t mode);

  文件创建成功返回对应fd,失败返回-1.

五:函数close
  函数close的声明如下:

int close(int fd);

六:函数lseek
  函数lseek声明如下:

off_t lseek(int fd, off_t offset, int whence);

  其中fd为文件描述符,offset为偏移量,whence为偏移量的设置方式。
  当whence为SEEK_SET,文件的偏移量设置为文件开始处offset个字节;当whence为SEEK_CUR,文件的偏移量设置为当前值加offset,offset可正可负;当whence为SEEK_END,文件的偏移量设置为文件长度加offset,offset可正可负。
  需要注意的是,如果文件的偏移量设置的有问题,可能会导致文件中间有一部分为空(比如当前偏移量为10,强行将偏移量改为20,那么中间又10个字节的空洞)。
七:函数read
  函数read声明如下:

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

  如果read执行成功,返回读取的字节数;如果已经读到了文件末尾,返回0.
八:函数write
  write函数声明如下:

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

  一般来说返回值与写入的值nbytes相同,否则表示出错。一般出错的原因是磁盘已经写满了。
九:IO的效率
  对于同一个文件,只是用read和write进行复制,但是指定的nbytes不同,发现当nbytes的值超过32字节的时候效率比较高,并且在超过32字节之后所消耗的差不多,这是由于大多数的文件系统为了改善性能都会有一个预读的技术。

十:文件共享
  UNIX系统支持在不同进程间共享打开的文件。内核使用三种数据结构表示打开的文件:
  1.进程表记录项中的包含有一张打开文件描述符表,每个文件描述符表包括:
   a)文件描述符标志(close_on_exec)
文件描述符标志是一个进程所有文件描述符的位图标志,每个比特位代表一个打开的文件描述符,用于确定在调用系统调用execve()时需要关闭的文件句柄。
   b)指向一个文件表项的指针
  2.内核为每个打开的文件维持一张文件表,每个文件表项包含:
   a)文件状态标志(读、写、添写、同步和非阻塞等)
   b)当前文件偏移量
   c)指向该文件v节点表项的指针
  3.每个打开的文件都有一个v节点结构。v节点包含了文件类型和对此文件进行各种操作的函数的指针。对于绝大多数文件,v节点还包含了该文件的i节点。这些信息是在打开文件时从磁盘上读入内存的,所以所有关于文件的信息都是快速可供使用的。例如:i节点包含了文件的所有者、文件长度、文件所在的设备、指向文件实际数据块在磁盘上所在位置的指针等等。
在这里插入图片描述
  如果两个独立的进程各自打开同一个文件,假设第一个进程在文件描述符3上打开该文件,另一个进程在文件描述符4上打开该文件。打开该文件的每个进程都得到一个文件表项,因为每个进程都有它自己的对该文件的当前偏移量。而对于一个给定的文件,只有一个v节点表项。
在这里插入图片描述
十一:原子操作
  所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束。
  在多线程的环境中,如果两个线程同时对一个文件进行设置偏移量,写文件的操作可能会导致一些问题。但是如果可以将定位偏移量和写操作封装为一个原子操作就不会出现问题,UNIX提供了函数pread和函数pwrite来实现原子操作。
十二:dup和dup2函数
  dup和dup2也是两个非常有用的调用,它们的作用都是用来复制一个文件的描述符。它们经常用来重定向进程的stdin、stdout和stderr。这两个函数的原形如下:

int dup( int oldfd );
int dup2( int oldfd, int targetfd );

  利用函数dup,我们可以复制一个描述符。传给该函数一个既有的描述符,它就会返回一个新的描述符,这个新的描述符是传给它的描述符的拷贝。这意味着,这两个描述符共享同一个数据结构。例如,如果我们对一个文件描述符执行lseek操作,得到的第一个文件的位置和第二个是一样的。
十三:函数sync、fsync和fdatasync
  一般来说磁盘IO写数据都是通过缓冲区来写,而不是立即去写。sync函数会将修改过的你缓冲区写入到磁盘,但是并不等待写磁盘操作结束就返回;fsync支队给定的fd起作用并且会等到写入磁盘操作返回;fdatasync函数类似于fsync,但是它仅影响文件的数据部分,对数据之外的文件属性并不起作用。
十四:函数fcntl和ioctl
  fcntl是用来修改已经打开文件的属性的函数,包含5个功能:
  复制一个已有文件描述符,功能和dup和dup2相同,对应的cmd:F_DUPFD、F_DUPFD_CLOEXEC。
  当使用这两个cmd时,需要传入第三个参数,fcntl返回复制后的文件描述符,此返回值是之前未被占用的描述符,并且必须一个大于等于第三个参数值。F_DUPFD命令要求返回的文件描述符会清除对应的FD_CLOEXEC标志;F_DUPFD_CLOEXEC要求设置新描述符的FD_CLOEXEC标志。
  获取、设置文件描述符标志,对应的cmd:F_GETFD、F_SETFD。
  用于设置FD_CLOEXEC标志,此标志的含义是:当进程执行exec系统调用后此文件描述符会被自动关闭。
  获取、设置文件访问状态标志,对应的cmd:F_GETFL、F_SETFL。
  获取当前打开文件的访问标志,设置对应的访问标志,一般常用来设置做非阻塞读写操作。
  获取、设置记录锁功能,对应的cmd:F_GETLK、F_SETLK、F_SETLKW。
  作为记录锁功能使用。
  获取、设置异步I/O所有权,对应的cmd:F_GETOWN、F_SETOWN。
  获取和设置用来接收SIGIO/SIGURG信号的进程id或者进程组id。返回对应的进程id或者进程组id取负值。
  ioctl函数放到第十八章和第十九章介绍
十五:/dev/fd
  对于/dev/fd/n操作,等同于复制文件描述符n的操作。


推荐阅读
  • linux网络子系统分析(二)—— 协议栈分层框架的建立
    目录一、综述二、INET的初始化2.1INET接口注册2.2抽象实体的建立2.3代码细节分析2.3.1socket参数三、其他协议3.1PF_PACKET3.2P ... [详细]
  • 问题描述现在,不管开发一个多大的系统(至少我现在的部门是这样的),都会带一个日志功能;在实际开发过程中 ... [详细]
  • 深入解析 C++ 中的 String 和 Vector
    本文详细介绍了 C++ 编程语言中 String 和 Vector 的使用方法及特性,旨在帮助开发者更好地理解和应用这两个重要的容器。 ... [详细]
  • 想把一组chara[4096]的数组拷贝到shortb[6][256]中,尝试过用循环移位的方式,还用中间变量shortc[2048]的方式。得出的结论:1.移位方式效率最低2. ... [详细]
  • 深入解析C语言中的关键字及其分类
    本文将全面介绍C语言中的关键字,并按照功能将其分为数据类型关键字、控制结构关键字、存储类别关键字和其他关键字四大类,旨在帮助读者更好地理解和运用这些基本元素。C语言中共有32个关键字。 ... [详细]
  • 本文详细探讨了在Java中如何将图像对象转换为文件和字节数组(Byte[])的技术。虽然网络上存在大量相关资料,但实际操作时仍需注意细节。本文通过使用JMSL 4.0库中的图表对象作为示例,提供了一种实用的方法。 ... [详细]
  • 函子(Functor)是函数式编程中的一个重要概念,它不仅是一个特殊的容器,还提供了一种优雅的方式来处理值和函数。本文将详细介绍函子的基本概念及其在函数式编程中的应用,包括如何通过函子控制副作用、处理异常以及进行异步操作。 ... [详细]
  • 在1995年,Simon Plouffe 发现了一种特殊的求和方法来表示某些常数。两年后,Bailey 和 Borwein 在他们的论文中发表了这一发现,这种方法被命名为 Bailey-Borwein-Plouffe (BBP) 公式。该问题要求计算圆周率 π 的第 n 个十六进制数字。 ... [详细]
  • 对于初学者而言,搭建一个高效稳定的 Python 开发环境是入门的关键一步。本文将详细介绍如何利用 Anaconda 和 Jupyter Notebook 来构建一个既易于管理又功能强大的开发环境。 ... [详细]
  • 本文介绍了如何通过C#语言调用动态链接库(DLL)中的函数来实现IC卡的基本操作,包括初始化设备、设置密码模式、获取设备状态等,并详细展示了将TextBox中的数据写入IC卡的具体实现方法。 ... [详细]
  • 高级缩放示例.就像谷歌地图一样.它仅缩放图块,但不缩放整个图像.因此,缩放的瓷砖占据了恒定的记忆,并且不会为大型缩放图像调整大小的图像.对于简化的缩放示例lookhere.在Win ... [详细]
  • 服务器虚拟化存储设计,完美规划储存与资源,部署高性能虚拟化桌面
    规划部署虚拟桌面环境前,必须先估算目前所使用实体桌面环境的工作负载与IOPS性能,并慎选储存设备。唯有谨慎估算贴近实际的IOPS性能,才能 ... [详细]
  • 本文探讨了如何在PHP与MySQL环境中实现高效的分页查询,包括基本的分页实现、性能优化技巧以及高级的分页策略。 ... [详细]
  • 处理Android EditText中数字输入与parseInt方法
    本文探讨了如何在Android应用中从EditText组件安全地获取并解析用户输入的数字,特别是用于设置端口号的情况。通过示例代码和异常处理策略,展示了有效的方法来避免因非法输入导致的应用崩溃。 ... [详细]
  • 二维码的实现与应用
    本文介绍了二维码的基本概念、分类及其优缺点,并详细描述了如何使用Java编程语言结合第三方库(如ZXing和qrcode.jar)来实现二维码的生成与解析。 ... [详细]
author-avatar
qwer
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有