正文
-
要求信号处理函数本身必须是可重入的,然后,在信号处理函数中不要去调用不可重入的函数。
-
函数在在处理一些全局数据时,要进行信号屏蔽,等处理完后再开启信号
通过上面对异步信号安全的解释,我认为可重入函数一定是异步信号安全函数,但是异步信号安全函数不一定是可重入的,因为程序中断不仅仅只有信号这一种,异步信号安全的函数是可以通过信号屏蔽的方法使得函数本身变成异步信号安全的。而可重入函数需要处理在任何类型的中断情况下都是可重入的。
最佳实践
如何写出一个可重入的函数,是一个需要仔细探讨的问题,要写一个可重入函数就不要在函数内部使用静态或全局数据,不要返回静态或全局数据,也不调用不可重入函数。而要实现线程安全的函数就需要解决多个线程调用函数时访问资源冲突的问题(加锁,线程局部存储或许是大家经常使用的手段)。
通过总结出实际工程项目开发中的一些工程实践经验,可以给我们编写可重入函数提供一些指导性意见。
下面是一个不可重入版本的大小写转换
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;
}
上面这个函数就是一个不可重入的函数,当在执行open完,因为某些原因导致open失败结果就是改变了errno错误码,但此时被信号中断,去执行信号处理函数,信号处理函数中执行了一些系统调用或者库函数导致了errno全局状态码发生了变化,那么等待信号处理函数执行完毕返还后那么open失败的错误码就被覆盖了。为了避免上述问题发生应该在信号处理函数中保存errno状态码,执行完毕后进行恢复。
void handler(int signo)
{
int errno_saved;
errno_saved = errno;
/*
执行一些业务
*/
//状态码恢复
errno = errno_saved;
}