Linux多线程应用中如何编写安全的信号处理函数

上传人:博****1 文档编号:469786278 上传时间:2023-10-16 格式:DOCX 页数:13 大小:42.15KB
返回 下载 相关 举报
Linux多线程应用中如何编写安全的信号处理函数_第1页
第1页 / 共13页
Linux多线程应用中如何编写安全的信号处理函数_第2页
第2页 / 共13页
Linux多线程应用中如何编写安全的信号处理函数_第3页
第3页 / 共13页
Linux多线程应用中如何编写安全的信号处理函数_第4页
第4页 / 共13页
Linux多线程应用中如何编写安全的信号处理函数_第5页
第5页 / 共13页
点击查看更多>>
资源描述

《Linux多线程应用中如何编写安全的信号处理函数》由会员分享,可在线阅读,更多相关《Linux多线程应用中如何编写安全的信号处理函数(13页珍藏版)》请在金锄头文库上搜索。

1、Linux多线程应用中如何编写安全的信号处理函数Linux 多线程应用中如何编写安全的信号处理函数 简介: 关于代码的可重入性,设计开发人员一般只考虑到线程安全,异步信号处理函数的安全却往往被忽略。本文首先介绍如何编写安全的异步信号处理函数;然后举例说明在多线程应用中如何构建模型让异步信号在指定的线程中以同步的方式处理。 Linux 多线程应用中编写安全的信号处理函数 在开发多线程应用时,开发人员一般都会考虑线程安全,会使用 pthread_mutex 去保护全局变量。如果应用中使用了信号,而且信号的产生不是因为程序运行出错,而是程序逻辑需要,譬如 SIGUSR1、SIGRTMIN 等,信号在

2、被处理后应用程序还将正常运行。在编写这类信号处理函数时,应用层面的开发人员却往往忽略了信号处理函数执行的上下文背景,没有考虑编写安全的信号处理函数的一些规则。本文首先介绍编写信号处理函数时需要考虑的一些规则;然后举例说明在多线程应用中如何构建模型让因为程序逻辑需要而产生的异步信号在指定的线程中以同步的方式处理。 回页首 线程和信号 Linux 多线程应用中,每个线程可以通过调用 pthread_sigmask 设置本线程的信号掩码。一般情况下,被阻塞的信号将不能中断此线程的执行,除非此信号的产生是因为程序运行出错如 SIGSEGV;另外不能被忽略处理的信号 SIGKILL 和 SIGSTOP

3、也无法被阻塞。 当一个线程调用 pthread_create 创建新的线程时,此线程的信号掩码会被新创建的线程继承。 POSIX.1 标准定义了一系列线程函数的接口,即 POSIX threads(Pthreads)。Linux C 库提供了两种关于线程的实现:LinuxThreads和 NPTL(Native POSIX Threads Library)。LinuxThreads已经过时,一些函数的实现不遵循POSIX.1 规范。NPTL 依赖 Linux 2.6 内核,更加遵循 POSIX.1 规范,但也不是完全遵循。 基于 NPTL 的线程库,多线程应用中的每个线程有自己独特的线程 ID

4、,并共享同一个进程ID。应用程序可以通过调用kill(getpid,signo) 将信号发送到进程,如果进程中当前正在执行的线程没有阻碍此信号,则会被中断,线号处理函数会在此线程的上下文背景中执行。应用程序也可以通过调用 pthread_kill(pthread_t thread, int sig) 将信号发送给指定的线程,则线号处理函数会在此指定线程的上下文背景中执行。 基于LinuxThreads的线程库,多线程应用中的每个线程拥有自己独特的进程 ID,getpid 在不同的线程中调用会返回不同的值,所以无法通过调用 kill(getpid,signo) 将信号发送到整个进程。 下文介绍的

5、在指定的线程中以同步的方式处理异步信号是基于使用了 NPTL 的 Linux C 库。请参考“Linux 线程模型的比较:LinuxThreads 和 NPTL”和“pthreads(7) - Linux man page”进一步了解 Linux 的线程模型,以及不同版本的 Linux C 库对 NPTL 的支持。 回页首 编写安全的异步信号处理函数 信号的产生可以是: 用户从控制终端终止程序运行,如Ctrk + C 产生 SIGINT; 程序运行出错时由硬件产生信号,如访问非法地址产生 SIGSEGV; 程序运行逻辑需要,如调用 kill、raise 产生信号。 因为信号是异步事件,即信号处

6、理函数执行的上下文背景是不确定的,譬如一个线程在调用某个库函数时可能会被信号中断,库函数提前出错返回,转而去执行信号处理函数。对于上述第三种信号的产生,信号在产生、处理后,应用程序不会终止,还是会继续正常运行,在编写此类信号处理函数时尤其需要小心,以免破坏应用程序的正常运行。关于编写安全的信号处理函数主要有以下一些规则: 信号处理函数尽量只执行简单的操作,譬如只是设置一个外部变量,其它复杂的操作留在信号处理函数之外执行; errno 是线程安全,即每个线程有自己的 errno,但不是异步信号安全。如果信号处理函数比较复杂,且调用了可能会改变errno 值的库函数,必须考虑在信号处理函数开始时保

