Sunday, November 9, 2014

Unix Prog: readv, writev

1. readv, writev

readv and writev function let us read into and write from multiple noncontiguous buffers in a single function call.

System Definition:
 ubuntu@ip-172-31-23-227:~$ less /usr/include/x86_64-linux-gnu/sys/uio.h  
 ......  
 /* Read data from file descriptor FD, and put the result in the  
   buffers described by IOVEC, which is a vector of COUNT 'struct iovec's.  
   The buffers are filled in the order specified.  
   Operates just like 'read' (see <unistd.h>) except that data are  
   put in IOVEC instead of a contiguous buffer.  
   
   This function is a cancellation point and therefore not marked with  
   __THROW. */  
 extern ssize_t readv (int __fd, const struct iovec *__iovec, int __count)  
  __wur;  
   
 /* Write data pointed by the buffers described by IOVEC, which  
   is a vector of COUNT 'struct iovec's, to file descriptor FD.  
   The data is written in the order specified.  
   Operates just like 'write' (see <unistd.h>) except that the data  
   are taken from IOVEC instead of a contiguous buffer.  
   
   This function is a cancellation point and therefore not marked with  
   __THROW. */  
 extern ssize_t writev (int __fd, const struct iovec *__iovec, int __count)  
  __wur;  
 ......  

struct iovec:
 ubuntu@ip-172-31-23-227:~$ less /usr/include/x86_64-linux-gnu/bits/uio.h  
 .....  
 /* Structure for scatter/gather I/O. */  
 struct iovec  
  {  
   void *iov_base;   /* Pointer to data. */  
   size_t iov_len;   /* Length of data. */  
  };  
 ......  

readv, writev returns how many bytes of data have been read or written, even if the procedure is broken it still returns how many bytes of data have been read or written.
readv read data from file descriptor, and fills the data into buffer array one by one. writev read data from the buffer array, and write into file descriptor one by one.

If there are multiple buffers of data needing to be read into or written from, readv, writev are more efficient compared to following ways:
1) call read, write multiple times
2) allocate a buffer of our own that is large enough to contain all buffers, then call read/write once.

2. Example:
vio.c:
 #include<stdio.h>  
 #include<stdlib.h>  
 #include<fcntl.h>  
 #include<unistd.h>  
 #include<sys/uio.h>  
   
 int main(int argc, char* argv[])  
 {  
  int fd;  
  char buf1[20] = "Hello";  
  char buf2[20] = "world!";  
   
  if((fd = open(argv[1], O_RDWR | O_CREAT)) < 0) {  
   printf("open error!\n");  
   exit(1);  
  }  
   
  struct iovec iv[2];  
  iv[0].iov_base = (void*)buf1;  
  iv[0].iov_len = 5;  
  iv[1].iov_base = (void*)buf2;  
  iv[1].iov_len = 6;  
   
  if(writev(fd, iv, 2) <= 0) {  
   printf("writev error!\n");  
   exit(2);  
  }  
   
  exit(0);  
 }  

shell:
 ubuntu@ip-172-31-23-227:~$ ./vio.out test.txt  
 ubuntu@ip-172-31-23-227:~$ cat test.txt  
 Helloworld!  

Note:
We should always try to use the fewest number of system calls necessary to get job done to save the time. We might find, however, the performance benefits aren't worth the extra complexity cost needed to manage our own staging buffers.

No comments:

Post a Comment