热门标签 | 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的操作。


推荐阅读
  • C++字符字符串处理及字符集编码方案
    本文介绍了C++中字符字符串处理的问题,并详细解释了字符集编码方案,包括UNICODE、Windows apps采用的UTF-16编码、ASCII、SBCS和DBCS编码方案。同时说明了ANSI C标准和Windows中的字符/字符串数据类型实现。文章还提到了在编译时需要定义UNICODE宏以支持unicode编码,否则将使用windows code page编译。最后,给出了相关的头文件和数据类型定义。 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • linux进阶50——无锁CAS
    1.概念比较并交换(compareandswap,CAS),是原⼦操作的⼀种,可⽤于在多线程编程中实现不被打断的数据交换操作࿰ ... [详细]
  • Metasploit攻击渗透实践
    本文介绍了Metasploit攻击渗透实践的内容和要求,包括主动攻击、针对浏览器和客户端的攻击,以及成功应用辅助模块的实践过程。其中涉及使用Hydra在不知道密码的情况下攻击metsploit2靶机获取密码,以及攻击浏览器中的tomcat服务的具体步骤。同时还讲解了爆破密码的方法和设置攻击目标主机的相关参数。 ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • 本文讨论了在数据库打开和关闭状态下,重新命名或移动数据文件和日志文件的情况。针对性能和维护原因,需要将数据库文件移动到不同的磁盘上或重新分配到新的磁盘上的情况,以及在操作系统级别移动或重命名数据文件但未在数据库层进行重命名导致报错的情况。通过三个方面进行讨论。 ... [详细]
  • Linux环境变量函数getenv、putenv、setenv和unsetenv详解
    本文详细解释了Linux中的环境变量函数getenv、putenv、setenv和unsetenv的用法和功能。通过使用这些函数,可以获取、设置和删除环境变量的值。同时给出了相应的函数原型、参数说明和返回值。通过示例代码演示了如何使用getenv函数获取环境变量的值,并打印出来。 ... [详细]
  • Android源码深入理解JNI技术的概述和应用
    本文介绍了Android源码中的JNI技术,包括概述和应用。JNI是Java Native Interface的缩写,是一种技术,可以实现Java程序调用Native语言写的函数,以及Native程序调用Java层的函数。在Android平台上,JNI充当了连接Java世界和Native世界的桥梁。本文通过分析Android源码中的相关文件和位置,深入探讨了JNI技术在Android开发中的重要性和应用场景。 ... [详细]
  • 在Oracle11g以前版本中的的DataGuard物理备用数据库,可以以只读的方式打开数据库,但此时MediaRecovery利用日志进行数据同步的过 ... [详细]
  • 深入解析Linux下的I/O多路转接epoll技术
    本文深入解析了Linux下的I/O多路转接epoll技术,介绍了select和poll函数的问题,以及epoll函数的设计和优点。同时讲解了epoll函数的使用方法,包括epoll_create和epoll_ctl两个系统调用。 ... [详细]
  • Python中sys模块的功能及用法详解
    本文详细介绍了Python中sys模块的功能及用法,包括对解释器参数和功能的访问、命令行参数列表、字节顺序指示符、编译模块名称等。同时还介绍了sys模块中的新功能和call_tracing函数的用法。推荐学习《Python教程》以深入了解。 ... [详细]
  • python3 nmap函数简介及使用方法
    本文介绍了python3 nmap函数的简介及使用方法,python-nmap是一个使用nmap进行端口扫描的python库,它可以生成nmap扫描报告,并帮助系统管理员进行自动化扫描任务和生成报告。同时,它也支持nmap脚本输出。文章详细介绍了python-nmap的几个py文件的功能和用途,包括__init__.py、nmap.py和test.py。__init__.py主要导入基本信息,nmap.py用于调用nmap的功能进行扫描,test.py用于测试是否可以利用nmap的扫描功能。 ... [详细]
  • C#多线程解决界面卡死问题的完美解决方案
    当界面需要在程序运行中不断更新数据时,使用多线程可以解决界面卡死的问题。一个主线程创建界面,使用一个子线程执行程序并更新主界面,可以避免卡死现象。本文分享了一个例子,供大家参考。 ... [详细]
  • 使用C++编写程序实现增加或删除桌面的右键列表项
    本文介绍了使用C++编写程序实现增加或删除桌面的右键列表项的方法。首先通过操作注册表来实现增加或删除右键列表项的目的,然后使用管理注册表的函数来编写程序。文章详细介绍了使用的五种函数:RegCreateKey、RegSetValueEx、RegOpenKeyEx、RegDeleteKey和RegCloseKey,并给出了增加一项的函数写法。通过本文的方法,可以方便地自定义桌面的右键列表项。 ... [详细]
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社区 版权所有