Linux实时信号程序中锁的探索

发表于:2010-9-17 11:43

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:未知    来源:51Testing软件测试网采编

  前言

  Linux 下的信号分为可靠信号和不可靠信号,或称为实时信号和非实时信号,对应于 Linux 的信号值为 1-31 和 34-64。对于他们的分类以及应用的时的区分并不在本文的讨论范围之内,读者可参考文献 1,对其应用做初步的了解。本文仅针对在应用实时信号处理函数时,如何解决其重入问题进行一些探索。

  Linux 下的实时信号更类似于软件层次的“中断”,它可能发生在任何时刻,而这与程序的运行必然存在一定的冲突,即重入性问题。在一次应用 Linux RT 信号编写程序的过程中,碰见了这个问题,尽管这是个老话题,几乎所有的文章都强调,不要将不可重入的代码段置于信号处理函数中,但是由于特定场合的需要,在一个信号函数中无法避免地处理某个临界区。在一般情况下,在处理多线程临界区时,采用加、解锁的办法达到对临界区串行的访问目的,是最简单、实用的解决方法。那么有没有办法,通过使用的锁来解决信号函数的重入的问题呢?答案是可以的,但是在信号函数中要格外注意锁的正确用法。本文将针对这种较为特殊的状况,介绍相应的处理方案,由于 LinuxThread 和 NPTL 在信号处理上的不同,本文仅讨论 NPTL 的情况。

  另外本文在提及的所有代码可在下载中获取。

  信号函数与主程序间潜在的重入性问题

  在 Linux 中,对于实时信号,推荐使用如下函数装载信号函数:

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

  其中,sigaction 的定义如下:

struct sigaction {
void     (*sa_handler)(int);
void     (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t   sa_mask;
int        sa_flags;
void     (*sa_restorer)(void);
};

  对于实时信号而言,在实时信号抵达时,如果该信号不在 sa_mask指定的信号集之内,即信号未被屏蔽,信号函数 sa_sigaction才会被执行。反之,如果实时信号存在于 sa_mask指定的信号集中,该信号会被加入到该进程的信号队列中,直到进程解除对该信号的屏蔽。由于实时信号的默认动作是打出该信号值,并退出当前进程,用户还需根据自己的实际情况屏蔽不必要的信号,可以使用下面方法在 sa_mask里面添加信号:

struct sigaction act;
sigemptyset( & act.sa_mask);                 // empty signal set,
sigaddset( & act.sa_mask, SIGRTMIN+1 );    // add SIGRTMIN+1 to set.
sigaddset( & act.sa_mask, SIGRTMIN+2 );

  …

  在默认情况下,在 sa_flags中不会设置 SA_NODEFER选项,当进程正在处理信号时,若再有新信号(相同信号)抵达,该信号会加入到进程的信号队列中,直到进程的信号处理过程结束;反之,程序会立即处理新信号。如果用户确定自己的信号函数可重入,也可以在 sa_flags里面设置 SA_NODEFER选项,此选项可以使得信号函数在收到信号时被立即执行。但是当到达的信号达到一定的频度之后,程序将因为无法及时处理信号函数而导致信号函数套嵌,积累到一定程度就会出现问题。

  在默认的情况下,当内核派发信号到这个程序时,信号函数会按照串行化方式被执行。由于信号的发生是非预期的,它可能在主程序运行到任何时刻发生,而信号函数的执行总是以中断当前主程序的运行为代价的,也可以认为,执行信号的进程与主程序进程本身就是同一个进程,但是它们并非是按照一定顺序顺次执行的。设想,如果主程序正在处理一个临界区,而到来的信号函数也要处理同一个临界区,就会面临类似于多线程程序的重入性问题。在普通的多线程应用中,各个线程之间的关系是并行的。我们一般可以用锁来解决这种问题,但是,如果简单的将这种方案引入信号函数处理过程中,就会出现一个比较讨厌的问题——死锁:

  清单 1. 信号函数与主程序间的死锁

void signal_test_func( int signo, siginfo_t * siginfo, void * ptr )
{

sem_wait( &semlock );  // acquire lock
// do something here.
crit_value = 0;
printf( "signal handled, crit_value = %d. \n", crit_value );
sem_post( &semlock );      // release lock

}
int main()
{
// create semaphone lock init value = 1

// link signal handler

sem_wait( &semlock );         // acquire lock
// use sleep to simulate main thread is doing some work.
crit_value = 1;
sleep(10);
printf( "main thread, crit_value = %d. \n", crit_value );
sem_post( &semlock );         // release lock
sleep(10);
printf( "main thread, job done. \n", crit_value );
}

41/41234>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号