linux信号的信号处理机制

信号是linux编程的一个非常重要的部分。本文将介绍信号机制的基本概念,linux信号机制的实现方法,信号的使用方法和信号的几种系统调用。



信号机制是一种互相传递消息的方法。这个信号被称为软中断信号,这个人也称为软中断,从它的命名可以看出它的本质和用途受到很大的干扰,所以信号可以说是过程控制的一部分。



一、信号的基本概念



本节介绍了信号的一些基本概念,然后给出了一些基本的信号类型和与事件相对应的信号,这些基本概念对于理解和使用信号,对于理解信号机制特别重要,现在让我们来看看信号是什么。



1。基本概念



软中断信号(信号,称为信号)是用来通知一个异步事件的过程。过程可以通过一个系统调用杀软中断信号发送给对方。内核还可以发送信号给内部的事件过程中,通知的过程,事件发生。注意信号仅用于通知处理什么样的事件时有发生,而且它不会传递任何数据的过程。



接收信号的过程中对各种信号的不同处理方法,处理方法可分为三种:第一种是一个类似的中断处理程序,并进行信号处理,本程序可以指定处理函数,由该函数处理。另一种方法是忽略了一个信号,什么也不做的信号似乎并没有发生。第三种方式是通过处理信号保留系统默认值。大多数信号的默认操作是终止进程,进程调用信号来指定某一进程上某一信号的处理行为。



有一个软中断信号在进程表中的表项,每个点对应一个信号,当信号被发送给进程,它对应于定位点。你可以看到,过程可以使不同的信号在同一时间,但对于相同的信号,过程中不知道有多少人来之前的过程。



2。信号类型



信号的原因有很多,这是根据信号的原因简单地分类来理解各种信号的。



(1)要终止的进程有关的信号。当进程退出,或者子进程终止,这种信号发送出去。



(2)与处理异常相关的信号,例如跨越边界的进程,或试图写入只读存储器区域(例如程序主体),或执行特权指令和其他硬件错误。



(3)与系统调用期间遇到的不可恢复条件相关联的信号。当调用系统时,原始资源已被释放,当前系统资源已用尽。



(4)在执行系统调用时与非预测错误条件相关联的信号,例如执行不存在的系统调用。



(5)进程在用户状态下产生的信号。如果进程调用系统调用杀送信号到其他进程。



(6)与终端交互相关联的信号。如果用户关闭终端,或按下中断键,等等。



(7)跟踪进程执行的信号。



Linux支持的信号列表如下:许多信号与机器的体系结构有关。上市在POSIX信号1是首次上榜:



信号值处理的原因。



----------------------------------------------------------------------



1终端控制进程挂起或终止进程



SIGINT 2键盘中断(如断键按下)



3键盘的SIGQUIT退出键被按下



sigill 4 C非法指令



sigabrt 6 C出口指示中止(3)



8 C语言浮点异常迷惑



9了解信号SIGKILL杀死



11 C为SIGSEGV无效的内存引用



sigpipe 13管道破裂:没有读写管道端口



14报警信号SIGALRM信号从(2)



15终止信号SIGTERM



用户自定义30,10,16 SIGUSR1信号1



用户自定义31,12,17 SIGUSR2信号2



子进程结束信号SIGCHLD 20,17,18 B



这19,18,25 SIGCONT过程持续(过程已停止)



SIGSTOP 17,19,23 DEF终止过程



按停止键SIGTSTP 18,20,24 D控制终端(TTY)



的sigttin 21,21,26 D守护进程试图从控制终端读取



的sigttou 22,22,27 D守护进程试图从控制终端写



以下信号不列入POSIX。1,但上市susv2



信号值处理的原因。



出版社



SIGBUS 10,7,10 C总线错误(错误的内存访问)



通过sigpoll系统V定义的可移植的事件,等同于SIGIO



sigprof 27,27,29仿形定时器



sigsys 12,-,-,12 - C无效的系统调用(SVID)



sigtrap 5 C /断点跟踪捕获



