五个文件io函数,open,read,write,lseek,close;
不带缓冲的io是指每个read,write函数都调用内核中的一个系统调用。它在多进程间共享资源,原子操作就变得很重要。
 
文件描述符:
对于内核而言,所有打开的文件都是通过文件描述符引用。他是一个费负整数,当打开或者创建一个文件,内核向进程返回一个文件描述符。
当读或者写一个文件时,使用open或creat返回的文件描述符标识该文件,将其作为参数传给read或write。
0----标准输入  STDIN_FILENO
1-----标准输出 STDOUT_FILENO
2-----标准错误输出 STDERR_FILENO (定义在unistd.h中)
 
文件描述符限制在0-OPEN_MAX间
 
open函数
#include<fcntl.h>
int open(const char *pathname, int flag, /* mod_t mode */)
成功返回文件描述符,失败返回-1
flag有以下几种,可以用或运算构成flag参数
O_RDONLY 只读 0 
O_WRONLY  只写 1 
O_RDWR  读写 2
 
这三个常量中必须指定一个且只能指定一个,则下列常量是可选的。
O_APPEND 每次写追加到末尾
O_CREAT 若此文件不存在则创建它,使用此选项时需要指定第三个参数设置文件的访问权限
 
   
O_EXCL 如果同时指定了O_CREAT,而文件存在,则会报错。可用来测试一个文件是否存在,如果不存在则创建之,使测试和创建成为一个原子操作。
 
    
O_TRUNC 如果此文件存在,而且为只写或读写成功打开,则将长度截短为0
 
    
O_NOCTTY 如果pathname指的是终端设备,则不将此设备分配作为此进程的控制终端。
 
    
O_NOBLOCK 如果pahtname指的是一个FIFO,一个特殊块,或一个字符特殊文件,则此选项为文件的本次打开操作和后续的io操作设置非阻塞模式.
 
    

      下面这三个操作是可选的 
    
 
    

      O_DSYNC 使每次write等待物理io操作完成,但是如果写操作并不影响读取到刚写入的数据,则不等待文件属性被更新。 
    
 
    

      O_RSYNC 使每一个以文件描述符作为参数的read操作等待,直到任何对文件同一部分进行的未决写操作都完成 
    
 
    

      O_SYNC 使每次写都等到物理io完成,包括有write操作引起的文件属性更新所需要的io。 
    
 
    

      creat函数 
    
 
    

      #include<fcntl.h> 
    
 
    

      int creat(const char *pathname, mod_t mod) 
    
 
    

      若成功则返回只写打开的文件描述符,否则返回-1 
    
 
    

      等价于 
    
 
    

      open(pathname, O_WRONLY | O_CREAT | O_TRUNC, mod) 
    
 
    

      由于unix早期版本中,open函数第二个参数只能是0,1或2,没有办法打开一个不存在的文件,因此需要creat创建一个新文件,现在open提供了O_CREAT, O_TRUNC,于是也不需要creat函数 
    
 
    
