一、文件共享
内核使用三种数据结构表示打开的文件,它们之间的关系决定了在文件共享方面一个进程对另一个进程可能产生的影响。
1、每个进程在进程表中都有一个记录项,记录项中包含有一张打开文件描述表
2、内核为所有打开文件维持一张文件表
3、每个打开文件都有一个v节点结构,v节点包含了文件类型和对此文件进行各种操作的函数的指针。PS:Linux中没有使用v节点,而是使用了通用i节点结构。
文件描述符标志只用于一个进程的一个描述符
文件状态标志适用于指向该给定文件表项的任何进程中的所有描述符
二、原子操作
1、pread函数和pwrite函数
#include <unistd.h>
ssize_t pread(int fd, //要读取数据的文件描述符
void *buf, //数据缓存区指针,存放读取出来的数据
size_t nbytes, //读取数据的字节数
off_t offset //读取的起始地址的偏移量,读取地址=文件开始+offset
); //返回值:读到的字节数,若已到文件结尾则返回0,若出错则返回-1
ssize_t pwrite(int fd, //要写入数据的文件描述符
const void *buf, //数据缓存区指针,存放要写入文件中的数据
size_t nbytes, //写入数据的字节数
off_t offset //写入起始地址的偏移量,写入地址=文件开始+offset
); //返回值:若成功则返回已写的字节数,若出错则返回-1
调用pread相当于顺序调用lseek和read,但是调用pread时,无法中断其定位和读操作,且不更新文件指针。
调用pwrite相当于顺序调用lseek和write。
2、原子操作指的是由多步组成的操作,如果该操作原子地执行,则要么执行完所有步骤。要么一步也不执行,不可能只执行所有步骤的一个子集。
三、dup和dup2函数
#include <unistd.h>
int dup(int fd); //返回的新文件描述符一定是当前可用文件描述符的最小数值
int dup2(int fd,int fd2); //用fd2参数指定新描述符的数值
两个函数都可用来复制一个现存的文件描述符,两函数的返回值:若成功则返回新的文件描述符,若出错则返回-1。
这两个函数的用处:返回的新文件描述符与参数fd共享一个文件表项,达到共享一个文件状态标志(读、写、添加等)以及同一当前文件偏移量。如下图所示:
四、sync、fsync和fdatasync函数
虽然延迟写减少了磁盘读写次数,但是却降低了文件内容的更新速度,使得欲写到文件中的数据在一段时间内并没有写到磁盘上。当系统发生故障时,这种延迟可能造成文件更新内容的丢失。sync、fsync和fdatasync函数可以保证磁盘上实际文件系统与缓冲区高速缓存中内存的一致性。
#include <unistd.h>
int fsync(int fd);
int fdatasync(int fd); //这两个函数的返回值:若成功则返回0,若出错则返回-1
void sync(void);
fsync :只对由文件描述符fd指定的单一文件起作用,并且等待写磁盘操作结束,然后返回
fdatasync :只影响文件的数据部分,而除数据外,fsync还会同步更新文件的属性
sync :只是将所有修改过的块缓冲区排入写队列,然后就返回,它并不等待实际写盘操作结束
五、fcntl函数
#include<fcntl.h>
int fcntl(int fd,int cmd,int arg); //返回值:若成功则依赖于cmd,若出错则返回-1
fcntl函数有5种功能:1、复制一个现有的描述符 cmd=F_DUPFD
2、获得/设置文件描述符标记 cmd=F_GETFD或F_SETFD
3、获得/设置文件状态标志 cmd=F_GETFL或F_SETFL
4、获得/设置异步I/O所有权 cmd=F_GETOWN或F_SETOWN
5、获得/设置记录锁 cmd=F_GETLK、F_SETLK或F_SETLKW