首页 > 编程语言 > 详细

可重入,异步信息安全,线程安全

时间:2016-10-21 20:17:16      阅读:333      评论:0      收藏:0      [点我收藏+]

  这三个概念一直纠缠着我,我也时不时的会拿出来辨析下,直到昨天才发现自己可以把它们理顺了。所以学习就是这样一个反复的过程,最终达到顿悟的效果。本文主要参考APUE第三版英文版第10.6和12.5节,以及WIKI百科,还有CSDN和stackoverflow中对这些概念的讨论,然后给出一份自己认为比较合理的理解。
?

I. 中断,信号,线程切换

  1. 这三个概念都牵涉到异步通信,即运行中的代码不可预测什么时候会发生中断,什么时候会收到信号,什么时候会发生线程切换:

    • 中断,一般指的硬件中断,是硬件对cpu的中断
    • 信号,则是对中断的模拟,可以看作是os对进程的中断
    • 线程切换,cpu的时分复用手段
  2. 代码执行流:

#-表示代码在cpu上运行,.表示等待中

##1. 中断和信号
<f1>---------...................-----------
<f2>.........-------------------...........
###f1在执行的过程中被f2打断,当且仅当f2完整的执行完后返回f1

##2. 线程切换
<f1>----....----....----....----....----
<f2>....----....----....----....----....
###f1和f2互相打断彼此,交错地运行

?

II. 可重入,异步信息安全,线程安全

1. 可重入

  可重入的意思就是一个函数没有执行完,又在另一个地方被调用一次,两次调用都能得到正确的结果。可重入概念在多任务操作系统之前就已经存在了,It is a concept from the time when no multitasking operating systems existed。

2. 异步信息安全,线程安全

  可重入中提到的”另一个地方”可以是:

  • 在中断或信号中,在此情况下如果函数是可重入的,那么就称这个函数是异步信号安全的,这两种情况可以看成是一种递归调用。APUE10.6节就是专门说明异步信号安全的。
  • 在另一个线程中,在此情况下如果函数是可重入的,那么就称这个函数是线程安全的。APUE12.5节就是专门说明线程安全的。

If a function is reentrant with respect to multiple threads, we say that it is thread-safe. This doesn’t tell us, however, whether the function is reentrant with respect to signal handlers. We say that a function that is safe to be reentered from an asynchronous signal handler is async-signal safe. —According to APUE 3e Chapter 12.5

3. 三者的关系

三者比较合理的关系图应该是:
               技术分享
  红色表示不可重入函数,A+B+C表示可重入函数,其中A表示递归调用情况下可重入,C表示多线程的情况下可重入,B表示种情况下都可重入。所以单独说某函数是重入,而不限定使用场景是没有意义的;如果仅仅说是非可重入则又是有意义的…

4. 异步信号安全函数列表

  技术分享

5. 常见困惑FAQ

  • Q:线程安全函数是可重入的吗?
    A:线程安全函数是多线程情况下的可重入。但在信号或者中断中调用的情况下是不可重入的。举例如下:
    一个函数通过使用mutex机制来实现多线程安全,那么这个函数如果用在信号处理函数中,那么就可能出现死锁,所以它就不是异步信号安全的。

  • Q:可重入函数和异步信号安全函数等同吗?
    A:根据上面的关系图可以得出两者是不等同的,可重入函数在不同的情景下可以分为异步信号安全和线程安全。异步信号安全可以看作是递归调用情况下的可重入。举例如下:

//非可重入版本
int t;

void swap(int *x, int *y)
{
    t = *x;
    *x = *y;

    // hardware interrupt might invoke isr() here!!
    *y = t;
}

void isr()
{
    int x = 1, y = 2;
    swap(&x, &y);
}
//异步信号安全版本,非线程安全
int t;

void swap(int *x, int *y)
{
    int s;

    s = t; // save global variable
    t = *x;
    *x = *y;

    // hardware interrupt might invoke isr() here!
    *y = t;
    t = s; // restore global variable
}

void isr()
{
    int x = 1, y = 2;
    swap(&x, &y);
}
  • Q:还有其它问题?
    A:没有了吧,上面解释的已经够清晰了,哈哈
    ?