close 函数
 
    
关闭一个打开的文件
 
    
#include <unistd.h>
 
    
int  close(int filedes)
 
    

      成功返回0,失败返回-1 
    
 
   

     lseek函数 
   
 
   

     没打开一个文件都会有一个与其相关联的“当前文件偏移量”,通常是一个非负整数,读写都是从当前文件偏移量开始,并使偏移量增加所读写的字节数。打开一个文件如果没有指定O_APPEND,则默认偏移量为0,这也是为什么写会覆盖原先的数据。 
   
 
   

     #include<unistd.h> 
   
 
   

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

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

     对offset的解释与参数whence有关 
   
 
   

     若whence是SEEK_SET,则将偏移量设置为据文件开始处offset字节。 
   
 
   

     若whence是SEEK_CUR,  则将偏移量设置为据当前值加上offset,offset可为正或负。 
   
 
   

     若whence是SEEK_END, 则将文件偏移量设置为文件长度加offset,offset可正可负。 
   
 
  

    若lseek成功则放回新的文件偏移量,失败返回-1 
  
 
  

    可用lseek(filedes, 0, SEEK_CUR)来测试文件是否可以设置偏移量。如果文件描述符引用的是FIFO,网络套接字,管道,则lseek返回-1,并将errno设置为ESPIPE 
  
 
  

    lseek便宜了记录在内核中,并不引起io操作,然后该偏移量用于下一个读或写操作。 
  
 
  

    偏移量大于当前文件长度将会引起文件空洞,文件空洞并不需要占用存储空间。 
  
 
  

    read函数 
  
 
  

    #include<unistd.h> 
  
 
  

    ssize_t read(int filedes, void *buf, size_t nbytes); 
  
 
  

    若成功则返回读到的字节数,读到文件尾则返回0,错误返回 -1 
  
 
  

    write函数 
  
 
  

    #include <unistd.h> 
  
 
  

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

    返回值与nbytes相同,否则表示出错。通常是磁盘已满,或者是超过一个给定进程的文件长度。 
  
 
  

    I/O效率 
  
 
 
当bufsize为1024时io效率最好,可由试验得到。
 
文件共享
unix支持在不同的进程间共享打开的文件。
内核使用三种数据结构表示打开的文件。
1。每个进程在进程表中都有一个记录项。
2。内核为打开的所有文件维护一张文件表。
3。每个打开文件或设备都有一个v节点结构。
 
原子性操作
pread,pwrite
 
dup和dup2函数
用来复制一个现存的文件描述符
dup(filedes);等效于 fcntl(filedes, F_DUPFD, 0);
而调用dup2(filedes, filedes2)等效于close(filedes2); fcntl(filedes, FDUPFD, filedes2);
后一种情况并不完全等于close加上fcntl操作,因为dup2是原子操作
 
sync,fsync,fdatasync函数
传统unix实现在内核中并没有高速缓冲去,高速缓存,或页面高速缓存,大多数io都是通过缓冲进行。
#include<unistd.h>
int fsync(int filedes);
int fdatasync(int filedes);
成功返回0, 错误返回-1
void sync(void);
sync 只将所有修改过的块缓冲区排入写入队列,然后就返回,并不等待实际写磁盘操作结束。
通常称为update的系统守护程序会周期性的调用sync函数。这就定期的冲洗内核的块缓冲区。
 
fsync函数只对由文件描述符filedes指定的单一文件起作用,并且等待写磁盘操作结束。可用于数据库类的程序,可以保证修改过的块立即写入到磁盘上。
fdatasync类似于fsync,但它只影响文件的数据部分。而数据外,fsync还会更新文件的属性。
 
fcntl函数
改变已经打开文件的性质。
#include <fcntl.h>
int fcntl(int filedes, int cmd, .../* int arg */);
返回值:若成功则依赖于cmd,出错返回-1
fcntl函数有五种功能:
1。复制一个现有的描述符 cmd=F_DUPFD
2。获得/设置文件描述符标记 cmd=F_GETFD/F_SETFD
3。获得设置文件状态标志 cmd=F_GETFL/F_SETFL
4。获得设置异步io所有权 cmd=F_GETOWN/F_SETOWN
5。获得/设置记录锁 cmd=F_GETLK, F_SETLK或者F_SETLKW
 
ioctl函数
ioctl函数是io操作的杂物箱,不能用本章中的其他函数表示的io函数都能用ioctl表示。
终端io是ioctl的最大 使用方面
#include <unistd.h>
#include <sys/ioctl.h>
#include <stropts.h>
int ioctl(int filedes,  int request, ...);
出错返回-1,成功返回其他值。
 
/dev/fd
较新的系统度提供了/dev/fd目录,其目录项是名为0,1,2等文件,打开/dev/fd/n等效于复制描述符n,(假定描述符n是打开的);