计算机系统-从应用程序到底层实现 课件 第24讲 信号_第1页
计算机系统-从应用程序到底层实现 课件 第24讲 信号_第2页
计算机系统-从应用程序到底层实现 课件 第24讲 信号_第3页
计算机系统-从应用程序到底层实现 课件 第24讲 信号_第4页
计算机系统-从应用程序到底层实现 课件 第24讲 信号_第5页
已阅读5页,还剩32页未读, 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

wait示例

若多个子进程结束,则执行顺序任意可使用宏指令macrosWIFEXITED和WEXITSTATUS以获得exit状态信息voidfork10(){pid_tpid[N];inti;intchild_status;for(i=0;i<N;i++) if((pid[i]=fork())==0) exit(100+i);/*Child*/for(i=0;i<N;i++){ pid_twpid=wait(&child_status);

if(WIFEXITED(child_status)) printf("Child%dterminatedwithexitstatus%d\n", wpid,WEXITSTATUS(child_status)); else printf("Child%dterminateabnormally\n",wpid);}}等待特定进程pid_t

waitpid(pid,&status,options);voidfork11(){pid_tpid[N];inti;intchild_status;for(i=0;i<N;i++) if((pid[i]=fork())==0) exit(100+i);/*Child*/for(i=N-1;i>=0;i--){ pid_twpid=waitpid(pid[i],&child_status,0); if(WIFEXITED(child_status)) printf("Child%dterminatedwithexitstatus%d\n", wpid,WEXITSTATUS(child_status)); else printf("Child%dterminatedabnormally\n",wpid);}}休眠进程unsignedintsleep(unsignedintsecs);将一个进程休眠一段指定的时间若请求的时间量到,则返回0intpause(void);让调用该函数的进程一直休眠直到该进程收到一个信号execve加载并运行程序加载并运行当前进程:可执行文件名参数列表argv环境变量列表envp无返回(除非出错)重写代码、数据和栈保存pid,打开的文件以及信号等上下文环境变量:“name=value”字符串getenv

与setenvintexecve(char*filename,char*argv[],char*envp[])Null-terminatedenv

varstringsunusedNull-terminatedcmdlineargstringsenvp[n]==NULLenvp[n-1]envp[0]…Linkervarsargv[argc]==NULLargv[argc-1]argv[0]…envpargcargvStackbottomStackframeformainenvironStacktopexecve

示例if((pid=fork())==0){if(execve(argv[0],argv,environ)<0){

printf("%s:Commandnotfound.\n",argv[0]);exit(0);}}envp[n]=NULLenvp[n-1]envp[0]…argv[argc]=NULLargv[argc-1]argv[0]…“ls”“-l”“/usr/include”“USER=droh”“PRINTER=iron”“PWD=/usr/droh”argvenvironhello程序的加载与执行Step1:在shell命令行提示符后输入命令:$./helloStep2:shell命令行解释器构造argv和envpargvargv[0]nullargv[]“./hello"hello程序的加载与执行Step1:在shell命令行提示符后输入命令:$helloStep2:shell命令行解释器构造argv和envpStep3:调用fork()函数,创建一个子进程,获得与父进程shell完全相同的虚拟空间(拷贝),包括只读段、可读写数据段、堆以及用户栈等。

进程的地址空间IA-32/Linux平台下,每个(用户)进程具有独立的私有地址空间(虚拟地址空间)每个进程的地址空间划分(即存储映像)布局相同(如右示)TASK_SIZEhello程序的加载与执行Step1:在shell命令行提示符后输入命令:$./helloStep2:shell命令行解释器构造argv和envpStep3:调用fork()函数,创建一个子进程,获得与父进程shell完全相同的虚拟空间(拷贝),包括只读段、可读写数据段、堆以及用户栈等。Step4:调用execve()函数,在当前进程(新创建的子进程)的上下文中加载并运行hello程序。将hello中的.text节、.data节、.bss节等内容加载到当前进程的虚拟地址空间(仅修改当前进程上下文中关于存储映像的一些数据结构,不从硬盘拷贝代码、数据等内容)Step5:调用hello程序的main()函数,hello程序开始在一个进程的上下文中运行。多任务系统系统并发执行多个进程进程:正在执行的程序实例其状态包括:存储器映像,寄存器值,程序计数器从一个进程切换到另一个进程当进程需要输入/出资源或发生计时器事件,挂起进程当输入/出可得或设定了调度优先级,则恢复进程对于用户而言,所有进程看起来都是同时执行大部分系统,一次只能执行一个进程除非比单独运行时性能更低程序员的多任务模型基本函数fork产生新进程一次调用,返回两次exit终止自身进程一次调用,不返回可能进入僵死状态wait与waitpid

