Reentrant and non reentrant

Posted by Jedi Legend on Sun, 30 Jan 2022 23:45:23 +0100

Reentrant and non reentrant
Reprint: https://www.cnblogs.com/reality-soul/p/6140932.html
It is mainly used in multitasking environment. A reentrant function is simply a function that can be interrupted, that is, it can be interrupted at any time when the function is executed, transferred to OS scheduling to execute another piece of code, and there will be no error when returning to control; The non reentrant function uses some system resources, such as global variable area, interrupt vector table, etc., so if it is interrupted, there may be problems. This kind of function cannot run in multi task environment.

It can also be understood that reentry means repeated entry. Firstly, it means that this function can be interrupted. Secondly, it means that it does not depend on any environment (including static) except using the variables on its own stack. Such a function is purecode (pure code) reentrant, which allows multiple copies of the function to run. Because they use separate stacks, So they won't interfere with each other. If you really need to access global variables (including static), be sure to implement mutual exclusion. Reentrant functions are very important in parallel running environment, but they usually pay some performance costs for accessing global variables.

When writing reentrant functions, if global variables are used, they should be protected by means of Customs interruption, semaphore (i.e. P, V operation), etc.

Note: if the global variables used are not protected, this function will not be reentrant, that is, when multiple processes call this function, it is likely to change the relevant global variables into an unknown state.

Example: suppose exam is a global variable of type int, and the function Squre_Exam returns the square value of exam. Then the following functions are not reentrant.

unsigned int example( int para )

{

unsigned int temp;
    Exam = para; // (**)
    temp = Square_Exam( );
    return temp;
}
If this function is called by multiple processes, the result may be unknown, because when(**)Just after the statement is executed, another process using this function may be activated. When the newly activated process executes this function, it will Exam Give another different para Value, so when the control returns to“ temp = Square_Exam( )"After that, the calculated temp It may not be the expected result. This function should be improved as follows.

unsigned int example( int para ) {
    unsigned int temp;
    [Apply for semaphore operation] //(1)
    Exam = para;
    temp = Square_Exam( );
    [Release semaphore operation]
    return temp;
}
(1)If the "semaphore" cannot be applied, it indicates that another process is in the process of giving Exam In the process of assigning and calculating its square (i.e. this signal is being used),This process must wait for its release signal before it can continue. If a signal is applied, it can continue to be executed, but other processes must wait for the process to release the semaphore before using the signal.

Methods to ensure the reentrancy of functions:
When writing functions, try to use local variables (such as registers and variables in the stack), and protect the global variables to be used (such as off interrupt, semaphore and other methods). In this way, the function must be a reentrant function.
VxWorks The reentrant technologies adopted in are:
* Dynamic stack variable (each sub function has its own independent stack space)
* Protected global and static variables
* Task variable
In the design of real-time system, multiple tasks often call the same function. If this function is unfortunately designed as a non reentrant function, when different tasks call this function, it may modify the data of other tasks calling this function, resulting in unpredictable consequences. So what is a reentrant function? The so-called reentrant function refers to a process that can be called by multiple tasks. The task does not have to worry about whether the data will be wrong when calling. Non reentrant functions are regarded as unsafe functions in real-time system design. Most functions that meet the following conditions are non reentrant:
1) Static data structure is used in the function body;
2) Function body called malloc()perhaps free()Function;
3) The standard is called in the function body I/O Function.

Examples are given below.
A. Reentrant function
void strcpy(char *lpszDest, char *lpszSrc)

{
while(*lpszDest++=*lpszSrc++);
*dest=0;
}

B. Non reentrant function 1
charcTemp;//global variable
void SwapChar1(char *lpcX, char *lpcY)

{
cTemp=*lpcX;
*lpcX=*lpcY;
lpcY=cTemp;// Access to global variables
}

C. Non reentrant function 2
void SwapChar2(char *lpcX,char *lpcY)

{
static char cTemp;// Static local variable
cTemp=*lpcX;
*lpcX=*lpcY;
lpcY=cTemp;// Static local variables are used
}

Question 1, how to write reentrant functions?
A: do not access those global variables in the function body, do not use static local variables, and insist on using only local variables. The written function will be reentrant. If you must access global variables, remember to protect them with mutually exclusive semaphores.

Question 2, how to rewrite a non reentrant function into a reentrant function?
A: the only way to make a non reentrant function reentrant is to rewrite it with reentrant rules. In fact, it is very simple. As long as you abide by a few rules that are easy to understand, the written function is reentrant.
1) Do not use global variables. Because other code is likely to overwrite these variable values.
2) When interacting with hardware, remember to perform similar tasks disinterrupt()Such operations are to turn off the hardware interrupt. It's called "interrupt, remember" on this series/Exit core.
3) You cannot call any other non reentrant functions.
4) Use stacks with caution. It's best to use it first OS_ENTER_KERNAL. 

Stack operation involves memory allocation. If you are not careful, it will result in overwriting the data of other tasks. Therefore, please use stack with caution! Better not use it! Many hacker programs take advantage of this so that the system can execute illegal code and easily gain control of the system. There are also some rules. In short, always remember one sentence: ensure that interruption is safe!

Example problem: once designed the following function, which was reminded when checking the code bugļ¼ŒBecause this function is not reentrant, why?
unsigned int sum_int( unsigned int base )

{
unsigned int index;
static unsigned int sum = 0; // Note that it is of type static
for (index = 1; index <= base; index++)
sum += index;
return sum;
}

Analysis: the so-called function is reentrant (can also be said to be predictable), that is, as long as the input data is the same, it should produce the same output. This function is unpredictable because it uses static Variable, because static According to the characteristics of variables, such functions are called functions with "internal memory" function. Therefore, if you need a reentrant function, you must avoid using it in the function static Variable, in this function static The principle of using variables is to use them as little as possible.
Change the above function to a reentrant function, as long as the declaration sum In variable static Keyword removed, variable sum Becomes a auto Type variable, the function becomes a reentrant function.
Of course, sometimes, it must be used in functions static For example, when the return value of a function is of pointer type, it must be static The address of the local variable as the return value, if auto Type, the error pointer is returned.

This situation occurs in multi task system. When the signal is captured and processed during task execution, the instruction sequence being executed by the process is temporarily interrupted by the signal processor. If it returns from the signal processing program, it will continue to execute the normal instruction sequence at the process breakpoint. In the process of resuming to the re execution of the breakpoint, the environment on which the function depends has not changed, that is, the function is reentrant, otherwise it is not reentrant.
As we all know, during the process interruption, the system will save and restore the process context. However, the restored context is limited to a small number of contexts such as return address, cpu register, etc., and the internal use of functions such as global or static variables and buffer are not protected. Therefore, if these values change during the function interruption, Then when the function returns to the breakpoint and continues to execute, the result is unpredictable. For example, malloc, a process is executing malloc to allocate heap space. When the program catches the signal and interrupts, there happens to be a malloc in the execution signal processing program, which will damage the environment of the process, because malloc usually maintains a link table for the storage area allocated to it. When inserting and executing the signal processing function, The process may be operating on this table, and the call of signal processing function just covers the operation of the process, resulting in an error.

Most functions that meet one of the following conditions are non reentrant functions:
(1) Using static data structure;
(2) malloc or free is called;
(3) Standard I/O functions are called; Many implementations of the standard io library use global data structures in a non reentrant manner.
(4) Floating point operation is carried out In many processors / compilers, floating-point operations are generally non reentrant (floating-point operations are mostly implemented by coprocessor or software simulation).

Topics: Embedded system FreeRTOS