Saturday, October 11, 2014

Unix Prog: Reentrant Functions

1. Interrupted System Calls
If a process caught a signal while the process was blocked in a "slow" system call, the system call was interrupted. The system call returned an error and errno was set to EINTR.

System calls can be divided into two categories: the "slow" system calls and all the others.

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 these same file types.
3) Opens that block until some condition occurs on certain file types
4) The "pause" function and "wait" function
5) Certain ioctl operations.
6) Some of inter-process communication functions.

If the process is reading from the terminal, and the user just walk away, then it probably delay for hours or days.

2. Solution without restarted system calls
 again:  
   if((n = read(fd, buf, BUFFSIZE)) < 0) {  
     if(errno == EINTR)  
       go to again;  
   }  

In order to handle interrupted system calls, 4.2 BSD introduced the automatic restarting of certain interrupted system calls: including ioctl, read, readv, write, writev, wait and waitpid.

3. Reentrant Functions
When a signal that is being caught is handled by a process, the normal sequence of instructions being executed by the process is temporarily interrupted. It could be:

1) Right after executing one system call
2) In the middle of one system call(like malloc), after the signal handler returns, it will continue executing that system call.

The problem:
If signal comes in the middle of execution of one system call, and then it return from signal handler to the normal execution of that system call, things may go wrong.

Example:
1) The process is in the middle of allocating memory with "malloc", at this time, signal comes. Inside signal handler, we call "malloc" again. Since calling malloc may need to update some global variables, after returning back from signal handler, the entire context is already changed for the interrupted "malloc" call.
2) The process is in the middle of system call "getpwnam". "getpwnam" need to update the global struct passwd variable. In the middle of execution, the signal occurs, and inside the signal handler, we run "getpwnam" again, and then return. At this time, the context of original "getpwnam" is already changed, since signal handler already updated global struct passwd variable.

So, single UNIX specification specifies the functions that are guaranteed to be reentrant, which means, even if it is interrupted in the middle, if coming back from signal handler, it is working well. For reentrant functions, they normally:
1) don't use static global variable
2) don't use "malloc" or "free"

So if we are updating global static variable, we normally need to block the specific signals who has setup the signal handler.

4. Reentrant functions example
entrant.c:
 #include<stdio.h>  
 #include<stdlib.h>  
 #include<pwd.h>  
 #include<signal.h>  
   
 static void my_alarm(int signo)  
 {  
  struct passwd *rootptr;  
   
  printf("in signal handler\n");  
  if((rootptr = getpwnam("root")) == NULL) {  
   printf("getpwnam(root) error\n");  
   exit(1);  
  }  
   
  alarm(1);  
 }  
   
 int main(void)  
 {  
  struct passwd *ptr;  
   
  signal(SIGALRM, my_alarm);  
  alarm(1);  
  for(;;) {  
   if((ptr = getpwnam("ubuntu")) == NULL) {  
    printf("getpwnam error!\n");  
    exit(1);  
   }  
   if(strcmp(ptr->pw_name, "ubuntu") != 0) {  
    printf("return value corrupted!, pw_name = %s\n", ptr->pw_name);  
   }  
  }  
 }  

shell:
The program just setup the SIGALRM handler, and then fall into endless loop for running getpwnam. After one second, SIGALRM signal occurs, and main function instructions get interrupted and then it starts to run my_alarm, inside which it call the getpwnam again, which mess with the call in main function. And the program stuck there.
 ubuntu@ip-172-31-23-227:~$ ./entrant.out  
 in signal handler  

Actually in different system, the behavior may be different, depending on the implementation of system.

No comments:

Post a Comment