Monday, November 3, 2014

Unix Prog: Non-blocking I/O

1. Slow system calls
1) reads that can block the caller forever if data isn't present with certain file types
2) writes that can block the caller forever if the data can't be accepted immediately by the same file types
3) Opens that block until some condition occurs on certain file types
4) Reads and writes of files that have mandatory record locking enabled.

2. Nonblocking I/O
Nonblocking I/O lets us issue an I/O operation without blocking forever

two ways of specifying nonblocking I/O for a given descriptor:
1) call open to get the descriptor, we can specify the O-NONBLOCK flag
2) for a file descriptor that is already open, call fcntl to turn on the O_NONBLOCK file status flag.

3. Example:
 #include<stdio.h>  
 #include<stdlib.h>  
 #include<fcntl.h>  
 #include<unistd.h>  
 #include<errno.h>  
   
 char buf[500000];  
   
 void set_fl(int fd, int flag)  
 {  
  int val;  
  if((val = fcntl(fd, F_GETFL, 0)) < 0) {  
    printf("fcntl error!\n");  
    exit(1);  
  }  
   
  val |= flag;  
   
  if(fcntl(fd, F_SETFL, val) < 0) {  
   printf("fcntl error!\n");  
   exit(1);  
  }  
 }  
   
 void clr_fl(int fd, int flag)  
 {  
  int val;  
  if((val = fcntl(fd, F_GETFL, 0)) < 0) {  
   printf("fcntl error!\n");  
   exit(1);  
  }  
   
  val &= ~flag;  
   
  if(fcntl(fd, F_SETFL, val) < 0) {  
   printf("fcntl error!\n");  
   exit(1);  
  }  
 }  
   
 int main(int argc, char *argv[])  
 {  
  int ntowrite, nwrite;  
  char *ptr;  
   
  // read data from standard input to buffer  
  ntowrite = read(STDIN_FILENO, buf, sizeof(buf));  
  fprintf(stderr, "read %d bytes\n", ntowrite);  
   
  // Setup the nonblock flag for standard output  
  set_fl(STDOUT_FILENO, O_NONBLOCK);  
   
  // write the data from buffer, to standard output  
  ptr = buf;  
  while(ntowrite > 0) {  
   errno = 0;  
   nwrite = write(STDOUT_FILENO, ptr, ntowrite);  
   fprintf(stderr, "nwrite = %d, errno = %d\n", nwrite, errno);  
   
   if(nwrite > 0) {  
    ptr += nwrite;  
    ntowrite -= nwrite;  
   }  
  }  
   
  // clear the standard output nonblock flag  
  clr_fl(STDOUT_FILENO, O_NONBLOCK);  
  exit(0);  
 }  

shell:
If we are writing against one file, then it is expected to execute only once.
 ubuntu@ip-172-31-23-227:~$ ./nonblock.out <test.txt >result.txt  
 read 500000 bytes  
 nwrite = 500000, errno = 0  

shell:
If we are writing against standard output(terminal), then it is expected to execute thousands of times, but only a few times succeed(from error.txt). After grepping "errno = 0" from error.txt, we know that it successfully write for only 5 times.
 ubuntu@ip-172-31-23-227:~$ ./nonblock.out <test.txt 2>error.txt  
 Hello world!  
 Hello world!  
 Hello world!  
 Hello world!  
 ......  
 ubuntu@ip-172-31-23-227:~$ less error.txt  
 read 500000 bytes  
 nwrite = 160185, errno = 0  
 nwrite = -1, errno = 11  
 nwrite = -1, errno = 11  
 ......  
 ubuntu@ip-172-31-23-227:~$ grep "errno = 0" error.txt  
 nwrite = 160185, errno = 0  
 nwrite = 105300, errno = 0  
 nwrite = 128934, errno = 0  
 nwrite = 18954, errno = 0  
 nwrite = 86627, errno = 0  

For all unsuccessful calls, we call it polling, which is a waste of CPU time, but if we want to avoid polling but still want to let the thread avoid blocking on I/O, we can try create another thread to work on I/O.

No comments:

Post a Comment