




免费预览已结束,剩余138页可下载查看
下载本文档
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
Linux环境高级编程,第五讲信号,第五讲信号,信号的概念signal函数不可靠信号与可靠信号信号的发送接收机制信号集与可靠信号机制sigsetjmp和siglongjmp函数abort函数,第五讲信号,信号的概念signal函数不可靠信号与可靠信号信号的发送接收机制信号集与可靠信号机制sigsetjmp和siglongjmp函数abort函数,5,信号的概念,基本概念信号的产生信号的处理方式常见信号信号与文字说明的转换,6,信号的概念,当某个程序长时间运行,如何中断它的执行,让其退出?示例5.8Ctrl+c中断程序执行是如何中断程序执行的呢?,7,信号的概念,信号是软件中断。例如,Ctrl+C,将产生SIGINT信号,中断程序执行它可以作为进程间通信的一种方式,但更主要的是,信号总是中断一个进程的正常运行,它更多地被用于处理一些非正常情况每个信号都有一个名字,以SIG开头。SIGABRT:进程异常终止信号,abort产生SIGALRM:闹钟信号,计时器超时后产生Linux2.4.22共有31种不同的信号查看头文件/usr/include/bits/signum.h,8,信号的产生,很多条件可以产生一个信号当用户按某些终端键时,产生信号。例如在终端上按Ctrl+C键通常产生中断信号(SIGINT)。硬件异常产生信号:除数为0、无效的存储访问等等。这些条件通常由硬件检测到,并将其通知内核。然后内核为该条件发生时正在运行的进程产生适当的信号。进程用Kill函数可将信号发生给另一个进程或进程组;用户用Kill命令将信号(终止信号SIGTERM)发送给其他进程当检测到某种软件条件已经发生,并将其通知有关进程时产生信号。如SIGPIPE、SIGALRM等等,9,信号的概念,基本概念信号的产生信号的处理方式常见信号信号与文字说明的转换,10,进程对信号的处理方式,进程对信号的处理方式可以有三种:忽略此信号。大多数信号都可使用这种方式进行处理,但有两种信号却不能被忽略。它们是:SIGKILL和SIGSTOP。这两种信号不能被忽略的原因时:它们向超级用户提供了一种使进程终止或停止的可靠方法;捕捉信号。为了做到这一点要通知内核在某种信号发生时,调用一个用户函数。在用户函数中,可执行用户希望对这种事件进行的处理。执行系统默认动作。对大多数信号的系统默认动作是终止该进程。,11,常见的信号,SIGABRT处理异常信号。当前进程调用abort后发送该信号;SIGALRM报时时钟。当进程设置的时钟超时后,内核向进程发送一个时钟信号;SIGCHLD子进程被终止或停止。每当子进程被终止或停止时,内核都会给父进程发送一个信号SIGCHLD,该信号表示的是一个子进程的消亡。SIGHUP挂起信号。当终端连接断开时,内核向所有依附在此控制终端上的进程发送此信号;SIGINT中断信号。当用户按下中断键时,从内核向所有与终端会话有联系的进程发出信号。,12,常见的信号(续),SIGPIPE写无接收方的管道或套接字信号。管道和套接字都是一种进程间通讯机制;SIGQUIT退出信号。与SIGINT信号非常类似,当用户在终端上敲下退出键时,内核会发出这个信号。与SIGINT不同点在于,该信号会导致进程的异常终止。SIGTTIN/SIGTTOU后台进程读/写信号。每当一个后台进程试图从控制终端读入/写数据时就会产生此信号,此信号的默认动作是终止进程的运行。SIGURG高带宽数据通知信号。该信号通知进程,网络连接中出现了紧急情况或者带外数据;SIGUSR1和SIGUSR2用户自定义信号。,13,信号的概念,基本概念信号的产生信号的处理方式常见信号信号与文字说明的转换,14,信号值与文字说明的转换,Linux提供了一个数组externchar*sys_siglist;数组的下标是信号的编号,给出的字符串指针指向相应信号的说明文字示例5.9注意,有效的信号编号是从1到31,15,psignal函数,该函数类似于perror,根据信号值,输出相关的信息函数原型#includevoidpsignal(intsigno,constchar*msg);该函数将字符串msg输出,后面接一个冒号和一个空格,再接着对该信号的说明,最后一个换行符示例(5.10),16,strsignal函数,该函数类似于strerror函数,将信号值翻译成可读的字符串函数原型#includechar*strsignal(intsigno);参数:signo,即信号编号返回值:signo对应的说明文字在Solaris9中,若信号编号无效,则返回NULL,在FreeBSD、Linux中,则返回字符串,说明信号不可识别示例(5.11),17,strsignal函数,strsignal函数返回了一个字符串指针该指针指向的内存区域需要释放吗?该指针指向的内存区域可以被修改吗?若能修改,修改后,再调用strsignal函数会有什么结果?示例(5.12),18,strsignal函数,为什么不需要释放内存?可以修改strsignal返回的指针,所对应的内存区域但是,再次调用strsignal后,并未修改成功为什么?,19,strsignal函数,查看源文件strsignal.c实际上,对于有效的信号编号,若修改对应的内存区域,将段错误而对于实时信号和未知信号,则可以随意修改;但每次调用strsignal后,修改的内容都不存在,原因即strsignal内部通过重新分配buffer,填充信号说明导致,第五讲信号,信号的概念signal函数不可靠信号与可靠信号信号的发送接收机制信号集与可靠信号机制sigsetjmp和siglongjmp函数abort函数,21,signal函数,用于设置信号的处理方法函数原型void(*signal(intsigno,void(*func)(int)(int);返回类型与参数返回类型:void(*)(int)第一个参数signo:信号名第二个参数func:类型void(*)(int),22,signal函数,说明:typedefvoid(*Sigfunc)(int);Sigfunc*signal(int,Sigfunc*);第二个参数funcSIG_IGN:内核忽略该信号(SIGKILL、SIGSTOP不能忽略)SIG_DFL:接收到信号signo后,按照系统默认动作处理用户自己提供的信号处理函数。当收到信号时,进程立即转移去执行信号处理函数返回值:成功,返回信号以前的处理方式出错,返回SIG_ERR,#defineSIG_ERR(void(*)()-1#defineSIG_DFL(void(*)()0#defineSIG_IGN(void(*)()1,23,signal函数,程序演示(5.1后台启动)$kill-USR1进程ID$kill-s信号编号进程ID,24,signal函数,当一个进程调用fork时,其子进程继承父进程的信号处理方式(处理函数地址在子进程有意义?)当执行一个程序时,所有信号的状态通常都是系统默认(除非调用exec的进程忽略某信号)exec函数将原先设置为要捕捉的信号,改为它们的默认动作(为什么?),其他信号的状态则不变,25,signal函数,例子(5.8)命令行中:ccmain.csig_int=signal(SIGINT,SIG_IGN);signal(SIGINT,sig_int);必须两次调用signal(两次改变信号处理方式),才能获取当前信号处理方式当改变信号处理方式之间,有信号达到,该如何?,29,问题?,signal函数存在的问题不可靠,在早期的UNIX系统中(如SVR4中),进程每次处理信号时,随即将信号动作复位为默认动作。因此需要重复安装信号函数,例如:voidsig_int(intsigno);signal(SIGINT,sig_int);voidsig_int(intsigno)signal(SIGINT,sig_int);,30,signal函数存在的问题(续),上述解决方法存在的问题是:在信号发生之后到信号处理程序中调用signal函数之间有一个时间窗口。在此期间,可能发生另一次信号,而第二个信号有可能造成执行默认动作。(信号丢失,即不可靠)进程所能做的仅仅是捕捉或忽略信号,而不能让内核阻塞信号不要忽略信号但是在发生信号时,记住它,在进程做好准备的时候传递它,31,第二个问题的典型例子,intsig_int_flag;intmain(void)voidsig_int();.signal(SIGINT,sig_int);.while(sig_int_flag=0)pause();.voidsig_int()signal(SIGINT,sig_int);sig_int_flag=1;,问题:有可能在while检测变量之后,而在pause函数之前发生SIGINT信号,而且信号只发生此一次,由此,该程序将一直睡眠。这种情况导致程序在大多数时间能正确运行。,假设在此处发生信号?,第五讲信号,信号的概念signal函数不可靠信号与可靠信号信号的发送接收机制信号集与可靠信号机制sigsetjmp和siglongjmp函数abort函数,33,不可靠信号与可靠信号,不可靠信号:建立在早期机制上的信号。这些信号的值从1到31,已有预定义含义,如SIGINT进程每次处理信号后,系统就将对信号的响应设置为默认动作(需两次调用signal)linux已解决信号可能丢失不可靠信号不支持排队(程序演示5.3,同一种信号,test后台启动)在信号处理函数执行过程中,到来的所有相同信号,都被合并为一个信号,34,不可靠信号与可靠信号,可靠信号:后期引入的,其信号值从34到64可靠信号支持排队,不会丢失(程序演示5.4,同一种信号,test后台启动)可靠信号也被称为实时信号不可靠信号被称为非实时信号信号:软中断(中断程序,执行信号处理程序)可靠信号与不可靠信号:在信号处理程序中,收到与正处理信号不同的信号时,会中断当前信号的处理,去执行新到信号的处理函数(示例5.13),35,示例5.13的启示,当正在处理可靠信号A时,若收到另一可靠信号B,则中断信号A的执行,转去执行信号B的处理;若此时又收到信号B,则该信号在B队列排队;若此时又收到信号A,则该信号在A队列排队若信号B处理完毕,则检查B队列中有无信号尚需处理,处理完毕后,再返回中断点执行,36,可靠信号与不可靠信号,早期的信号安装和发送机制signalkill改进后的信号安装和发送机制sigactionsigqueue信号可靠与否,只与信号值有关,与采用何种信号安装、发送函数无关,第五讲信号,信号的概念signal函数不可靠信号与可靠信号信号的发送接收机制信号集与可靠信号机制sigsetjmp和siglongjmp函数abort函数,38,信号的发送接收机制,kill函数、raise函数alarm函数、pause函数sleep函数及实现具有超时功能的API,39,信号的发送接收机制,kill函数、raise函数alarm函数、pause函数sleep函数及实现具有超时功能的API,40,kill函数,该函数将信号发送给其他进程或者进程组函数原型#includeintkill(pid_tpid,intsigno);参数与返回值signo:需要发送的信号编号pid:发送的目的地成功返回0,出错返回-1,41,kill函数,pid的取值pid0:将信号发送给进程ID为pid的进程pid=0:将信号发送给起进程组ID等于发送进程的进程组ID,而且发送进程有许可权向其发送信号的所有进程pid0:将信号发送给其进程组ID等于pid绝对值,而且发送进程有许可权向其发送信号的所有进程pid=-1将信号发送给除进程0以外的所有进程,但发送进程必须有许可权,42,raise函数,该函数将信号发送给自己函数原型#includeintraise(intsigno);参数与返回值signo:需要发送的信号编号成功返回0,出错返回-1,43,发送信号的权限,有发送信号许可权的基本规则是:1)超级用户可以将信号发送给任意进程;2)发送者的实际或有效用户ID,必须等于接收者实际或有效用户ID;当signo为0时,则kill仍执行正常的错误检测,但不发送信号。这常被用来确定一个特定进程是否存在。如果向一个并不存在的进程发送空信号,则kill返回-1,errno则被设置为ESRCH。,44,信号的发送接收机制,kill函数、raise函数alarm函数、pause函数sleep函数及实现具有超时功能的API,45,alarm函数,该函数用于设置一个计时器,在将来某个指定的时间,该计时器将超时,产生SIGALRM信号函数原型#includeunsignedintalarm(unsignedintseconds);参数与返回值seconds:经过seconds秒后,产生SIGALRM返回0或以前设置的计时器时间的余留秒数,46,alarm函数,注意:每个进程只有一个计时器若在调用alarm时,以前已为该进程设置的计时器并未超时,则将该计时器的余留值作为本次alarm调用的值返回,以前登记的计时器,则被新计时器替代取消定时器若有以前为进城登记的尚未超时的计时器,而且本次调用seconds等于0,则取消计时器,47,alarm函数,是先调用alarm函数,再调用signal函数设置SIGALRM处理函数?还是先调用signal函数设置SIGALRM处理函数,再调用alarm函数?,48,alarm函数,使用示例(5.14),49,pause函数,该函数使调用进程挂起,直到捕捉到一个信号函数原型#includeintpause();返回值只有执行了一个信号处理程序并从其返回时,pause才返回返回-1,errno被设置为EINTR,50,信号的发送接收机制,kill函数、raise函数alarm函数、pause函数sleep函数及实现具有超时功能的API,51,sleep函数,该函数用于让调用进程挂起,直到已经过了指定的时间,或者调用进程捕捉到一个信号,并从信号处理程序返回函数原型#includeunsignedintsleep(unsignedintseconds);,52,sleep函数,返回值:若已经过了指定的时间,则返回0若调用进程捕捉到一个信号,并从信号处理程序返回,则sleep提前返回,返回值是未睡够的秒数,53,sleep函数的实现,方式一:Solaris9使用alarm实现sleep函数方式二:FreeBSD、Linux使用nanosleep提供时间延迟,54,sleep的实现方式一,staticvoidsig_alrm(intsigno)return;/nothingtodo,justreturntowakeupthepauseunsignedintsleep1(unsignedintnsecs)if(signal(SIGALRM,sig_alrm)=SIG_ERR)return(nsecs);alarm(nsecs);/*startthetimer*/pause();/nextcaughtsignalwakesusupreturn(alarm(0);/turnofftimer,returnunslepttime,Thereisaracecondition,55,sleep1函数,上述实现存在如下问题:如果调用者此前曾设置了计时器,则它被sleep1函数中的第一次alarm调用擦去。(考虑解决方法)该程序中修改了对SIGALRM的配置。如果编写一个函数供其他函数调用,则在该函数调用时要保存原配置,并在返回前恢复原配置;在调用alarm和pause之间有一个竞争条件。(思考在什么情况下会发生?结果如何?),56,sleep1函数,对于问题1检查第一次调用alarm的返回值,若其小于本次调用alarm的参数值,则只应等到上次设置的计时器超时;若上次设置的计时器超时时间晚于本次设置值,则在sleep1函数返回前,复位此计时器,使其在上次计时器的设定时间再次发生超时,57,sleep1函数,对于问题2保存signal函数的返回值,在返回前复位原配置对于问题3可以使用setjmp,或者sigprocmask、sigsuspendsleep的早期实现,更正了问题1和问题2,58,sleep2函数,staticjmp_bufenv_alrm;staticvoidsig_alrm(intsigno)longjmp(env_alrm,1);unsignedintsleep2(unsignedintnsecs)if(signal(SIGALRM,sig_alrm)=SIG_ERR)return(nsecs);if(setjmp(env_alrm)=0)alarm(nsecs);/*startthetimer*/pause();/*nextcaughtsignalwakesusup*/return(alarm(0);/*turnofftimer,returnunslepttime*/,59,sleep2函数,这种实现解决了竞争条件,即使pause从未执行,在发生SIGALRM时,sleep2也会返回但却带来另一个难于察觉的问题:它涉及到与其他信号的相互作用。如果SIGALRM中断了某个其他信号处理程序,则调用longjmp会提早终止该信号处理程序下页PPT的例子,60,unsignedintsleep2(unsignedint);staticvoidsig_int(int);intmain(void)unsignedintunslept;if(signal(SIGINT,sig_int)=SIG_ERR)err_sys(signal(SIGINT)error);unslept=sleep2(5);printf(sleep2returned:%un,unslept);exit(0);,staticvoidsig_int(intsigno)inti;volatileintj;printf(nsig_intstartingn);for(i=0;i_val_cnt=0;,84,sigfillset函数,intsigfillset(sigset_t*set);该函数初始化由set指向的信号集,使其包括所有信号实现(bits/sigset.h)中的关键代码片断#define_sigfillset(set)int_cnt=_SIGSET_NWORDS;sigset_t*_set=(set);while(-_cnt=0)_set-_val_cnt=0UL;,85,sigaddset函数,intsigaddset(sigset_t*set,intsigno);该函数将一个信号添加到set中查看bits/sigset.h中的108到120行实现中的关键代码翻译int_sigaddset(_sigset_t*_set,int_sig)unsignedint_mask=1_val_word|=_mask;.,86,sigaddset函数,在sigset_t结构体中,第一个32比特,对应信号1到31,第二个32比特,对应可靠信号34到64这一点与教材描述的结构不同示例5.17在gdb中,使用“x/12tb该函数从set中删除信号signo查看bits/sigset.h中的108到120行实现中的关键代码翻译int_sigdelset(_sigset_t*_set,int_sig)unsignedint_mask=1_val_word.,88,sigismember函数,intsigismember(constsigset_t*set,intsigno);该函数判断信号signo是否在set中查看bits/sigset.h中的108到120行实现中的关键代码翻译int_sigismember(_const_sigset_t*_set,int_sig)unsignedint_mask=1_val_word返回值成功返回0出错返回-1,92,sigprocmask函数,参数oset:进程的当前信号屏蔽字通过oset返回,若不关心当前信号屏蔽字,可以设为NULL若set不为NULL,则若how=SIG_BLOCK,则进程新的信号屏蔽字是当前信号屏蔽字和set指向信号集的并集,set包含了希望阻塞的附加信号若how=SIG_UNBLOCK,则进程新的信号屏蔽字是当前信号屏蔽字和set指向信号集补集的交集,set包含了希望解除阻塞的信号若how=SIG_SETMASK,则进程新的信号屏蔽字将被set指向的信号集的值代替,93,sigprocmask函数,参数若set为NULL,how也无意义调用sigprocmask后,如果有任何未决的、不再阻塞的信号,则在sigprocmask返回前,至少会将其中一个信号递送给该进程。该函数仅为单线程的进程定义的,对于多线程的进程,有另一套函数示例5.18,94,sigpending函数,该函数返回一个信号集,其中的各个信号对于调用进程是阻塞的而不能递送,因而也一定是当前未决的函数原型#includeintsigpending(sigset_t*set);参数与返回值set:sigpending函数填充该结构体,返回未决信号成功返回0,出错返回-1程序演示(5.5),95,sigprocmask,if(sigismember(,能否使用:SIG_UNBLOCK,96,信号集和可靠信号机制,基本概念信号集及基本操作sigprocmask函数,sigpending函数sigaction函数sigsuspend函数可重入函数和线程安全的函数,97,sigaction函数,该函数用于替换signal函数,即可以检查、修改信号的处理动作函数原型#includeintsigaction(intsigno,conststructsigaction*act,structsigaction*oact);返回值成功返回0,出错返回-1,98,sigaction函数,intsigaction(intsigno,conststructsigaction*act,structsigaction*oact);参数signo:指定需要处理的特定信号。应该在该信号收到之前调用sigaction。除了SIGSTOP和SIGKILL之外,可以把signo设为任何类型的信号若act非空,则要修改信号的处理动作若oact非空,则系统经由oact指针返回该信号的上一个动作act、oact可以为空,99,sigaction结构体,structsigactionvoid(*sa_handler)(int);sigset_tsa_mask;intsa_flags;void(*sa_sigaction)(int,siginfo_t*,void*);,sa_handler用于设置信号处理函数的地址,或SIG_IGN、SIG_DFLsa_mask说明了一个信号集,在调用该信号处理函数之前,这一信号集要附加到进程的信号屏蔽字中;仅当从信号处理函数返回时,再将进程的信号屏蔽字复原在信号处理函数被调用时,操作系统建立的新信号屏蔽字包括了正在被处理的信号。因此,保证了在处理一个给定信号时,同种信号会被阻塞(可靠信号与不可靠信号),100,设置信号处理动作:sigaction,sa_flags成员用来改变signo信号的行为属性。sa_flags的若干值可以用或运算组合。比如把sa_flags设为SA_RESETHAND,使得在执行该信号处理函数时,信号处理函数重新设为SIG_DFL。把sa_flags设为SA_RESTART,则如果该信号中断了系统调用,在处理完信号处理程序后,重新启动该系统调用。,101,Linux中常用的sa_flags值,SA_INTERRUPT(0 x20000000)由此信号中断的系统调用不会自动重启(示例5.19)SA_NOCLDSTOP(1)若signo是SIGCHLD,当子进程停止时,不产生此信号,但当其终止时,仍旧产生SA_NOCLDWAIT(2)若signo是SIGCHLD,则当调用进程的子进程终止时,不创建僵死进程;若调用进程在后面调用了wait,则调用进程阻塞,直到其所有子进程都终止,此时返回-1,并将errno设置为ECHILD(示例5.20),102,Linux中常用的sa_flags值,SA_NODEFER(0 x40000000)在捕捉到此信号时,在执行其信号捕捉函数时,系统不自动阻塞此信号除非sa_mask包括了此信号此种类型的操作对应于早期的不可靠信号SA_RESETHAND(0 x80000000)在执行该信号处理函数时,信号处理函数重新设为SIG_DFL,并清除SA_SIGINFO标志,103,Linux中常用的sa_flags值,SA_RESTART(0 x10000000)由此信号中断的系统调用会自动重启动SA_SIGINFO(4)此选项对信号处理函数提供了附加信息:一个指向siginfo结构的指针以及一个指向进程上下文标识符的指针,104,sigaction结构体,structsigactionvoid(*sa_handler)(int);sigset_tsa_mask;intsa_flags;void(*sa_sigaction)(int,siginfo_t*,void*);,sa_sigaction字段:用于设置一个替代的信号处理函数若sa_flag指定了SA_SIGINFO标志,则使用由sa_sigaction字段指定的信号处理函数,而不是由sa_handler指定的信号处理函数注意sa_sigaction和sa_handler可能使用同一个存储区域,因此只能使用其中之一,105,信号处理函数,原来的信号处理函数原型:voidhandler(intsigno);现在的信号处理函数原型:voidhandler(intsigno,siginfo_t*info,void*context);查看siginfo_t结构(程序5.6)Context:可以转换为ucontext_t类型指针,用于标识信号传递时进程的上下文,106,sigqueue函数,用于向进程发送一个带参数的信号函数原型:intsigqueue(pid_tpid,intsigno,constunionsigvalval);val:typedefunionsigvalintsival_int;/该值可以传递给信号处理函数void*sival_ptr;sigval_t,107,信号处理函数,voidhandler(intsigno,siginfo_t*info,void*context)coutsi_value.sival_intstarttime+5)break;pr_mask(finishingsig_usr1:);canjump=0;siglongjmp
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 影像科CT技师上岗证真题与答案
- 游客行为分析优化旅游服务
- 企业培训课件价值观
- 企业培训技巧课件
- 英语全能阅读强化练(十五)
- 优化外资利用结构改进措施
- 企业培训车辆无偿借用及管理合同
- 美容养生连锁店按摩技师团队承包合同
- 高效采购合同谈判策略与标的物协议
- 彩票站与彩票代销商代理合作协议
- 2025年中国军工用电源模块项目投资可行性研究报告
- 2025明光事业单位笔试真题
- 2025房县事业单位笔试真题
- DBJ50T-195-2025 混凝土抗压强度检测技术标准
- 《南昌市海绵城市建设规划设计导则》
- 牙齿漂白治疗技术操作指南
- 道路施工工艺培训
- 2025-2030全球及中国乙酰柠檬酸三丁酯(ATBC)行业市场现状供需分析及市场深度研究发展前景及规划可行性分析研究报告
- 克罗恩病诊断与治疗课件
- 2025济宁市泗水县泗河街道社区工作者考试真题
- 初二化学全套试题及答案
评论
0/150
提交评论