This occurs in a multi-task system. When a signal is captured and processed during task execution, the command sequence being executed by the process is temporarily interrupted by the signal processing program. If the result is returned from the signal processing program, the normal command sequence at the breakpoint of the execution process is continued. During the process from re-recovery to re-execution of the breakpoint, the environment on which the function depends has not changed, this function is reentrant, and vice versa.
As we all know, during a process interruption, the system will save and restore the context of the process. However, the recovered context is limited to a small number of contexts such as the return address and CPU register, the global or static variables, buffer, and other values used in the function are not protected. If these values change during the interruption of the function, when the function returns to the breakpoint for further execution, the results are unpredictable. For example, for malloc, a process is executing malloc to allocate heap space at this time. At this time, the program captures the interrupted signal, and the execution of the signal processing program exactly has a malloc, this will cause damage to the process environment, because malloc usually maintains a chain table for the storage area it is allocated to. When inserting the signal processing function, the process may be operating on this table, the call of the signal processing function overwrites the operation of the process, resulting in errors.
Most of the functions that meet one of the following conditions are non-reentrant functions:
(1) The static data structure is used;
(2) malloc or free is called;
(3) The standard I/O function is called. Many implementations of the standard Io library use the global data structure in non-reentrant mode.
(4) floating point operations are performed. In many processors/compilers, floating points are generally not reentrant (floating point operations are mostly implemented using coprocessor or software simulation.
1) The signal processing program a calls the same non-reentrant function B both inside and outside. B is interrupted by the signal during execution and enters a (B is called in ), after the completion of the task, the system returns B to be executed by a breakpoint. In this case, the environment of function B may change and the result is unpredictable.
2) multi-thread sharing of resources within the process. If two threads a and B call the same non-reentrant function f, after thread a enters thread F, thread scheduling switches to thread B, B Also executes F, so when it switches to thread a again, the result of calling F is unpredictable.
In the signal processing program, even if you call the reentrant function, pay attention to the problem. As a general rule, when calling a reentrant function in a signal processing program, the errno should be saved before it and restored after it. (Because each thread has only one errno variable, the signal processing function may modify its value. You need to know that the signal often caught under arrest is sigchld, and its signal processing program usually calls a wait function, various wait functions can change errno .)
Reentrant function list:
_ Exit (), access (), alarm (), cfgetispeed (), cfgetospeed (), cfsetispeed (), cfsetospeed (), chdir (), chmod (), chown (), close (), creat (), DUP (), dup2 (), execle (), execve (), fcntl (), fork (), fpathconf (), fstat (), fsync (), getegid (), geteuid (), getgid (), getgroups (), getpgrp (), getpid (), getppid (), getuid (), kill (), Link (), lseek (), mkdir (), mkfifo (), open (), pathconf (), pause (), pipe (), raise (), read (), rename (), rmdir (), setgid (), setpgi D (), setsid (), setuid (), sigaction (), sigaddset (), sigdelset (), sigemptyset (), sigfillset (), sigismember (), signal () sigpending (), sigprocmask (), sigsuspend (), sleep (), Stat (), sysconf (), tcdrain (), tcflow (), tcflush (), tcgetattr (), tcgetpgrp (), tcsendbreak (), tcsetattr (), tcsetpgrp (), time (), times (), umask (), uname (), unlink (), utime (), wait (), waitpid (), write ().
The following is an example of calling a non-reentrant function in a signal processing program:
# Include <stdlib. h>
# Include <stdio. h>
# Include <PWD. h>
Static void func (INT signo)
{
Struct passwd * rootptr;
If (rootptr = getpwnam ("root") = NULL)
{
Err_sys ("getpwnam error ");
}
Signal (sigalrm, func );
Alarm (1 );
}
Int main (INT argc, char ** argv)
{
Signal (sigalrm, func );
Alarm (1 );
For (;;)
{
If (PTR = getpwnam ("SAR") = NULL)
{
Err_sys ("getpwnam error ");
}
}
Return 0;
}
Signal has a sigalrm, and then sets a timer. During the for function operation, the signal may be interrupted during the getpwnam function operation and enters the signal processing function func, when you run func, you receive a signal from alarm. getpwnam may be interrupted again, which is prone to unexpected problems.
========================================================== ========================================================== =
A non-reentrant function cannot be called again if it has not been returned. For example, printf, malloc, and free functions are non-reentrant functions. Because the interruption may occur at any time, for example, during the execution of printf, you cannot call printf In the interrupt processing function; otherwise, printf will be reimported.
Most functions cannot be reentrant because global variables are referenced in the function. For example, printf will reference the global variables stdout, malloc, and free will reference the global memory allocation table.
Personal Understanding: When an interruption occurs, when running to printf, it is assumed that the nested interrupt occurs, and stdout resources are occupied at this time, therefore, the second interrupt printf waits for the first interrupted stdout resource to be released, and the first interrupt waits for the second interrupt to return, resulting in a deadlock.
A non-reentrant function indicates that an error may occur when the function is called again before it is called. The reentrant function does not have this problem.
Non-reentrant functions usually use global resources during implementation. In a multi-threaded environment, errors may occur if data protection and mutual access are not well handled.
Common Non-reentrant functions include:
Printf -------- reference the global variable stdout
Malloc -------- global memory allocation table
Free -------- global memory allocation table
In UNIX, function versions with the same name and suffix _ r are usually reentrant. If not, try to add a protection lock synchronization mechanism in the foreseeable case.