III. 它山之玉

  1. 信号就像硬件中断一样,会打断正在执行的指令序列。信号处理函数无法判断捕获到信号的时候,进程在何处运行。如果信号处理函数中的操作与打断的函数的操作相同,而且这个操作中有静态数据结构等,当信号处理函数返回的时候(当然这里讨论的是信号处理函数可以返回),恢复原先的执行序列,可能会导致信号处理函数中的操作覆盖了之前正常操作中的数据。
    所以通常函数不可重入的原因在于:

    • 函数使用静态数据结构;
    • 函数调用malloc和free.因为malloc通常会为所分配的存储区维护一个链接表,而插入执行信号处理函数的时候,进程可能正在修改此链接表;
    • 函数是标准IO函数,因为标准IO库的很多实现都使用了全局数据结构;
    • 函数会修改自身代码,导致多次调用不同代码;
    • 等等。
  2. 即使对于可重入函数,在信号处理函数中使用也需要注意一个问题就是errno。一个线程中只有一个errno变量,信号处理函数中使用的可重入函数也有可能会修改errno。例如,read函数是可重入的,但是它也有可能会修改errno。因此,正确的做法是在信号处理函数开始,先保存errno;在信号处理函数退出的时候,再恢复errno。
    例如,程序正在调用printf输出,但是在调用printf时,出现了信号,对应的信号处理函数也有printf语句,就会导致两个printf的输出混杂在一起。
    如果是给printf加锁的话,同样是上面的情况就会导致死锁。对于这种情况,采用的方法一般是在特定的区域屏蔽一定的信号。
    屏蔽信号的方法:
    1> signal(SIGPIPE, SIG_IGN); //忽略一些信号
    2> sigprocmask()
    sigprocmask只为单线程定义的
    3> pthread_sigmask()
    pthread_sigmasks可以在多线程中使用

  3. 很多函数并不是线程安全的,因为他们返回的数据是存放在静态的内存缓冲区中的。通过修改接口,由调用者自行提供缓冲区就可以使这些函数变为线程安全的。操作系统实现支持线程安全函数的时候,会对POSIX.1中的一些非线程安全的函数提供一些可替换的线程安全版本。
    例如,gethostbyname()是线程不安全的,在Linux中提供了gethostbyname_r()的线程安全实现。
    函数名字后面加上”_r”,以表明这个版本是可重入的(对于线程可重入,也就是说是线程安全的,但并不是说对于信号处理函数也是可重入的,或者是异步信号安全的)。

  4. 多线程程序中常见的疏忽性问题

    • 将指针作为新线程的参数传递给调用方栈,我就犯过这样的错…
    • 在没有同步机制保护的情况下访问全局内存的共享可更改状态。
    • 两个线程尝试轮流获取对同一对全局资源的权限时导致死锁。其中一个线程控制第一种资源,另一个线程控制第二种资源。其中一个线程放弃之前,任何一个线程都无法继续操作。
    • 尝试重新获取已持有的锁(递归死锁)。
    • 在同步保护中创建隐藏的间隔。如果受保护的代码段包含的函数释放了同步机制,而又在返回调用方之前重新获取了该同步机制,则将在保护中出现此间隔。结果具有误导性。对于调用方,表面上看全局数据已受到保护,而实际上未受到保护。
    • 将UNIX 信号与线程混合时,使用sigwait(2) 模型来处理异步信号。
    • 调用setjmp(3C) 和longjmp(3C),然后长时间跳跃,而不释放互斥锁。
    • 从对*_cond_wait() 或*_cond_timedwait() 的调用中返回后无法重新评估条件。
  5. 如果一个函数对多个线程来说是可重入的,则说这个函数是线程安全的,但这并不能说明对信号处理程序来说该函数也是可重入的。
    如果函数对异步信号处理程序的重入是安全的,那么就可以说函数是”异步-信号安全”的。
    ?

IV. 参考文档

  1. APUE第三版原版10.6&12.5,去阅读原版而不是中文版(有道在手,天下我有:P)
  2. https://en.wikipedia.org/wiki/Reentrancy_(computing)
  3. http://bbs.csdn.net/topics/310140183
  4. http://stackoverflow.com/questions/9837343/difference-between-thread-safe-and-async-signal-safe
  5. http://stackoverflow.com/questions/18198487/are-r-unix-calls-reentrant-async-signal-safe-thread-safe-or-both

可重入,异步信息安全,线程安全

原文:http://blog.csdn.net/lidonghat/article/details/52881418

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!