Saturday, August 30, 2014

Unix Prog: Standard I/O -- Buffering

1. Standard I/O Buffering
The goal of the buffering is to use the minimum number of read and write calls.
1) Fully Buffered: files residing on disk are normally fully buffered.Actual I/O takes place when the buffer is fully filled. The buffer is allocated by "malloc" when the first time I/O is performed.
I/O could also take place when fflush is called on stream

2) Line Buffered, actual I/O takes place when the newline character is encountered on input or output. It is normally used on terminal: standard input or output. I/O make also take place when input is requested through standard i/o lib from either: a) unbuffered stream or b) line-buffered stream

3) Unbuffered: standard I/O lib doesn't buffer characters. standard error stream is normally unbuffered.

ISO C standard:
Standard input/output are fully buffered, as long as they don't refer to interactive devices. Standard error is never fully buffered.

Actual Implementation:
Standard error is not buffered.
Other streams are line buffered if referring to interactive device otherwise fully buffered.

2. Change Stream Buffer
setbuf and setvbuf system calls are used to change the stream buffer.

system call definition:
 ubuntu@ip-172-31-23-227:~$ less /usr/include/stdio.h  
 ......  
 /* The possibilities for the third argument to `setvbuf'. */  
 #define _IOFBF 0        /* Fully buffered. */  
 #define _IOLBF 1        /* Line buffered. */  
 #define _IONBF 2        /* No buffering. */  
 ......  
 __BEGIN_NAMESPACE_STD  
 /* If BUF is NULL, make STREAM unbuffered.  
   Else make it use buffer BUF, of size BUFSIZ. */  
 extern void setbuf (FILE *__restrict __stream, char *__restrict __buf) __THROW;  
 /* Make STREAM use buffering mode MODE.  
   If BUF is not NULL, use N bytes of it for buffering;  
   else allocate an internal buffer N bytes long. */  
 extern int setvbuf (FILE *__restrict __stream, char *__restrict __buf,  
           int __modes, size_t __n) __THROW;  
 __END_NAMESPACE_STD  
 ......  

BUFSIZ definition:
 ubuntu@ip-172-31-23-227:~$ less /usr/include/_G_config.h  
 ......  
 #define _G_BUFSIZ 8192  
 ......  
 ubuntu@ip-172-31-23-227:~$ less /usr/include/libio.h  
 ......  
 #include <_G_config.h>  
 ......  
 #define _IO_BUFSIZ _G_BUFSIZ  
 ......  
 ubuntu@ip-172-31-23-227:~$ less /usr/include/stdio.h  
 ......  
 /* Default buffer size. */  
 #ifndef BUFSIZ  
 # define BUFSIZ _IO_BUFSIZ  
 #endif  
 ......  

fileio.c:
 #include<stdio.h>  
 #include<stdlib.h>  
 #include<unistd.h>  
   
 int main(int argc, char* argv[])  
 {  
  FILE* fp;  
  char buf[BUFSIZ];  
   
  if((fp = fopen("test.txt", "w+")) == NULL) {  
   printf("fopen error!\n");  
   exit(1);  
  }  
   
  // Setup the buffer, buffer size has to be block size  
  // System will decide if it is full buffer or line buffered  
  // For regular file residing on disk, it is normally full buffer  
  // For interactive device, it is normally line buffer  
  printf("current linux block size: %d\n", BUFSIZ);  
  setbuf(fp, buf);  

  // Disable the buffer: un-buffer now
  setbuf(fp, NULL);
   
  // Setup the stream to be full buffer, size is BUFSIZ  
  if(setvbuf(fp, buf, _IOFBF, BUFSIZ) < 0) {  
   printf("setvbuf error!\n");  
   exit(2);  
  }  

  // Setup to full buffer, but provide buf pointer as null
  // Then system will allocate the buffer automatically with
  // default BUFSIZ(4th argument is ignored)
  if(setvbuf(fp, NULL, _IOFBF, 0) < 0) {
    printf("setvbuf error!\n");
    exit(2);
  }
   
  // Setup the stream to be line buffer, size is BUFSIZ  
  if(setvbuf(fp, buf, _IOLBF, BUFSIZ) < 0) {  
   printf("setvbuf error!\n");  
   exit(2);  
  }  

  // Setup to line buffer, but provide buf pointer as null
  // Then system will allocate the buffer automatically with
  // default BUFSIZ
  if(setvbuf(fp, NULL, _IOLBF, 0) < 0) {
    printf("setvbuf error!\n");
    exit(2);
  }
   
  // Setup the stream to be non-buffer, size is BUFSIZ  
  // In this case buf and BUFSIZ are ignored by system  
  if(setvbuf(fp, buf, _IONBF, BUFSIZ) < 0) {  
   printf("setvbuf error!\n");  
   exit(2);  
  }  
   
  exit(0);  
 }  

3. Impact of customized buffer size
fileio.c:
 #include<stdio.h>  
 #include<stdlib.h>  
 #include<unistd.h>  
   
 #define OWN_BUFFSIZE 1024  
   
 int main(int argc, char* argv[])  
 {  
  FILE* fp;  
  char buf[OWN_BUFFSIZE];  
   
  printf("System block buffer size: %d\n", BUFSIZ);  
  printf("Our own block buffer size: %d\n", OWN_BUFFSIZE);  
   
  //By default, regular file is fully buffered  
  //when process is terminated, the content in buffer will be released  
  // to disk  
  if((fp = fopen("test1.txt", "w+")) == NULL) {  
   printf("fopen error!\n");  
   exit(1);  
  }  
   
  fputs("Hello world!\n", fp);  
   
  // We open the test2.txt with customisized buffer, in some  
  // implementations, if using different buffer size compared to  
  // default block buffer size BUFSIZ constant, the system will not  
  // release the buffer after the stream is closed. So we need to call  
  // flush to make sure content is released to disk.  
  if((fp = fopen("test2.txt", "w+")) == NULL) {  
   printf("fopen error!\n");  
   exit(1);  
  }  
   
  if(setvbuf(fp, buf, _IOFBF, OWN_BUFFSIZE) != 0) {  
   printf("setvbuf error!\n");  
   exit(2);  
  }  
   
  fputs("Hello world!\n", fp);  
  fflush(fp);  
   
  exit(0);  
 }  

shell:
 ubuntu@ip-172-31-23-227:~$ ./io.out  
 System block buffer size: 8192  
 Our own block buffer size: 1024  
 ubuntu@ip-172-31-23-227:~$ cat test1.txt  
 Hello world!  
 ubuntu@ip-172-31-23-227:~$ cat test2.txt  
 Hello world!  

Definition of "fflush":
 ubuntu@ip-172-31-23-227:~$ less /usr/include/stdio.h  
 ......  
 /* Flush STREAM, or all streams if STREAM is NULL.  
   
   This function is a possible cancellation point and therefore not  
   marked with __THROW. */  
 extern int fflush (FILE *__stream);  
 ......  

No comments:

Post a Comment