等待并回收终止的子进程execve

在现有进程上运行新程序调用一次,通常不返回编程挑战理解函数的非标准语义避免不恰当使用系统资源例如,“forkbombs”可能会令系统崩溃0304进程020105任何时候,系统里面由多个活跃进程一次一个核只能执行一个进程每个进程看起来都独占处理器和私有内存地址空间进程生成/结束异常加载和运行程序调用函数wait或waitpid调用函数execve

一次调用,(一般)没有返回调用函数fork,其一次调用,两个返回值调用函数exit,其一次调用,无返回需要非标准控制流的事件产生外部(中断)或内部(陷阱和故障)

小结回收/等待进程Unix/Linux进程层次LoginshellChildChildChildGrandchildGrandchild[0]Daemone.g.httpdinit[1]何谓“后台作业”一般而言,用户一次执行一个命令输入命令,读输出结果,再输入下一条指令部分程序会运行很长时间例如:“在两小时内删除一个文件”后台作业是一个无需等待的进程$sleep7200;rm/tmp/junk#shellstuckfor2hours$(sleep7200;rm/tmp/junk)&[1]907#readyfornextcommand简单shell示例问题外壳示例显示等待并回收前台作业可是对于后台作业呢?其终止后,可能产生僵死进程通常shell不会终止,可能会导致进程永不被回收产生内存泄漏,导致内核运行内存不足现代Unix/Linux:一旦运行进程数超过配额,在shell不能再运行任何新命令,即fork函数此时会返回-1unix>limitmaxproc#cshsyntaxmaxproc202752unix>ulimit-u#bashsyntax202752异常控制流提供解决方案问题当后台作业结束时,shell并不知道通常,如何时候都会发生上述情况shell的惯常控制流不能及时回收结束的后台进程例行的控制流是“等待正在运行的作业完成,然后回收它”解决方案:异常控制流内核中断的惯常进程,以预警提示后台作业完成在Unix-like系列OS中,这种预警机制称为信号《计算机系统》信号篇《计算机系统》课程教学组2025年春季学期信号signal

是一条小消息,通知进程,发生了某种类型的系统事件与异常or中断类似自内核(有时是应另一个进程的需求),发送到一个进程信号类型定义为1-30的整型ID号信号内信息就是其ID号,表示信号到达ID名称缺省操作相应事件2SIGINTTerminateInterrupt(e.g.,ctl-cfromkeyboard)9SIGKILLTerminateKillprogram(cannotoverrideorignore)11SIGSEGVTerminate&DumpSegmentationviolation14SIGALRMTerminateTimersignal17SIGCHLDIgnoreChildstoppedorterminated发送一个信号通过更新目标进行的上下文的某些状态,实现发送一个信号到一个目标进程内核发送信号的可能原因:内核检测到一个系统事件,例如除以零(SIGFPE)或终止一个子进程(SIGCHLD)另一个进程引发系统调用

kill函数,显示地要求内核发送一个信号到目标进程接收一个信号目标进程接收一个信号,以某种方式响应这个内核发送的信号三种可能的响应方式:Ignore忽略这个信号(什么也不做)Terminate

结束这个进程(可选coredump)Catch

捕获这个信号,执行用户级的函数信号处理程序(signalhandler)类似于调用硬件异常处理程序以响应异步中断挂起和阻塞信号如果一个信号被发出但没被接收,则该信号被挂起

对于任何类型的信号,最多只有一个信号挂起注意:待处理信号不能被排队若有一个类型为K的信号被挂起,则后续发送的类型K的信号均被丢弃一个进程可以阻塞特定类型的信号被阻塞信号可以被传送,但不会被接收,直至信号被停止阻塞被挂起信号最多被接收一次信号的基本概念内核在每个进程的上下文中维护挂起和被阻塞位向量挂起:表示被挂起的一组信号当类型K的信号被挂起时,内核置位k当类型K的信号被接收,内核清除置位挂起的位K被阻塞:表示被阻塞的一组信号使用sigprocmask函数可以设置和清除被阻塞置位进程组每个进程都只属于一个进程组Back-groundjob#1Fore-groundjobBack-groundjob#2ShellChildChildpid=10pgid=10前台进程组20后台进程组32后台进程组40pid=20pgid=20pid=32pgid=32pid=40pgid=40pid=21pgid=20pid=22pgid=20getpgrp()

