Unix_文件I/O

lseek函数

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

whence:  SEEK_SET, 表示将文件的偏移量设置为距文件开始offset个字节。

     SEEK_CUR, 当前+offset个字节,offset可正可负。

     SEEK_END, 文件长度+offset个字节,可正可负。

成功,返回新的文件偏移量;失败,返回-1。

lseek仅将当前的文件偏移量记录在内核中,并不引起任何I/O操作。然后,该偏移量用于下一个读写操作。

空洞

offset可以大于文件的当前长度,这种情况下,对该文件的下一次写将加长该文件,并在文件中构成一个空洞,这是允许的。位于文件中,但是没有写过的字节都被读为0。

空洞并不占用磁盘空间。具体处理方式与文件的实现有关,当定位到超出文件尾端后写时,新写的数据需要分配磁盘块,但对于原文件尾端和新开始写的位置之间的部分,则不需要分配空间。

有空洞文件与无空洞文件的长度虽然可以相同,but,它们实际占用的磁盘块数是不同的。

文件共享

UNIX支持在不同进程间共享打开的文件。内核使用三种数据结构表示打开的文件。

  1. 进程表项

    • 文件描述符标志
    • 指向文件表项目的指针
  2. 文件表项
    • 文件状态标志(such as read, write, append, sync, and nonblocking)
    • 当前文件偏移量
    • 指向该文件V节点的指针
  3. V-node表项
    • v节点信息(包含文件类型,对此文件进程各种操作的指针...)
    • i节点信息(索引节点),(包含文件的所有者,文件长度,文件所在设备...)
    • 当前文件长度

如图:

  • 每一个进程都对应一个进程表项,一个进程能打开好多文件,所以进程表项里有好多fd;
  • 一个fd就代表一个文件,也可以说一个文件可以由很多个fd来代表;
  • 不同进程会对同一个文件有不同的操作,这个文件表项其实就是把文件和进程联系起来的东东,里面包含一些该进程对这个文件的操作,然后它再用一个指针指向文件本身。

dup、dup2函数

#include <unistd.h>
int dup(int filedes);
int dup2(int filedes, int filedes2);

dup和dup2都是用来复制一个现存的文件描述符(已经打开的文件描述符)的函数。成功,返回新的描述符;失败,返回-1.

dup:返回当前可用fd的最小值;

dup2:可以指定返回的描述符为fd2。如果fd2已经打开,则先将其关闭。如果fd等于fd2, 则直接返回fd2,即将fd替换为fd2, 而不关闭它。

这两个函数返回的新文件描述符与原来的fd共享同一个文件表项。如上图中,fd0与fd4。

/dev/fd

比较新的系统都提供名为/dev/fd的目录,目录项是名为0,1,2等的文件。打开/dev/fd/n 等效于复制描述符n。

某些系统提供路径名/dev/stdin, /dev/stdout, /dev/stderr。等效于/dev/fd/0, /dev/fd/1,/dev/fd/2。

原子操作

所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)

如果一个进程要在一个文件的尾端添加数据,由于早期UNIX不支持open的O_APPEND,所以采用先lseek到文件末尾,再write的方法。但若有多个进程,这种方法就会出现问题

if (lseek(fd, OL, 2) < 0)  //position to EOF
    err_sys("lseek error");
if (write(fd, buf, 100) != 100)
    err_sys("write error");

  假设有两个独立的进程A和B都对同一个文件进行添加操作。每个进程都已经打开了该文件,但未使用O_APPEND标志。这时A,B都有自己的文件表项,但是共享一个v-node表项。如果A调用了lseek,它将进程A的该文件当前偏移量设置为1500字节(当前文件末尾)。然后内核切换到B,B再执行lseek,将进程B的当前偏移量也设置为1500字节(文件末尾)。然后B调用write,将B的该文件当前偏移量增加到1600。因为文件的长度增加了,所以内核对V节点中的当前文件长度更新为1600。然后,切换回A,A再write时,由于其当前文件表中记录的偏移量是1500,所以将数据写入时就覆盖了B刚写到这个文件中的数据。

解决方法:  使lseek和write这两个操作对于其他进程而言成为一个原子操作。UNIX提供了一种方法,在打开文件时设置O_APPEND标志,使内核每次对这个文件进行写之前,都将进程的当前偏移量更新到文件末尾,于是在每次写之前就不用调用lseek了。

sync、fsync、fdatasync

传统的UNIX系统实现在内核中设有缓冲区高速缓存或者页面高速缓存,大多数磁盘I/O都通过缓冲进行。当我们向一个文件写入数据时,数据通常被内核拷贝到它的其中一个缓冲区,并排队以便在之后的某个时刻写入到磁盘中。这被称为延迟写(delayed write)。这方法虽然减少了磁盘读写次数,BUT降低了文件内容的更新速度,若系统发生故障,将会造成文件更新内容的丢失。

So,为了保证磁盘上实际文件系统与缓冲区高速缓存中的内容一致,UNIX提供了sync、fsync和fdatasync函数来强制把缓冲里的东东写到磁盘文件里。

