专栏名称: Linux内核之旅
Linux内核之旅
目录
相关文章推荐
51好读  ›  专栏  ›  Linux内核之旅

可重入、线程安全和异步信号安全

Linux内核之旅  · 公众号  · linux  · 2017-10-01 07:30

正文

请到「今天看啥」查看全文


  1. 要求信号处理函数本身必须是可重入的,然后,在信号处理函数中不要去调用不可重入的函数。
  2. 函数在在处理一些全局数据时,要进行信号屏蔽,等处理完后再开启信号

通过上面对异步信号安全的解释,我认为可重入函数一定是异步信号安全函数,但是异步信号安全函数不一定是可重入的,因为程序中断不仅仅只有信号这一种,异步信号安全的函数是可以通过信号屏蔽的方法使得函数本身变成异步信号安全的。而可重入函数需要处理在任何类型的中断情况下都是可重入的。

最佳实践

如何写出一个可重入的函数,是一个需要仔细探讨的问题,要写一个可重入函数就不要在函数内部使用静态或全局数据,不要返回静态或全局数据,也不调用不可重入函数。而要实现线程安全的函数就需要解决多个线程调用函数时访问资源冲突的问题(加锁,线程局部存储或许是大家经常使用的手段)。

通过总结出实际工程项目开发中的一些工程实践经验,可以给我们编写可重入函数提供一些指导性意见。

  • 返还指定静态数据结构的指针可能会导致函数不可重入

下面是一个不可重入版本的大小写转换

char * strtoupper(char *str)
{
        static char buf[BUFSIZ];
        int index;
        for(index = 0; str[index];index++)
        {
                buf[index] = toupper(str[index]);
        }
        buf[index] = 0;
        return buf;
}

修改为可重入版本

char * strtoupper_r(char *str,char *dst)
{
        int index;
        for(index = 0; str[index];index++)
        {
                dst[index] = toupper(str[index]);
        }
        dst[index] = 0;
        return dst
}
  • 在函数中记忆(保存)数据的状态导致不可重入

    char GetLowerCaseChar(char *str)
    {
            static index = 0;
            char c = 0;
            while(c = str[index])
            {
                    if(islower(c))
                    {
                            index++;
                            break;
                    }
                    index++
            }
            return c;
    }

这是一个搜索字符串中小写字符的程序,函数保存了搜索到字符串的位置index,导致这个函数是不可重入的这个index应该由调用者来维护,下面是这个函数的可重入版本:

char GetLowerCaseChar(char *str,int *index)
{
        char c = 0;
        while(c = str[*index])
        {
                if(islower(c))
                {
                        (*index)++;
                        break;
                }
                (*index)++
        }
        return c;
}
  • 任何分配和释放内存的库函数都是不可重入的
  • 小心处理进程范围内的全局变量入(例如 errno 和h_errno)
      if(open(filename,O_CREAT|O_RDWR) < 0 ){
          perror("open file fail:");
          exit(-1);
      }

上面这个函数就是一个不可重入的函数,当在执行open完,因为某些原因导致open失败结果就是改变了errno错误码,但此时被信号中断,去执行信号处理函数,信号处理函数中执行了一些系统调用或者库函数导致了errno全局状态码发生了变化,那么等待信号处理函数执行完毕返还后那么open失败的错误码就被覆盖了。为了避免上述问题发生应该在信号处理函数中保存errno状态码,执行完毕后进行恢复。

void handler(int signo)
{
    int errno_saved;
    errno_saved = errno;
    /*
        执行一些业务
    */
    //状态码恢复
    errno = errno_saved;
}






请到「今天看啥」查看全文