这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » FPGA » 信号可重入线程安全

共1条 1/1 1 跳转至

信号可重入线程安全

助工
2014-12-21 10:20:11     打赏
信号可重入线程安全






#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/time.h>
void gotsig(int n)
{
    printf("haha");
}

int main()
{
    struct itimerval value;
        struct sigaction sact;

    sigemptyset( &sact.sa_mask );
    sact.sa_flags = 0;
    sact.sa_handler = gotsig;
    sigaction(SIGALRM,&sact,NULL);

    value.it_interval.tv_sec = 0;
    value.it_interval.tv_usec = 100000;
    value.it_value.tv_sec = 0;
    value.it_value.tv_usec = 1000;

    setitimer(ITIMER_REAL, &value,NULL);


    while(1) {
        /* other code here */
        printf("HA~");
    }
}

 这个代码的意思就是每一毫秒发送给自己一次SIGALRM信号,然后一直打印
这个代码有什么问题,咋一看,看不出来有什么问题吧(如果你一眼就看出问题来,那么不要再往下阅读了^^)
答案是,很可能会死锁,死锁有几个必要条件,我要得到资源a的时候被阻塞,而能释放a的那个家伙又在等待我已经拥有的资源。
那么这里资源在哪呢,在printf的锁里,是什么东西,我咋不知道呢??

众所周知,操作系统支持多线程,而且标准IO是有缓冲的,显然stdout是全局资源(文件是属于进程的资源,是所有线程共享的,就像地址空间一样). 那么多线程printf的时候会发生什么?很显然,缓冲区的概念(消费者和生产者的故事应该知道)出来了,必须得有锁,否则缓冲区可能会出问题的(一个打印aaa,一个打印bbb,最后有可能打印aabbba)
这样的锁如果有,就表明printf是线程安全的,否则就不是线程安全的!

显然如果标准库的printf一族如果不是线程安全的,那么线程中就得自己加锁了,多麻烦的事情(虽然C标准没有线程的概念,但是我的标准库是线程安全的);

这样printf的行为大概为
int printf(const char *fmt,...)
{
    /**/
    加锁
tag1:
    操作缓冲,如果必要write(1,buf,size);
    释放锁
    /**/
}
这个锁肯定是所有线程可见的,这个函数不可重入,在信号处理中调用的时候,被中断的地方可能正是加锁和解锁之间的位置,这样信号处理中的printf进行同样的加锁解锁过程,因为那时候它已经拥有了锁(在被打断的时候),再去要锁,那么肯定得不到,信号处理中的printf永远阻塞在那锁的获取代码上,永远不会返回,信号处理函数也就永远不会返回,显然死锁了

strace ./a.out 会发现阻塞在futex上,这个系统调用正是我的系统上锁的实现所使用的(pthread_mutex_lock)
目前的死锁还只是这个线程,当别的线程调用printf时,也死锁了

总结一下
信号处理中不能调用不可重入函数,带有锁的函数是不可重入的
printf是线程安全的,同理,malloc也是,只是malloc操作地址空间,printf操作文件而已,都是全局资源,都有锁的

这只是个我遇到的情况,


共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]