实验五信号处理一、实验目的学习和掌握信号的处理方法,特别是sigaction,alarm,sigpending,sigsetjmp和siglongjmp等函数的使用。二、实验要求1、编制具有简单执行时间限制功能的shell:myshell[-ttime]这个测试程序的功能类似实验1,但是具有系统shell(在cs8服务器上是bash)的全部功能。time是测试程序允许用户命令执行的时间限制,默认值为无限制。当用户命令的执行时间限制到达时,测试程序终止用户命令的执行,转而接收下一个用户命令。2、myshell只在前台运行。3、按Ctrl-\键不是中断myshell程序的运行,而是中断当前用户命令的接收或终止当前用户命令的执行,转而接收下一个用户命令。4、注意信号SIGALRM和SIGQUIT之间嵌套关系的处理。三、实验分析1、shell功能实现可以直接利用系统shell(在cs8是bash):execl(“/bin/sh”,“sh”,“-c”,buf,(char*)0);这样程序sigtest就具有系统shell的全部功能。如果命令带“-t”选项,则在创建执行上面函数的子进程之前,必须设置闹钟;在子进程结束之后,必须将闹钟清零。因为需要使用闹钟,所以实验需要处理两个信号:SIGALRM和SIGQUIT。如果当前程序正在执行用户命令,则信号处理函数必须“杀死”用户命令进程:kill(pid,SIGKILL);//pid为用户命令进程的ID对于信号SIGQUIT还有一种可能:正在接收用户输入的命令串。此时需要放弃当前输入,重新开始接收输入。解决方法可能需要使用非局部转移机制。2、信号SIGALRM和SIGQUIT之间嵌套关系的处理:由于如果同时存在多个未决信号,系统总是一个信号处理完之后再处理下一个信号,但是在处理完全部未决信号之前,不会返回被中断的函数或系统调用。因此,无论SIGALRM和SIGQUIT这两个信号哪个先处理,另一个未决信号就应该忽略(清除未决信号);在处理其中一个信号时,屏蔽另一个信号(如果发生,就是未决信号)。staticvolatilepid_tpid;//全局变量,存放执行用户命令的子进程的ID,//非0表正在执行用户命令设置信号SIGALRM处理方式的代码(供参考):structsigactionact,oact;act.sahandler=func;//信号SIGALRM的处理函数sigemptyset(&act.sa_mask);act.sa_mask|=SIGQUIT;//在处理信号SIGALRM时,屏蔽信号SIGQUITact.sa_flags=0;#ifdefSA_RESTART//如果定义了该常量,则系统默认不重启,应改为重启act.sa_flags|=SA_RESTART;#endifsigaction(SIGALRM,&act,&oact);信号SIGQUIT处理函数的末尾应该包含如下代码段(供参考):sigset_tpendmask;sigemptyset(&pendmask);sigpending(&pendmask);//获得未决信号集合pid=0;//表示当前无正在执行的用户命令alarm(0);//清除闹钟if(sigismember(&pendmask,SIGALRM)){//存在未决信号SIGALRMsignal(SIGALRM,SIG_IGN);//清除未决信号SIGALRMsigaction(SIGALRM,&act,NULL);//恢复原来的处理方法}四、实验程序#includeapue.h#includesys/wait.h#includesetjmp.h#includeunistd.hstaticvolatilepid_tpid;//存放执行用户命令的子进程的IDstaticsigjmp_bufjmpbuf;staticvoidsig_alrm(intsigno);staticvoidsig_quit(intsigno);charbuf[MAXLINE];Sigfunc*signal(intsigno,Sigfunc*func){structsigactionact,oact;act.sa_handler=func;sigemptyset(&act.sa_mask);act.sa_flags=0;/*处理一个信号时,屏蔽另一个信号*/if(signo==SIGALRM)sigaddset(&act.sa_mask,SIGQUIT);elseif(signo==SIGQUIT)sigaddset(&act.sa_mask,SIGALRM);if(signo==SIGALRM){#ifdefSA_INTERRUPTact.sa_flags|=SA_INTERRUPT;#endif}else{#ifdefSA_RESTARTact.sa_flags|=SA_RESTART;#endif}if(sigaction(signo,&act,&oact)0)return(SIG_ERR);return(oact.sa_handler);}voidsig_alrm(intsigno){printf(---TIMEOUT---\n);sigset_tpendmask;if(pid0){kill(pid,SIGKILL);pid=0;}if(sigemptyset(&pendmask)0)err_sys(sigemptyseterror!);if(sigpending(&pendmask)0)err_sys(sigpendingerror!);if(sigismember(&pendmask,SIGQUIT)){signal(SIGQUIT,SIG_IGN);signal(SIGQUIT,sig_quit);}siglongjmp(jmpbuf,1);}voidsig_quit(intsigno){printf(---QUIT---\n);sigset_tpendmask;if(pid0){kill(pid,SIGKILL);pid=0;}alarm(0);//清除闹钟if(sigemptyset(&pendmask)0)err_sys(sigemptyseterror!);if(sigpending(&pendmask)0){err_sys(sigpendingerror!);}if(sigismember(&pendmask,SIGALRM)){signal(SIGALRM,SIG_IGN);signal(SIGALRM,sig_alrm);}siglongjmp(jmpbuf,1);}intmain(intargc,char*argv[]){unsignedinttime=0;intstatus,flag=0;if((argc!=1&&argc!=3)||(argc==3&&strcmp(argv[1],-t)!=0))err_quit(usage:myshell[-ttime]);elsetime=atoi(argv[2]);if(signal(SIGQUIT,sig_quit)==SIG_ERR)err_sys(signalerror!);if(signal(SIGALRM,sig_alrm)==SIG_ERR)err_sys(signalerror!);sigsetjmp(jmpbuf,1);//设置跳转点printf(%%);while(fgets(buf,MAXLINE,stdin)!=NULL){if(buf[strlen(buf)-1]=='\n')buf[strlen(buf)-1]=0;if(time)alarm(time);if((pid=fork())0)err_sys(forkerror!);elseif(pid==0){execl(/bin/sh,sh,-c,buf,(char*)0);err_ret(couldn'texecute:%s,buf);exit(127);}waitpid(pid,&status,0);pid=0;alarm(0);printf(%%);}exit(0);}五、运行结果源文件:myshell.c可执行文件:myshell生成可执行文件的命令:gccmyshell.cerror.c–omyshell测试过程:1.运行:./myshell–t62.测试基本shell功能:ls–lpwdcatapue.h3.测试信号处理功能:1)sleep102)超过6s后输入:Control+\3)sleep204)在6s之内(不超过设定的时钟值)输入:Control+\5)输入:Control+C六、实验总结通过这次实验,我熟悉了信号的处理方法,由于对课本内容不熟悉,且对信号处理的相关函数理解程度不够,因此在实验过程中遇到了较大困难。Signal函数在课本程序10-12的基础上进行修改,使之能够在处理一个信号时屏蔽另一个信号。