linux系统编程-day06-文件IO(1)

2018-05-30  本文已影响0人  桔子满地

内核为每个进程维护一个打开文件的列表,该表被称为文件表(file table)。该表由一些叫做文件描述符(file descriptors)(常缩写为fds)的非负整数进行索引。
用户空间和内核空间都把文件描述符作为每个进程的唯一cookies。

打开文件

最基本的访问文件的方法是read( )和write( )系统调用,在一个文件能被访问之前,必须通过open( )或者creat( )系统调用打开它,一旦使用完毕,就应该用close( )系统调用来关闭文件。

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int open(const char *name, int flags);
int open(const char *name, int flags, mode_t mode);

open( )系统调用将路径名name给出的文件与一个成功返回的文件描述符想关联,文件位置指针被设定为零,而文件则根据flags给出的标志位打开。

  1. O_APPEND: 文件以追加模式打开。在每次写操作之前,文件位置指针将被置于文件末尾。
  2. O_ASYNC: 当指定文件可写或者可读时产生一个信号(默认是SIGIO),这个标志仅用于终端和套接字,不能用于普通文件。
  3. O_CREAT: 当name指定的文件不存在时,将由内核来创建。
  4. O_DIRECT: 用于直接IO。
  5. O_DIRECTORY: 如果name不是一个目录,open( )调用将会失败。这个标志用于在opendir( )内部使用。
  6. O_EXCL: 和O_CREAT一起给出的时候,如果由name给定的文件已经存在,则open( )调用失败。用来防止文件创建时出现竞争。
  7. O_LARGEFILE: 给定文件打开时将使用64位偏移量,这样大于2G的文件也能被打开。
  8. O_NOCTTY: 如果name指向一个终端设备(/dev/tty),它将不会成为这个进程的控制终端,即时该进程目前没有控制终端。
  9. O_NOFOLLOW: 如果name是一个符号链接,open( )调用会失败。
  10. O_NONBLOCK: 如果可以,文件将在非阻塞模式下打开。open( )调用不会,任何其它操作都不会使该进程
  11. O_SYNC: 打开文件用于同步IO。
  12. O_TRUNC: 如果文件存在,且为普通文件,并允许写,将文件的长度截断为0。

当文件创建时,mode参数提供新建文件的权限。mode参数是常见的Unix权限位集合,像八进制数0644(所有者可以读写,其他人只能读)。例如:

int fd;
fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP | S_IROTH);
if (fd == -1)
    /* error */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int creat (const char *name, mode_t mode);

对creat的调用:

int fd;
fd = creat(file, 0644);
if (fd == -1)
    /* error */

等价于:

int fd;
fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd == -1)
   /* error */

用read( )读取文件

最基本、最常见的读取文件的机制是使用read( )系统调用:

#include <unistd.h>
ssize_t read(int fd, void *buf, size_t len);

该系统调用从fd指向的文件的当前偏移量至多读len个字节到buf中。成功时,将返回写入buf中的字节数。出错时,返回-1,并设置errno。

  1. 等于len。则与预期一致
  2. 大于0但是小于len。合法,读取的字节存入buf中。
  3. 返回0。标志着EOF,没有可以读入的数据
  4. 返回-1,errno被设置为EINTR。表示在读入字节之前收到了一个信号,可以重新进行调用。
  5. 返回-1, errno被设置为EAGAIN。这表示读取会因为没有可用的数据而阻碍,而读请求应该在之后重开。这只在非阻塞模式下发生。
  6. 返回-1, errno被设置不同于EINTR或EAGAIN的值。这表示某种更严重的错误。
ssize_t ret;
while (len != 0 && (ret = read(fd, buf, len) != 0)) {
  if (ret == -1) {
    if (errno == EINTR)
      continue;
    perror("read");
    break;
  }
  len -= ret;
  buf += ret;
}
char buf[BUFSIZE];
ssize_t nr;
start:
  nr = read(fd, buf, BUFSIZE);
  if (nr == -1) {
    if (errno == EINTR)
      goto start; 
    if (errno == EAGAIN)
      /* resubmit later */
    else
      /* error */
  }
if (len > SSIZE_MAX)
  len = SSIZE_MAX;

用write( )来写

#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);

一个write( )调用从由文件描述符fd引用文件的当前位置开始,将buf中至多count个字节写入文件。

ssize_t ret, nr;
while (len != 0 && (ret = write(fd, buf, len)) != 0) {
  if (ret == -1) {
    if (errno == EINTR)
      continue;
    perror("write");
    break;
  }
  len -= ret;
  buf += ret;
}

同步IO

由于一般都使用page cache & writeback的机制,然而又有应用想要控制数据写入磁盘的时间,于是Linux提供了同步机制,用性能来换取同步操作。

  1. fsync( )
#include <unistd.h>
int fsync(fd);

调用fsync( )可保证fd对应文件的脏数据回写到磁盘上。

  1. fdatasync( ):
#include <unistd.h>
int fdatasync(fd);

和fsync( )的区别在于,它仅仅写入数据,不保证元数据同步到磁盘上。

  1. sync( ):
#include <unistd.h>
void sync(void);

sync( )用来对磁盘上的所有缓冲区进行同步!

  1. O_SYNC标志
    O_SYNC标志在open( )中使用,使所有在文件上的I/O操作同步。例如:
int fd;
fd = open(file, O_WRONLY | O_SYNC);
if (fd == -1){
  perror("open");
  return -1;
}
  1. O_DSYNC和O_RSYNC

直接IO

由于Linux内核实现了一个复杂的缓存、缓冲以及设备和应用之间的I/O管理的层次结构。通过O_DIRECT标志使内核最小化I/O管理的影响。

关闭文件

程序完成对某个文件的操作后,可以使用close( )系统调用将文件描述符和对应的文件解除关联。

#include <unistd.h>
int close(int fd);

close( )调用解除了已打开的文件描述符的关联,并分离进程和文件的关联。

上一篇 下一篇

猜你喜欢

热点阅读