标签:des style blog http color io os 使用 strong
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支持在不同进程间共享打开的文件。内核使用三种数据结构表示打开的文件。
如图:
#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了。
传统的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);
标签:des style blog http color io os 使用 strong
原文地址:http://www.cnblogs.com/beatrice7/p/4041722.html