#include <unistd.h>
int fsync(int filedes);
int fdatasync(int filedes);
//成功,返回0;出错,返回-1
void sync(void);

sync: 全局性的,对整个系统都flush。不管你实际写磁盘结束否,有修改就排入写队列,也就是只要有点儿变动就冲洗内核的块缓冲区。

fsync: 仅引用一个文件,由文件描述符指定,并且在返回前等待磁盘写的完成。

fdatasync: 顾名思义,就是只更新data部分,因为fsync还会同步更新文件属性等。

以上三种方法是系统提供的系统调用,C语言里有一个对C标准扩充的函数fflush,它也有相似的功能。

#include <stdio.h>
int fflush(FILE* stream);
时间: 2024-08-27 02:20:13

Unix_文件I/O的相关文章

Unix_文件系统介绍_1

unix文件类型: 普通文件:   大部分文件 目录文件:   其他文件的信息,特殊格式的记录 特殊文件:   设备文件 ---------------------------------------------------------------- 目录: root --> bin, lib, usr, dev-->...   最高为根目录root /   根目录 /usr    包含用户的主目录.该目录包含许多其他面向用户的目录: /usr/docs     各类文档: /usr/man

Unix_文件系统介绍_2

目录命令: 1 显示目录路径名:   pwd   (print working directory) login:cnyxj [return]...................................输入登录名(cnyxj) password.................................................. 输入口令 Welcome to UNIX! $pwd [return] ....................................

Unix_文件系统介绍_3

还是unix命令学习: 显示文件命令:cat 已经在之二中的回复中 相关打印命令:因为自己较少接触,详情略过不记,今后如用到再学. 打印:lp 取消打印:cancel 获取打印机状态:lpstat 删除文件:rm 用rm(remove)命令删除文件,如果文件在工作目录下,就给出文件名:如果不在工作目录下,则指定想要删除文件的路径名. 另外,此命令不单能删除文件,通过选项还可以删除目录. rm [选项] [Linux对应的选项] -i --interactive 删除文件前,给出确认信息 -r -

UNIX_文件&amp;目录

除了对文件进行打开,读写等操作.文件系统还有其他的特征和性质等着我们去研究哦. stat.fstat.lstat函数 #include <sys/stat.h> int stat(const char *restrice pathname, struct stat *restrict buf); int fstat(int fieldes, struct stat *buf); int lstat(const char *restrice pathname, struct stat *rest

最佳vim技巧

最佳vim技巧----------------------------------------# 信息来源----------------------------------------www.vim.org         : 官方站点comp.editors        : 新闻组http://www.newriders.com/books/opl/ebooks/0735710015.html : Vim书籍http://vimdoc.sourceforge.net/cgi-bin/vim

Mac下获取AppStore安装包文件路径

本文介绍了Mac下如何找到AppStore下载的安装包路径,以及如何提取出来供以后使用的相关步骤,希望对大家有所帮助. 通过远在大洋彼岸的苹果服务器下载东西,确实有够慢啊!AppStore更甚:甚至都经常提示连不上服务器,而有些软件呢,还必须从AppStore下载安装,所以没办法,谁让上了苹果的贼船呢!公司的网速更是不敢恭维,以至于基本上不下东西,除非像这次一样:手贱的把iPhone6升级到8.2.2了,然后Xcode6.1.1真机调试不成了,所以需要下个Xcode6.2.昨天刚更新的Xcode

微信文件传输助手文件夹在哪?一起来找找

微信文件传输助手是微信电脑版与手机微信之间相互传输图片等文件的好工具,但很多童鞋都找不到微信文件传输助手文件夹在哪,就让我们一起找找吧 1.先说说手机微信文件传输助手文件夹在哪吧 文件夹路径为/Tencent/MicroMsg/Download/ 2.电脑版微信文件传输助手文件夹在:/微信安装保存目录/wechat files/微信号/ 也可以点击接收到的图片下载保存到相应位置即可

GitHub限制上传大于100M的单个大文件

工作中遇到这个问题,一些美术资源..unitypackage文件大于100M,Push到GitHub时被拒绝.意思是Push到GitHub的每个文件的大小都要求小于100M. 搜了一下,很多解决办法只是把这些超过100M的大文件从本地版本库中移除,使得Push可以成功.但这并没有解决如何上传大文件到GitHub的问题. 解决办法是使用Git LFS. 用法参考:http://blog.csdn.net/tyro_java/article/details/53440666 按照以上方法设置好后,就

Linux 将文件夹下的所有文件复制到另一个文件里

如何将文件夹/home/work下的文件复制到/home/temp里面? 使用命令: cp -R /home/work/* /home/temp *表示所有文件 但是/home/work 下的隐藏文件都不会被拷贝 更好的复制的方法是用"."代替"*"就好了. cp -R /home/work/.  /home/temp 将一个文件夹复制到另一个文件夹下,例如将/home下的work文件夹复制到temp下面 命令为: cp -R /home/work  /home/t