Reentrant function

1, What problems can asynchronous IO of global variables cause?

The parent and child processes are accumulated separately:

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>

// The parent-child processes accumulate n respectively
// Send signals to each other every 1 second to inform accumulation
int n = 0, flag = 0;

void sys_err(char *str)
{
	perror(str);
	exit(EXIT_FAILURE);
}

void deal_sig_child(int signo)
{
	printf("I am child %d\t%d\n", getpid(), n);
	n += 2;
	flag = 1;	// Count complete
	//sleep(1);
}

void deal_sig_parent(int num)
{
	printf("I am parent %d\t%d\n", getpid(), n);
	n += 2;
	flag = 1;	// Count complete
	//sleep(1);
}

int main()
{
	pid_t pid;
	struct sigaction act;

	if ((pid = fork()) < 0)
	{
		sys_err("fork");
	}
	else if (pid > 0)
	{
		n = 1;
		sleep(1); // Ensure that the action of sub process registration signal can be completed
		act.sa_handler = deal_sig_parent;
		sigemptyset(&act.sa_mask);
		act.sa_flags = 0;
		sigaction(SIGUSR2, &act, NULL);	//	Register the capture function of SIGUSR2

		// The parent process starts accumulation first
		deal_sig_parent(0);
		
		while(1)
		{
			// wait for signal
			if (flag == 1) 
			{
				kill(pid, SIGUSR1);
                  // ----------------CPU may be lost here
				flag = 0;
			}
		}
	}
	else if (pid == 0)
	{
		n = 2;
		act.sa_handler = deal_sig_child;
		sigemptyset(&act.sa_mask);
		act.sa_flags = 0;
		sigaction(SIGUSR1, &act, NULL);	//	Register the capture function of SIGUSR1

		while(1)
		{
			// wait for signal
			if (flag == 1) 
			{
				kill(getppid(), SIGUSR2); // Send SIGUSR2 to parent process
				flag = 0;
			}
		}
	}

	return 0;
}

SIGUSR1: 10, user-defined signal. That is, the programmer can define and use the signal in the program. The default action is to terminate the process.

SIGUSR2: 12, another user-defined signal. That is, the programmer can define and use the signal in the program. The default action is to terminate the process.

   if you comment out two sleep(1) in the code, there will be a problem: after n is accumulated for a while, it will not be accumulated again, and the program enters the loop and waits. The reasons are as follows:

  1. The parent process calls kill to send a signal to the child process to start counting.
  2. The parent process may lose CPU and wait for CPU before executing flag=0.
  3. When the child process count is completed, it will send a signal to the parent process.
  4. After the parent process obtains the CPU, first call the signal processing function and set flag = 1;
  5. After processing the signal, the parent process returns to the breakpoint to continue processing the program, executes flag = 0, and the value of flag is overwritten.
  6. At this time, the child process no longer sends signals to the parent process, and the parent process cannot send signals to the child process. The program falls into an endless loop.

resolvent:

  1. Locking;
  2. Without using global variables, the signal processing function becomes a reentrant function.

2, What is a reentrant function?

   when a function is called and executed (before the end of the call), it is repeatedly called due to a certain timing, which is called reentry. According to the methods of function implementation, it is divided into reentrant function and non reentrant function.

1. Reentrant function

int main()
{
    func(a) {
        ...
        func(a);
        ...
    }
}

int main()
{
    func(a);
    func(a);
}

// If the results of the above two main functions are consistent, func(int a) is called a reentrant function.

   recursive calls and successive calls have the same result, that is, reentrant functions.

2. Non reentrant function


  obviously, the insert function is a non reentrant function. Repeated calls will lead to unexpected results. The reason is that the internal implementation of the function uses the global variable head.

3. Precautions

  1. Define a reentrant function. The function cannot contain global variables and static variables. malloc and free cannot be used.

  2. The signal capture function should be designed as a reentrant function.

  3. The reentrant function that can be called by the signal handler can refer to man 7 signal.

  4. Most functions not included in the above list are non reentrant functions. The reasons are:

    a) Static data structure is used

    b) malloc or free called

    c) Is a standard IO function

Added by FlashbackJon on Fri, 18 Feb 2022 00:40:34 +0200