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

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

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

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

  在程序清单 1 中,信号函数与主函数都会访问 crit_value这一临界资源,于是在访问这一共享资源之前,用锁进行访问的互斥(实际上,printf函数也是不可重入的,也意味着主程序与信号函数对它的访问同样需要注意临界区的问题,如果出于安全性的考虑,应该使用 write 替换 printf,在本文中它不是讨论的主要目的,请读者不必对 printf较真)。编译并运行该程序(编译时需要连接 rt 库,以及给 gcc 增加参数 -lrt),如果不做任何动作,则程序在 20 秒之后退出,并输出:

xxx@xxx-desktop:~$ ./test
main thread started, use "kill -35 18283" to trigger dead lock. \
Otherwise, program will exit within 10secs.
main thread, crit_value = 1.
main thread, job done.

  若按照程序的指示在前 10 秒内用‘ kill ’向本程序发送信号 35(注,用户可能得到的信号值不一定是 35)。程序将无法自行退出,输出为:

xxx@xxx-desktop:~$ ./test &

xxx@xxx-desktop:~$ kill -35 18466
enter signal handler. If there is no exit message, a dead lock happened.

  此时程序进入死锁状态,需要用 kill或者 Ctrl+C强制使程序退出。该死锁发生的机制可以进行如下解释:如图 1 所示,当主程序请求并持有锁(sem_lock),开始做一些工作时。如果此时有信号的发生,主程序会被中断执行并跳转至信号函数 signal_test_func执行。进入信号函数之后,由于信号函数也需要访问共享资源,进而请求锁,由于锁仍然被主程序持有,信号函数就会一直等待锁的释放。然而,因为主程序的运行已经被信号函数抢占,在信号函数完成之前无法运行,也就无法继续执行解锁动作,于是信号函数在请求锁时变成了死等待。

图 1. 在信号函数与主函数间加锁

  方案 1,使用测试加锁

  如何解决这种问题呢?第一种思路可以将信号函数的加锁动作替换为测试加锁动作,例如使用:

int sem_trywait(sem_t *sem);
int sem_timedwait(sem_t *restrict sem, const struct timespec *restrict abs_timeout);

  该函数是 sem_wait的非阻塞版本,如果加锁失败或超时则返回 -1。使用 sem_trywait修改函数 signal_test_func如下:

  清单 2. 使用 sem_trywait 代替 sem_wait

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

if ( sem_trywait( &semlock ) != 0 )
{
msg = "exit signal handler. Lock failed\n";
write( 1, msg, strlen(msg));
return;
}
// do something here.
crit_value = 0;
printf( "signal handled, crit_value = %d. \n", crit_value );
sem_post( &semlock );

}

42/4<1234>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

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

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

沪ICP备05003035号

沪公网安备 31010102002173号