返回当前进程所属的进程组号

setpgid()

改变进程的进程组

$./forks16Child1:pid=24818pgrp=24817Child2:pid=24819pgrp=24817

linux>psPIDTTYTIMECMD24788pts/200:00:00bash24818pts/200:00:02forks24819pts/200:00:02forks24820pts/200:00:00ps$/bin/kill-9-24817$psPIDTTYTIMECMD24788pts/200:00:00bash24823pts/200:00:00ps信号发送方法一使用/bin/kill/bin/kill程序可向进程或进程组发送任意信号Examples/bin/kill-924818

向进程24818发送信号SIGKILL/bin/kill-9-24817

发送信号SIGKILL给进程组24817中的每一个进程信号发送方法二键盘组合键键盘输入

ctrl-c(ctrl-z)会向前台进程组的每一个作业(job)发送信号SIGINT(SIGTSTP)SIGINT–终止一个进程的缺省操作SIGTSTP–停止(挂起)一个进程的缺省操作Fore-groundjobBack-groundjob#1Back-groundjob#2ShellChildChildpid=10pgid=10前台进程组20后台进程组32后台进程组40pid=20pgid=20pid=32pgid=32pid=40pgid=40pid=21pgid=20pid=22pgid=20ctrl-c与ctrl-z$./forks17Child:pid=28108pgrp=28107Parent:pid=28107pgrp=28107<typesctrl-z>Suspended$pswPIDTTYSTATTIMECOMMAND27699pts/8Ss0:00-bash28107pts/8T0:01./forks1728108pts/8T0:01./forks1728109pts/8R+0:00psw$fg./forks17<typesctrl-c>$pswPIDTTYSTATTIMECOMMAND27699pts/8Ss0:00-bash28110pts/8R+0:00pswSTAT(进程状态)说明:Firstletter:S:sleepingT:stoppedR:runningSecondletter:s:sessionleader+:foregroundprocgroup使用“manps”可了解更多细节信号发送方法三kill函数voidfork12(){pid_tpid[N];inti,child_status;for(i=0;i<N;i++) if((pid[i]=fork())==0) while(1);/*Childinfiniteloop*/

/*Parentterminatesthechildprocesses*/for(i=0;i<N;i++){ printf("Killingprocess%d\n",pid[i]); kill(pid[i],SIGINT);}

/*Parentreapsterminatedchildren*/for(i=0;i<N;i++){ pid_twpid=wait(&child_status); if(WIFEXITED(child_status)) printf("Child%dterminatedwithexitstatus%d\n", wpid,WEXITSTATUS(child_status)); else printf("Child%dterminatedabnormally\n",wpid);}}收到信号后……当内核从一个异常处理程序返回,准备将控制传递给进程p时内核计算pnb=pending&~blocked进程p的未阻塞的待处理信号的集合If(pnb==0)控制传递回进程p的逻辑控制流的下一条指令else选择pnb中最小的非零位k并强制进程p接收信号k收到的信号触发进程p的某种行为对pnb中的所有非零k重复以上过程将控制传递回逻辑控制流中的下一个指令默认行为

每个信号类型有一个预定义的默认行为,是下面中的一种:

进程终止(terminates)进程终止并转储存储器(dumpscore)进程停止(stops)直到被信号SIGCONT重启进程忽略(ignores)该信号信号处理程序