sigurg 16,23,21 B座紧急情况(4.2 BSD)



sigvtalrm 26,26,28实时报警时钟信号(4.2 BSD)



24,24,30 sigxcpu C超过设定的CPU时间限制(4.2 BSD)



SIGXFSZ 25,25,31 C超过设定的文件大小限制(4.2 BSD)



(为sigsys,sigxcpu,SIGXFSZ,和SIGBUS一些机器架构,Linux下的默认行为是(终止),susv2是C(终止和终止)。



下面是一些其他信号



信号值处理的原因。



----------------------------------------------------------------------



sigiot 6 C IO捕获指令,sigabrt的同义词



sigemt 7,-,7



sigstkflt -,16 -一个协处理器堆栈错误



一个SIGIO 23,29,22我/ O操作现在可以(4.2 BSD)



sigcld - -,18个是同义的SIGCHLD



电源故障信号29,30,19一断电(系统V)



而言进一步有用29,,-一个具有电源故障信号



siglost -,-,-一个文件锁定损失



sigwinch 28,28,20 B窗口大小变化(4.3 BSD,太阳)



sigunused -,31,-一个未使用的信号(将sigsys)



(在这里,这意味着信号不落实;三价值观的含义是,第一个值通常是有效的在α和SPARC,中间值对应于i386和PPC和SH,和最后的值对应于MIPS。信号29是α,而言进一步有用/电源故障信号和siglost SPARC。)



动作项中字母表的含义如下



a的默认动作是终止进程。



B的默认操作是忽略这个信号。



C的默认操作是终止进程并执行内核映像转储(转储核心)。



D的默认动作是停止进程。



无法捕获E信号



f信号不能忽略



上述信号由常见的系统支持。在一个表中,形式的名字,介绍了功能和信号的行动。所有违约行为意味着程序的终止是指退出过程;忽略该信号是信号丢弃,不做处理;停止该程序是指该程序挂起,进入停止状态会很快恢复,一般是在调试的过程中(如ptrace系统调用);内核转储是指在记忆和图像在内核结构处理存储在一定的格式转储到文件系统中的数据处理部分,和退出的过程,这样做的的好处是为编程提供方便,使他们能够得到的过程执行时间时的数据值,允许他们确定原因,并可以调试转储。他们的计划。



注意信号SIGKILL和SIGSTOP既不能被捕获或忽略。信号sigiot和sigabrt是一个信号,可以看出,相同的信号可以在不同的系统不同,所以最好是使用的名称定义为信号而不是直接使用的信号值。









二、信号机制



在最后一节中,介绍了信号的基本概念。在本节中,我们将介绍如何在内核实现的信号机制,如何在内核发送信号的一个过程,这个过程是如何接收信号,如何处理控制信号的响应,当内核处理,以及如何处理该信号的接收过程也介绍。setjmp和longjmp信号中的作用。



1。信号核的基本处理方法



通过内核发送软中断信号处理的方法是设置点对应于信号中的进程表项的信号域。在这里补充的是,如果信号传输到睡眠的过程,然后看看睡眠进程优先,如果睡在优先级进程的中断,然后唤醒过程;否则只能设置在对应的信号域的进程表,而不是唤醒的过程,这是更重要的,由于时间过程检查是否接收信号是一个过程,回来从核心到用户状态当它来临的时候,或当一个进程要进入或离开一个适当的低优先级睡眠状态。



内核的处理过程中接收到的信号是当进程返回用户状态从内核态的时间。所以,当一个进程运行在内核态,软中断信号不马上工作,直到它将返回到用户态处理。过程只会返回给用户状态时的信号处理过程,并不会在用户状态处理信号。



内核处理软中断的进程上下文中处理接收信号,所以过程必须处于运行状态。我介绍的概念的时候,我提到有三种类型的信号处理的过程是:接收信号后撤回;过程忽略了信号接收的过程;信号后,执行用户设定的系统呼叫信号的功能。当一个进程接收到一个信号,它忽略了,过程中丢弃的信号,如果没有收到的信号。如果一个进程接收到信号采集、过程执行用户定义的函数时,进程返回用户状态内核态和用户定义的函数的执行。方法很聪明,核心是建立用户栈的一个新的层,该层将设置返回地址的值转换为用户定义的函数的地址,所以过程从内核返回返回到堆栈的顶部,当用户定义函数,然后返回到栈顶的从函数,它返回到原来的地方到内核中。这样做的原因是用户定义的处理功能不能、也不允许在内核态执行(如果用户定义的函数在内核态,运行用户可以得到任何权限)。



在信号处理过程中,有几点要注意:第一,在一些系统中,当一个进程完成处理中断信号返回用户删除内核态,用户区中的地址设置信号处理程序,即对信号处理方法改为默认值,除非在下一次信号到来之前再次使用系统调用的信号。这可能会导致程序在调用信号得到的信号。在BSD,核心不再清除地址。但不清除这个地址可能造成进程栈溢出是因为它太快了得到一个信号。为了避免上述情况,在BSD系统中,内核模拟处理硬件中断,也就是防止在处理中断时接收新的中断类型。



其次要注意的是,如果信号采集过程中发生的是一个系统调用,和睡眠过程中的中断优先级,然后由longjmp过程引起的信号,跳出睡眠,返回用户状态和执行信号处理程序,当返回的信号处理程序,该过程就像是从系统调用返回,但返回错误码,说明系统调用被中断。应当指出的是,BSD内核可以自动重启系统调用。



要注意的第三件事是,如果过程的中断优先级的睡着了,当它接收到一个信号被忽略,过程被唤醒,但不是longjmp,通常继续睡觉。但用户并不觉得过程已被唤醒,但如果它没有发生。



注意第四件事:到sigcld信号处理方法的核心是不同于其他的信号。当进程查看终止进程接收到的信号,默认情况下,这个过程是没有收到信号似的,如果父进程执行的系统调用等,从系统调用过程等醒来,回到等待调用,执行一系列等后续操作调用(找到死孩子过程和释放子进程表),然后返回从等待的sigcld信号的功能是唤醒睡眠过程对中断优先级。如果过程捕获这个信号,它去处理日常就像普通的信号处理。如果过程忽略该信号,那么系统呼叫等待行动有些不同,因为sigcld刚刚醒来的过程,睡眠可以中断优先级,然后等待调用父进程唤醒后续操作继续呼叫等待,然后等待其他子过程。



如果一个进程调用信号系统调用,并设立一个sigcld处理方法和过程有一个死的状态子过程中,内核会向进程发送一个sigcld信号。



2的作用,setjmp和longjmp



在信号处理机制之前的介绍,多次提到了setjmp和longjmp,但他们没有仔细描述它们的功能和实现方法。下面是一个简短的介绍。



当我们介绍的信号,我们看到,许多地方需要过程返回直接从原来的系统调用后检查收到的信号,而不是等到完成呼叫。这一过程中突然改变其上下文的上下文,使用setjmp和longjmp的结果。setjmp保存保存上下文到用户区并继续在旧的上下文执行。也就是说,执行一个系统调用的过程,因为当资源或其他原因去睡觉,内核做了setjmp的过程,如果在睡眠信号不能入睡,过程调用longjmp的过程内核,运行是原先setjmp进程的内核将存储在呼叫过程的用户上下文的当前上下文的恢复,这样的过程可以恢复之前的状态,等待资源,对于setjmp 1内核返回,系统的过程叫不知道,他们所做的事情。









三。关于信号的系统调用



前两部分已经介绍了很多关于信号的知识。在这一节中,让我们了解这些系统调用的。在这方面,系统调用信号是通过过程来设置信号处理和系统调用kill是用来将信号发送到指定的过程。这两个电话可以形成信号的基本操作,接下来两呼吁暂停报警过程停顿和定时器通过信号实现的,和报警的信号来通知进程定时器的时间。所以在这里,我们也有引进的两个电话。



1,信号系统调用



系统调用信号用于设置信号处理方法:



*信号(int Signum,无效(*处理)(int)))(int);(int);(int Signum,void(int)))(int);



