天天看點

linux檔案IO與記憶體映射:分散/聚集IO技術(scatter-gather)

前言

根據上文我們學習到的​​使用者空間的IO緩沖區​​,作業系統為了減少系統調用的次數,節省系統開銷,提出了使用者空間的IO緩沖區,即為使用者空間的檔案讀寫開辟一段可以利用​

​setvbuf​

​配置大小的記憶體空間來作為檔案IO緩沖區。

描述

為了在以上IO緩沖區的基礎上更進一步得減少系統調用的次數,提出了分散/聚合IO技術,總體上是使用了單個向量的IO操作代替了多個向量的IO操作。

讀文多個件的時候将從page cache中讀到的内容先讀入到一個IO緩沖區資料結構中,再由一個緩沖區分别分散傳回到多個檔案緩沖區中;寫檔案的過程與讀檔案的過程剛好相反,即将多個檔案緩沖區的内容聚合寫到一個緩沖區中,寫入page cache中,進而更高得提升系統調用效率。

linux檔案IO與記憶體映射:分散/聚集IO技術(scatter-gather)

實作

實作​

​scatter-gather​

​技術的系統調用原型如下:

  • 頭檔案​

    ​<sys/uio.h>​

  • 函數使用:

    ​ssize_t readv(int fd, const struct iovec *iov, int iovcnt);​

    ​​

    ​ssize_t writev(int fd, const struct iovec *iov, int iovcnt);​

  • 函數參數

    ​fd​

    ​ :打開的檔案描述符

    ​iov​

    ​: IO向量資料結構内容如下,每個結構體成員代表一個buffer:
struct iovec {
     void  *iov_base;    /* Starting address */
     size_t iov_len;     /* Number of bytes to transfer */
 };      

​iovcnt​

​:IO向量的個數

這兩個系統調用的底層仍然是使用read/write系統調用,隻是對IO 緩沖區做了聚合,支援多個IO緩沖區内容聚合到同一個IO向量中

代碼如下:

​​

​writev.c​

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/uio.h>
#include <fcntl.h>

int main(int argc, char *argv[])
{
  ssize_t count = 0;
  int fd, i = 0;
  char *buf[]={"buffer1\n","buffer2\n","buffer3\n"};
  
  fd = open(argv[1], O_WRONLY|O_CREAT, 0666);
  if (-1 == fd) {
    printf("open failed \n");
    _exit(-1);
  }
  
  //将三個IO緩沖區的檔案聚合為一個IO向量
  struct iovec iov[3];
  for (i ; i < 3; ++i) {
    iov[i].iov_base = buf[i];
    iov[i].iov_len = sizeof(buf[i]) + 1;
    printf("iovec[%d] size is %ld \n",i,iov[i].iov_len);
  }
  
  //寫入一個檔案
  count = writev(fd, iov, 3);
  
  if (-1 == count) {
    printf("writev failed\n");
    _exit(-1);
  }
  close(fd);


  return 0;
}      
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/uio.h>
#include <fcntl.h>

int main(int argc, char *argv[])
{
  ssize_t count = 0;
  int fd, i = 0;
  char buf1[9],buf2[9],buf3[9];

  //生成一個檔案,可以傳入聚合IO緩沖區的檔案
  fd = open(argv[1], O_RDONLY,0666);
  if (-1 == fd) {
    printf("open failed \n");
    _exit(-1);
  }
  
  //對IO向量中的各個IO緩沖區進行位址和内容大小指派
  struct iovec iov[3];
  iov[0].iov_base = buf1;
  iov[0].iov_len = sizeof(buf1);  
  iov[1].iov_base = buf2;
  iov[1].iov_len = sizeof(buf2);  
  iov[2].iov_base = buf3;
  iov[2].iov_len = sizeof(buf3);  

  //從page cache讀入的一個向量會分散到三個緩沖區中
  count = readv(fd,iov,3);

  if (-1 == count) {
    printf("writev failed\n");
    _exit(-1);
  }

  for (i ; i < 3; ++i) {
    printf(" %s",(char *)(iov[i].iov_base));
  }
  prinft("buf1 %s\n",buf1);
  prinft("buf2 %s\n",buf2);
  prinft("buf3 %s\n",buf3);

  close(fd);


  return 0;
}      
zhang@ubuntu:~/Desktop/cpp_practice$ ./writev  writevfile
iovec[0] size is 9 
iovec[1] size is 9 
iovec[2] size is 9 
zhang@ubuntu:~/Desktop/cpp_practice$ ./readv writevfile 
 buffer1
 buffer2
 buffer3
buf1 buffer1

buf2 buffer2

buf3 buffer3      

繼續閱讀