signal函数改变和收到信号signum相关联的默认行为:handler_t*signal(intsignum,handler_t*handler)相应不同的handler值:SIG_IGN:忽略类型为signum的信号SIG_DFL:类型为signum的信号行为恢复为默认行为否则,handler就是用户定义的信号处理程序(signalhandler)的函数地址当接收到一个类型为signum的信号,就会调用这个函数被称为安装该处理程序(handler)执行该处理程序被称为捕获或处理该信号当处理程序执行它的return语句,控制通常传递回控制流中进程被信号接收中断位置处的指令信号处理程序示例voidint_handler(intsig){safe_printf("Process%dreceivedsignal%d\n",getpid(),sig);exit(0);}voidfork13(){pid_tpid[N];inti,child_status;signal(SIGINT,int_handler);for(i=0;i<N;i++)if((pid[i]=fork())==0){while(1);/*childinfiniteloop}for(i=0;i<N;i++){printf("Killingprocess%d\n",pid[i]);kill(pid[i],SIGINT);}for(i=0;i<N;i++){pid_twpid=wait(&child_status);if(WIFEXITED(child_status))printf("Child%dterminatedwithexitstatus%d\n",wpid,WEXITSTATUS(child_status));elseprintf("Child%dterminatedabnormally\n",wpid);}}N=5信号处理程序示例voidint_handler(intsig){safe_printf("Process%dreceivedsignal%d\n",getpid(),sig);exit(0);}voidfork13(){pid_tpid[N];inti,child_status;signal(SIGINT,int_handler);for(i=0;i<N;i++)if((pid[i]=fork())==0){while(1);/*childinfiniteloop}for(i=0;i<N;i++){printf("Killingprocess%d\n",pid[i]);kill(pid[i],SIGINT);}for(i=0;i<N;i++){pid_twpid=wait(&child_status);if(WIFEXITED(child_status))printf("Child%dterminatedwithexitstatus%d\n",wpid,WEXITSTATUS(child_status));elseprintf("Child%dterminatedabnormally\n",wpid);}}N=5信号处理程序示例挂起信号不会排队等待对每一种信号类型,只有一位信号标示位用以标明是否被挂起即使有多个进程已经发送了该类型信号intccount=0;voidchild_handler(intsig){intchild_status;pid_tpid=wait(&child_status);ccount--;safe_printf(

"Receivedsignal%dfromprocess%d\n",sig,pid);}voidfork14(){pid_tpid[N];inti,child_status;ccount=N;signal(SIGCHLD,child_handler);for(i=0;i<N;i++) if((pid[i]=fork())==0){ sleep(1);/*deschedulechild*/ exit(0);/*Child:Exit*/ }while(ccount>0) pause();/*Suspenduntilsignaloccurs*/}$./forks14ReceivedSIGCHLDsignal17forprocess21344ReceivedSIGCHLDsignal17forprocess21345信号不排队必须检查所有已终止作业

典型操作是循环waitvoidchild_handler2(intsig){intchild_status;pid_tpid;while((pid=waitpid(-1,&child_status,WNOHANG))>0){ ccount--; safe_printf("Receivedsignal%dfromprocess%d\n",sig,pid);}}voidfork15(){...signal(SIGCHLD,child_handler2);...}$forks15Receivedsignal17fromprocess27476Receivedsignal17fromprocess27477Receivedsignal17fromprocess27478Receivedsignal17fromprocess27479Receivedsignal17fromprocess27480

信号处理程序相关在诸如read这类慢速系统调用中,信号到达信号处理程序中断read调用Linux:从信号处理程序返回,read调用自动重新开始其它类Unix系统会使用一个EINTER错误编码(errno)引发read调用失败,此时应用程序会重新开始慢速系统这些微妙的差异使得使用信号的可移植代码的写入变得复杂阅读教材可以获得更多细节 响应内部事件示例内部事件——编程代码内部设定#include<stdio.h>#include<signal.h>

intbeeps=0;

/*SIGALRMhandler*/voidhandler(intsig){safe_printf("BEEP\n");

if(++beeps<5)alarm(1);else{safe_printf("BOOM!\n");exit(0);}}Intmain(void){signal(SIGALRM,handler);alarm(1);/*sendSIGALRMin1second*/

while(1){

/*handlerreturnshere*/}}$./internal

BEEPBEEPBEEPBEEPBEEPBOOM!

非本地跳转信号Nonlocaljumpssignals本讲内容非本地跳转强有力(但是危险)的用户级异常控制流机制,可将控制转移到任意位置无需经过调用-返回序列在错误恢复和信号处理时应用intsetjmp(jmp_bufenv)必须在longjmp之前被调用为后续的longjmp定义了一个返回位置一次调用,多次返回执行:记住保存在jmp_buf缓冲区中的内容:当前寄存器上下文、栈指针和PC值的位置等“当前调用环境”返回0非本地跳转续voidlongjmp(jmp_bufenv,inti)含义:从缓冲区env中恢复调用环境触发一个从最近一次初始化env的setjmp调用的返回在setjmp后被调用调用一次,但不返回执行longjmp:保存寄存器上下文(含栈指针、基址指针、PC值)设置寄存器%eax(返回值)为i跳转到缓冲区env中保存的PC所指向的位置非本地跳转示例#include<setjmp.h>jmp_bufbuf;intmain(){if(setjmp(buf)!=0){printf("backinmainduetoanerror\n");elseprintf("firsttimethrough\n");p1();/*p1callsp2,whichcallsp3*/}...p3(){

<errorcheckingcode>if(error)longjmp(buf,1)}longjump函数的限制以堆栈规则序列工作只能longjump到已经被调用但尚未结束的函数的环境变量jmp_bufenv;P1(){

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论