在调用过程中添加以下头文件:



#包括



上述声明的格式比较复杂,如果不清楚如何使用它,你也可以使用以下类型定义的格式(POSIX定义):



typedef void(* sighandler_t)(int);



sighandler_t信号(int符号,sighandler_t处理);



但是这种格式在不同的系统中有不同类型的定义,所以使用这种格式最好是参考联机手册。



在通话中,Signum指出信号的参数设置的处理方法。二参数处理程序是一个处理函数,或是



sig_ign:忽略该信号所提到的参数符号。



sig_dfl:信号的处理方法称为恢复系数的正负号是默认值。



通过信号处理例程的整数参数是信号值,使信号处理程序处理多个信号,系统调用的返回值是以前信号处理程序指定的信号符号或错误代码sig_err当错误是错误的。这里有一个简单的例子:



#包括



#包括



#包括



无效sigroutine(int不知道){ / *信号处理例程,它不知道将得到的信号值。



开关(不知道){



案例1:



printf(得到信号SIGHUP );



打破;



案例2:



printf(得到信号SIGINT );



打破;



案例3:



printf(得到信号SIGQUIT );



打破;



}



返回;



}



int main(){



printf(进程ID为%d



(SIGHUP信号,sigroutine);一个三集 / / *以下的信号处理方法



信号(SIGINT,sigroutine);



(SIGQUIT信号,sigroutine);



为了(;;);



}



该信号也由按Ctrl-C发送,和信号SIGQUIT由按Ctrl -发送。程序的结果如下:



本地:~ / sig_test美元。



进程ID是463



得到一个信号SIGINT / /按Ctrl-C结果



得到一个信号SIGQUIT / /按Ctrl -结果



/ /压在后台进程使用



{ 1 } +停止。 / sig_test



本地:~ BG美元



{ 1 } +。 / sig_test



本地:~ $杀侠 / / 463发送SIGHUP信号处理



本地:~美元得到SIGHUP信号ndash;



杀9 / / 463处理发送SIGKILL信号传输、终止进程



本地:~美元



2,杀死系统调用



系统调用杀死用于向进程发送信号。调用声明的格式如下所示:



int杀(pid_t PID,int sig);



在调用过程中添加以下头文件:



#包括



#包括



系统调用可以用来发送任何信号的任何过程或过程组。如果参数PID是积极的,电话发送信号SIG与进程号PID的进程。如果PID等于0,那么信号信号将发送给当前进程的进程组的所有进程:如果PID参数等于1,信号的信号将被发送到所有的过程比其他过程1和本身。如果PID参数小于1,信号的信号将被发送到所有属于过程组-pid.if参数信号是0个过程,信号将不被发送。当函数执行成功,返回值是0;当错误,返回1,并设置相应的错误代码errno。这里有一些错误C可退还的颂歌:



einval:指定信号sig无效。



esrch:过程或过程的PID参数指定的组不存在。请注意,在这个过程中表的过程中,可能是一个等待尚未恢复,但终止死者的实施过程。



eperm:过程没有权力到指定接收信号的进程发送信号。因为过程是允许发送信号给进程的PID,它必须有根的力量,或UID或euid调用过程是作为指定的进程或用户ID相同的UID(savedset用户ID)。如果PID参数小于1,即信号发送到一个组,在组成员的过程错误表示不能接收信号。



3,暂停系统调用



系统调用暂停的功能是等待信号。调用的声明格式如下所示:



暂停(无效);



在调用过程中添加以下头文件:



#包括



这个电话让调用进程睡眠直到收到一个信号,电话总是返回1,设置错误代码eintr(接收信号)。下面是一个简单的例子:



#包括



#包括



#包括



无效sigroutine(int闲置){



printf(捕捉信号SIGINT );



}



int main(){



信号(SIGINT,sigroutine);



停顿();



printf(接收信号);



}



在这种情况下,程序开始执行,好像它进入一个死循环,这是因为进程在等待信号。当我们按Ctrl-C,信号被暂停了等待状态。









4、报警和setitimer系统调用



系统调用报警的功能是设置定时器。当计时器到达时,一个信号将被发送到进程:



无符号整数警报(无符号int秒);



在调用过程中添加以下头文件:



#包括



系统报警设置内核后指定的秒到调用进程发送一个SIGALRM信号。如果指定的参数是0秒,SIGALRM信号不再发送。最后设置将取消最后一次调用返回值是剩下的最后一次调用之间传递0,或者因为以前没有时间打电话。



注意,在使用中,只设置警报发送一个单一信号,如果您要多次发送警报,则多次使用警报呼叫。



为了报警,这里没有更多的例子。系统中的许多程序不再使用报警呼叫。相反,他们使用setitimer设置定时器和使用getitimer得到定时器的状态。这两个语句有以下格式。



Int getitimer(int,结构itimerval *值);



(int,int setitimer,const struct itimerval *值,结构itimerval * ovalue);



在使用这两个调用过程中添加以下头文件:



#包括



系统调用为进程提供三个定时器,每个进程都有自己的时间域。当它们到达时,它们向进程发送相应的信号,并使计时器再次启动。三定时器是由参数指定的,如下所示:



timer_real:计时时间,时间的到来将向进程发送SIGALRM信号。



itimer_virtual:只有当进程执行是时间。时间的到来将给进程发送信号的sigvtalrm。



itimer_prof:时间定时执行该过程时,系统执行动作的过程。它是一种对itimer_vir-tual,用来计数在用户态和内核态的时间。时间的到来将给进程发送信号的sigprof。



定时器中的参数值用来指示定时器的时间,其结构如下所示:



结构itimerval {



当前it_interval结构; / *的下一个值



当前it_value结构; / * * /这个设定值



};



当前的结构定义如下:



当前结构{



长tv_sec; / * * /秒



长tv_usec; / * * / = 1微秒,1000000微秒



};



在setitimer调用,如果参数ovalue不是空的,它是保留的最后调用的值。当定时器it_value减小0,生成一个信号的it_value值设置的it_interval值,然后定时重新启动,如此往复。当的it_value设置为0,定时器停止,或当定时器到期时,和it_interval停在0。当调用成功,返回0;当错误,返回1,并设置相应的错误代码错误:



-EFAULT:参数值或ovalue是无效的指针。



einval:参数不一itimer_real,itimer_virt,或itimer_prof。



这里是一个简单的演示了setitimer调用。在这种情况下,SIGALRM信号发出的每一秒,和sigvtalrm信号发送每0.5秒。



#包括



#包括



#包括



#包括



int秒;



无效sigroutine(int Signo){



开关(该){



案例SIGALRM信号:



printf(抓信号SIGALRM信号);



打破;



案例sigvtalrm:



printf(捕捉信号- sigvtalrm );



打破;



}



返回;



}



int main(){



结构itimerval价值,ovalue,value2;



秒=5;



printf(进程ID为%d



(SIGALRM信号,sigroutine);



信号(sigvtalrm,sigroutine);



value.it_value.tv_sec = 1;



value.it_value.tv_usec = 0;



value.it_interval.tv_sec = 1;



value.it_interval.tv_usec = 0;



setitimer(itimer_real,价值,ovalue);



value2.it_value.tv_sec = 0;



value2.it_value.tv_usec = 500000;



value2.it_interval.tv_sec = 0;



value2.it_interval.tv_usec = 500000;



setitimer(itimer_virtual,value2,ovalue);



为了(;;);



}



此示例的屏幕副本如下所示:



本地:~ / timer_test美元。



进程ID是579



捕捉信号ndash;sigvtalrm



抓住一个SIGALRM信号ndash;



捕捉信号ndash;sigvtalrm



捕捉信号ndash;sigvtalrm



抓住一个SIGALRM信号ndash;



捕捉信号ndash;gvtalrm