Unix环境高级编程笔记(一)文件I/O

File I/O

介绍

  • 我们通过描述文件I / O可用的函数来开始对UNIX系统的讨论——打开文件、读取文件、编写文件,等等。在UNIX系统上,大多数文件I/O只能使用5个函数来执行:open、read、write、lseekclose
  • 然后,我们将研究各种缓冲区大小对读写函数的影响
  • 本章中描述的函数通常被称为无缓冲I/O。术语unbuffered的意思是每个读或写调用内核中的一个系统调用。这些没有缓冲的I/O函数不是ISO C的一部分,而是POSIX.1和单一UNIX规范的一部分。
  • 每当我们描述多个进程之间的资源共享时,原子操作的概念变得很重要。我们通过文件I/O和open函数的参数来研究这个概念。这将导致讨论如何在多个进程之间共享文件以及涉及哪些内核数据结构。在描述这些特性之后,我们将介绍dup、fcntl、sync、fsync和ioctl函数。

文件描述符(File Descriptors)

  • 对于内核,所有打开的文件都由文件描述符引用。文件描述符是一个非负整数, 当我们打开一个现有文件或创建一个新文件时,内核将向进程返回一个文件描述符;当我们想要读取或写入一个文件时,我们使用open或creat返回的文件描述符作为读取或写入的参数来标识该文件。
  • 按照惯例,UNIX系统将:
    • 文件描述符0与标准输入(STDIN_FILENO1)相关联
    • 文件描述符1与标准输出(STDOUT_FILENO1)相关联
    • 文件描述符2与标准错误(STDERR_FILENO1)相关联

open 和 openat 函数

  • 通过调用open函数或openat函数来打开或创建文件。

    函数详细用法这里不再赘述,linux环境下大家打开终端,输入命令:
man 2 open

这里主要说明两者区别,fd参数将openat函数与open函数区分开。 有三种可能性:

  1. path参数指定绝对路径名。 在这种情况下,将忽略fd参数,并且openat函数的行为类似于open函数。
  2. path参数指定一个相对路径名,fd参数是一个文件描述符,该描述符指定要在文件系统中评估相对路径名的起始位置。 通过打开要在其中评估相对路径名的目录来获得fd参数。
  3. path参数指定相对路径名,而fd参数具有特殊值AT_FDCWD。 在这种情况下,将从当前工作目录开始评估路径名,并且openat函数的行为类似于open函数。

openat函数是添加到最新版本POSIX.1的函数之一,可以解决两个问题。 首先,它为线程提供了一种使用相对路径名打开当前工作目录以外的目录中文件的方法。 在后续章节中我们将学到,同一进程中的所有线程共享相同的当前工作目录,因此,同一进程中的多个线程很难同时在不同的目录中工作。 其次,它提供了一种避免使用时间检查(TOCTTOU)错误的方法。

TOCTTOU错误的基本思想是,如果程序进行两次基于文件的函数调用,而第二次调用取决于第一次调用的结果,则该程序容易受到攻击。 因为这两个调用不是原子调用,所以文件可以在两个调用之间切换,从而使第一个调用的结果无效,从而导致程序错误。 文件系统名称空间中的TOCTTOU错误通常通过欺骗特权程序以减少特权文件的权限或修改特权文件以打开安全漏洞来处理破坏文件系统权限的尝试。

creat 函数


同理,我们使用man命令查看具体用法。
我们可以发现,这个函数等同于:

open(path, O_WRONLY | O_CREAT | O_TRUNC, mode);

creat的一个不足之处是仅打开文件进行写入。 在提供新版本的open之前,如果我们要创建要写入然后要回读的临时文件,则必须调用creat,close然后再打开。 更好的方法是使用open函数,如

open(path, O_RDWR | O_CREAT | O_TRUNC, mode);

close 函数

在这里插入图片描述
关闭文件也会释放该进程可能在文件上拥有的所有记录锁。
这部分我们之后章节补充。

lseek 函数

每个打开的文件都有一个关联的“当前文件偏移量”,通常是一个非负整数,用于测量从文件开头算起的字节数。 读写操作通常从当前文件偏移量开始,并使偏移量增加读取或写入的字节数。 默认情况下,打开文件时,此偏移量初始化为0,除非指定了O_APPEND选项。
在这里插入图片描述
偏移量的解释取决于whence参数的值。

  • 如果whence为**SEEK_SET**,则文件的偏移量将设置为从文件开头开始的偏移字节。
  • 如果whence是**SEEK_CUR**,则文件的偏移量将设置为其当前值加上偏移量。偏移量可以为正或负。
  • 如果whence为**SEEK_END**,则文件的偏移量将设置为文件大小加上偏移量。偏移量可以为正值或负值。
  • lseek仅将当前的文件偏移量记录在内核中,它并不引起任何I/O操作。然后该偏移量用于下一个读写操作。

read 函数

在这里插入图片描述
成功时返回读取到的字节数(为零表示读到文件描述符), 此返回值受文件剩余字节数限制.当返回值小于指定的字节数时 并不意味着错误;这可能是因为当前可读取的字节数小于指定的字节数(比如已经接近文件结尾,或者正在从管道或者终端读取数据,或者 read()被信号中断). 发生错误时返回-1,并置 errno 为相应值.在这种情况下无法得知文件偏移位置是否有变化.

write 函数

在这里插入图片描述
其返回值通常与 nbytes 的值相同,否则表示出错。write出错的一个常见的原因是磁盘已写满,或者超过了一个给定进程的文件长度限制。

原子操作

原子操作(atomic operation)指的是由多步组成的一个操作。如果该操作原子地执行,则要么执行完所有的步骤,要么一步也不执行,不可能只执行所有步骤的一个子集。


  1. These constants are defined in the <unistd.h> header ↩︎ ↩︎ ↩︎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值