| 
 
									 
										
 
 
#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操作文件而已,都是全局资源,都有锁的
 
 这只是个我遇到的情况,
 |