7、存、结束的时候恢复被中断线程的 errno 值; 信号处理函数只能调用可以重入的 C 库函数;譬如不能调用 malloc,free以及标准 I/O 库函数等; 信号处理函数如果需要访问全局变量,在定义此全局变量时须将其声明为 volatile,以避免编译器不恰当的优化。 从整个 Linux 应用的角度出发,因为应用中使用了异步信号,程序中一些库函数在调用时可能被异步信号中断,此时必须根据errno的值考虑这些库函数调用被信号中断后的出错恢复处理,譬如socket 编程中的读操作: rlen = recv(sock_fd, buf, len, MSG_WAITALL); if (rlen = -

8、1) & (errno = EINTR) / this kind of error is recoverable, we can set the offset change /rlen as 0 and continue to recv 回页首 在指定的线程中以同步的方式处理异步信号 如上文所述,不仅编写安全的异步信号处理函数本身有很多的规则束缚;应用中其它地方在调用可被信号中断的库函数时还需考虑被中断后的出错恢复处理。这让程序的编写变得复杂,幸运的是,POSIX.1 规范定义了sigwait、sigwaitinfo 和pthread_sigmask 等接口,可以实现: 以同步的方式处理异步信

9、号; 在指定的线程中处理信号。 这种在指定的线程中以同步方式处理信号的模型可以避免因为处理异步信号而给程序运行带来的不确定性和潜在危险。 sigwait sigwait 提供了一种等待信号的到来,以串行的方式从信号队列中取出信号进行处理的机制。sigwait只等待函数参数中指定的信号集,即如果新产生的信号不在指定的信号集内,则 sigwait继续等待。对于一个稳定可靠的程序,我们一般会有一些疑问: 多个相同的信号可不可以在信号队列中排队? 如果信号队列中有多个信号在等待,在信号处理时有没有优先级规则? 实时信号和非实时信号在处理时有没有什么区别? 笔者写了一小段测试程序来测试 sigwait

10、在信号处理时的一些规则。 清单 1. sigwait_test.c #include #include #include #include #include void sig_handler(intsignum) printf(Receive signal. %dn, signum); void* sigmgr_thread sigset_twaitset, oset; int sig; intrc; pthread_tppid = pthread_self; pthread_detach(ppid); sigemptyset(&waitset); sigaddset(&waitset, SI

11、GRTMIN); sigaddset(&waitset, SIGRTMIN+2); sigaddset(&waitset, SIGRTMAX); sigaddset(&waitset, SIGUSR1); sigaddset(&waitset, SIGUSR2); while (1) rc = sigwait(&waitset, &sig); if (rc != -1) sig_handler(sig); else printf(sigwaitinfo returned err: %d; %sn, errno, strerror(errno); int main sigset_tbset, o

12、set; int i; pid_tpid = getpid; pthread_tppid; sigemptyset(&bset); sigaddset(&bset, SIGRTMIN); sigaddset(&bset, SIGRTMIN+2); sigaddset(&bset, SIGRTMAX); sigaddset(&bset, SIGUSR1); sigaddset(&bset, SIGUSR2); if (pthread_sigmask(SIG_BLOCK, &bset, &oset) != 0) printf(! Set pthread mask failedn); kill(pi

13、d, SIGRTMAX); kill(pid, SIGRTMAX); kill(pid, SIGRTMIN+2); kill(pid, SIGRTMIN); kill(pid, SIGRTMIN+2); kill(pid, SIGRTMIN); kill(pid, SIGUSR2); kill(pid, SIGUSR2); kill(pid, SIGUSR1); kill(pid, SIGUSR1); / Create the dedicated thread sigmgr_thread which will handle signals synchronously pthread_creat

14、e(&ppid, NULL, sigmgr_thread, NULL); sleep(10); exit (0); 程序编译运行在 RHEL4 的结果如下: 图 1. sigwait测试程序执行结果 从以上测试程序发现以下规则: 对于非实时信号,相同信号不能在信号队列中排队;对于实时信号,相同信号可以在信号队列中排队。 如果信号队列中有多个实时以及非实时信号排队,实时信号并不会先于非实时信号被取出,信号数字小的会先被取出:如 SIGUSR1会先于 SIGUSR2 (12),SIGRTMIN会先于 SIGRTMAX (64),非实时信号因为其信号数字小而先于实时信号被取出。 sigwaitinf

15、o 以及 sigtimedwait 也提供了与 sigwait 函数相似的功能。 Linux 多线程应用中的信号处理模型 在基于 Linux 的多线程应用中,对于因为程序逻辑需要而产生的信号,可考虑调用 sigwait使用同步模型进行处理。其程序流程如下: 1. 主线程设置信号掩码,阻碍希望同步处理的信号;主线程的信号掩码会被其创建的线程继承; 2. 主线程创建信号处理线程;信号处理线程将希望同步处理的信号集设为 sigwait的第一个参数。 3. 主线程创建工作线程。 图 2. 在指定的线程中以同步方式处理异步信号的模型 代码示例 以下为一个完整的在指定的线程中以同步的方式处理异步信号的程序。 主线程设置信号掩码阻碍 SIGUSR1 和 SIGRTMIN 两个信号,

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 建筑/环境 > 施工组织

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号