Linux文件I.O

102人浏览   2024-05-29 10:11:42

0.知识点

文件描述符

不带缓冲的IO、带缓冲的IO

文件IO函数:open、read、write、lseek、close

打开文件的内核数据结构

原子操作

其他IO操作函数:dup、fcntl、sync/fsync/fdatasync、ioctl函数

1. 文件描述符

对内核而言,所有打开的文件都通过文件描述符引用。当打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符;当读写一个文件时,使用open或create返回的文件描述符标识该文件,将其作为参数传递给read或write。

3个特殊的文件描述符(用于shell):

  • 0(STDIN_FILENO) 与进程的标准输入关联
  • 1(STDOUT_FILENO 与进程的标准输出关联
  • 2(STDERR_FILENO) 与进程的标准错误关联

2.不带缓冲的IO、带缓存的IO

不带缓冲的IO是指每个read、write都调用内核中的一个系统调用,以文件描述符来进行读写。

带缓冲的IO是指按缓冲区来控制是否发起read、write调用,例如可以是缓冲区满了才发起系统调用,以来进行读写。

重要说明:流的本质是封装了文件描述符和缓存区的File对象指针。

3.文件IO函数

open

作用:打开或创建一个新文件(成功返回文件描述符,出错返回-1)

int open(const char* path, in oflag, .../* mode_t mode */)
int open_at(int fd, const char* path, int oflag, ... /* mode_t */)

open_at函数是用来支持以相对路径来打开文件的。

create

也可用来创建一个新文件 (成功返回文件描述符,出错返回-1)

int create(const char* path, mode_t mode)

close

作用:关闭一个打开的文件

int close(int fd);

重要说明:

  • 关闭一个文件时还会释放该进程加在文件上的所有记录锁。
  • 当一个进程自动终止时,内核自动关闭它所有的打开文件,很多程序利用了这一点而不显示的用close关闭打开文件。

lseek

作用:每个打开文件都有一个与其相关联的当前文件偏移量,可以调用lseek显示的为一个打开文件设置偏移量。

off_t lseek(int fd, off_t offset, int whence); //成功返回新的文件偏移量,失败返回-1

重要说明:

  • 管道、FIFO、网络套接字不支持lseek,即如果文件描述符指向一个管道、FIFO或网络套接字,则lseek返回-1。

read:从打开文件中读数据

ssize_t read(int fd, void* buf, size_t nbytes); //返回值:读到的字节数,若已读到文件尾返回0,若出错返回-1

重要说明:

有多种情况可使实际读到字节数少于要求读的字节数:

  • 读普通文件,在读到要求的字节数之前已经到达文件尾;
  • 从终端设备读,通常一次最多读一行
  • 从网络读时,网络中的缓冲机制可能造成返回数据小于所要读的字节数。
  • 从管道或FIFO读时,如果管道中包含的字节数小于要读的字节数,只能返回实际可用字节数。
  • 从某些面向记录(如磁带)的设备读时,一次最多返回一个记录
  • 当这一信号造成中断,而已经读了部分数据量时

write

向打开文件写数据

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

重要说明:

  • 其返回值通常与nbytes相同,否则表示出错了。write出错的常见原因:1.磁盘写满或磁盘损坏写不进去;2.超过了一个给定进程的文件长度限制。

4.打开文件的内核数据结构

两个进程打开同一个文件

5.原子操作

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

对于多个进程读同一文件时,因为每个进程都有它自己的文件表项,其中也有它自己的当前文件偏移量,所以对与多进程读同一文件每个进程都能正确的读到数据。

但是,当多个进程写同一文件时,则可能产生预想不到的结果。

(1)追加到一个文件

如果是"先定位到文件尾端,然后写",使用了两个分开的函数,就会造成进程B将进程A刚写的数据覆盖掉。解决问题的方法是使这两个操作成为对于其他进程而已成为一个原子操作。任何要求多于一个函数调用的操作都不是原子操作,因为在两个系统调用之间,内核有可能会临时挂起进程。

linux提供的原子操作解决方法是在打开文件时设置O_APPEND标志。这样使得内核在每次写操作之前,都将进程的当前偏移量设置到该文件的尾端处,于是在每次写之前就不再需要调用lseek。

(2)pread、pwrite

pread是lseek和read的原子封装

pwrite是lseek和write的原子封装

(3)创建文件

open函数提供了O_CREATE和O_EXCL选项来保证该文件已经存在时,open将失败。

6.其他函数

dup、dup2:复制文件描述符,复制后两个文件描述符指向同一个文件表项,所以他们共享同一文件状态标志(读、写、追加等)以及同一当前文件偏移量。

fcntl:用来改变已经打开文件的属性。有5个功能:

  • 复制文件描述符
  • 获取/设置文件描述符标志
  • 获取/设置文件状态标志
  • 获取/设置异步IO所有权
  • 获取/设置记录锁

sync、fsync、fdatasync:负责将页高速缓存(PageCache)中的数据刷到磁盘。当我们向文件写入数据时,内核通常先将数据复制到缓冲区中,然后排入队列,晚些时候再写入磁盘,这种方式称为延迟写。

通常当内核需要重用缓冲区来存放其他磁盘块数据时,它会把所有延迟写数据块写入磁盘。为了保证磁盘上实际文件系统与缓冲区中内容一致性,提供了sync、fsync和fdatasync来三个函数同用户使用。

iocntl:IO操作的杂物箱。

相关推荐