可重入函数

文章目录

一、全局变量异步IO可能会造成什么问题?

父子进程分别对累加:

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

// 父子进程分别对n进行累加
// 每隔1秒,相互发送信号通知累加
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;	// 计数完成
	//sleep(1);
}

void deal_sig_parent(int num)
{
	printf("I am parent %d\t%d\n", getpid(), n);
	n += 2;
	flag = 1;	// 计数完成
	//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); // 保证子进程注册信号的动作能完成
		act.sa_handler = deal_sig_parent;
		sigemptyset(&act.sa_mask);
		act.sa_flags = 0;
		sigaction(SIGUSR2, &act, NULL);	//	注册SIGUSR2的捕捉函数

		// 父进程先开始累加
		deal_sig_parent(0);
		
		while(1)
		{
			// wait for signal
			if (flag == 1) 
			{
				kill(pid, SIGUSR1);
                  // ----------------这里可能失去CPU
				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);	//	注册SIGUSR1的捕捉函数

		while(1)
		{
			// wait for signal
			if (flag == 1) 
			{
				kill(getppid(), SIGUSR2); // 给父进程发送SIGUSR2
				flag = 0;
			}
		}
	}

	return 0;
}

SIGUSR1: 10,用户定义的信号。即程序员可以在程序中定义并使用该信号。默认动作为终止进程。

SIGUSR2: 12,另一个用户定义的信号。即程序员可以在程序中定义并使用该信号。默认动作为终止进程。

  如果注释掉代码中的两处sleep(1),会出现一个问题:n累加一会后,不会再累加,程序进入循环等待。原因如下:

  1. 父进程调用kill给子进程发送信号,通知开始计数。
  2. 父进程在执行flag=0前可能会失去CPU,等待CPU。
  3. 而子进程计数完成,会发送信号给父进程。
  4. 父进程获得CPU后,先调用信号处理函数,将 flag = 1;
  5. 父进程处理完信号后,恢复到断点继续处理程序,执行flag = 0,flag 的值被覆盖。
  6. 此时子进程不再给父进程发送信号,父进程也不能给子进程发送信号,程序陷入死循环。

解决方法:

  1. 加锁;
  2. 不使用全局变量,使信号处理函数成为可重入函数。

二、什么是可重入函数?

  一个函数在被调用执行期间(尚未调用结束),由于某种时序又被重复调用,称之为重入。根据函数实现的方法分为可重入函数不可重入函数两种。

1、可重入函数

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

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

// 如果上面两个main函数执行的结果一致,则称func(int a)为可重入函数。

  递归调用与连续调用多次,结果一致,就是可重入函数。

2、不可重入函数

可重入函数


  显然,insert函数是不可重入函数,重复调用,会导致意外的结果。究其原因,是该函数内部实现使用了全局变量head

3、注意事项

  1. 定义可重入函数,函数内不能含有全局变量及static变量,不能使用malloc、free。

  2. 信号捕捉函数应该设计为可重入函数。

  3. 信号处理程序可以调用的可重入函数可以参考man 7 signal

  4. 没有包含在上述列表中的函数大多是不可重入函数。其原因为:

    a) 使用了静态数据结构

    b) 调用了malloc或free

    c) 是标准IO函数

上一篇:MySQL触发器


下一篇:守护进程-线程