免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1034 | 回复: 0
打印 上一主题 下一主题

信号可重入线程安全 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2007-11-13 20:49 |只看该作者 |倒序浏览

之所以把这几个概念放一起,是因为它们组合在一起容易出现一些莫名其妙的错误,而且一旦出现,还很难被发现。更糟糕的是它们的出现需要一定的时间,并不是非常容易重现的,而且需要了解的比较多才能更好的理解它们发生的原因。
这里要用例子阐述一下。
信号的是UNIX系统上是最原始的进程间通信方式之一(参考>以及>),信号本身不能携带任何的数据,只能通知别的进程表示某个事件,比如Ctrl-C通常只表示"我想中断你的执行"。
进程可以捕获除了,SIGSTOP,SIGKILL之外的任何信号,比如"当有人想中断我的执行请让我关闭我的文件".这两个信号保证进程在出问题的时候是可以控制的(不然它就是不退出,咋办?),如果信号被捕获,那么执行的那个地方就被暂时中断了,直到信号处理函数返回,才接着刚才被中断的地方执行;
可重入的概念就是一段代码可以被中断执行,然后又有一个新的执行流在执行它。可重入函数不能拥有公共资源。
信号可以由进程发送,也可以由内核发送,但是这里和是谁发送的无关,所以给的例子如下:
               
               
               
#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操作文件而已,都是全局资源,都有锁的
这只是个我遇到的情况,详细可以参考apue


本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u1/43233/showart_421573.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP