进程调度与系统调用ppt课件.ppt
Linux操作系统内核分析,湘潭大学信息工程学院,讲课内容,中断管理进程管理信号处理,中断的概念,改变处理器正常执行顺序的事件中断来源:硬件:时钟、键盘、硬盘等,异步发生 异常:CPU检测到的错误软件 系统调用:进程向OS发出的请求,中断向量,保护模式支持256个中断,每个中断用一个0到255的整数来标识,把这个整数称为中断向量0到31对应异常(P76表5-2)32到47对应硬件中断(P9图2-5)48到255预留,linux使用了0 x80作为系统调用,中断,保护模式下中断处理,idtr,0号中断描述符,1号中断描述符,2号中断描述符,3号中断描述符,中断描述符表,中断描述符,段选择子,段内偏移(0-15),段内偏移(16-31),2位,4位,Head中对中断描述符表的处理,在程序232行(P58),定义了有256个中断描述符的中断描述符表,并在程序开始处把该描述符表的基地址输出(可以被C程序使用)在程序78行(P54),把这256个中断描述符中的处理程序设置为ignore_int在程序105行(P55)把中断描述符表的基地址装入到idtr寄存器中,gcc嵌入汇编,_asm_ _volatile_(汇编语句 : 输出寄存器 : 输入寄存器 : 会被修改的寄存器);输出寄存器:“=代表寄存器的字母” (变量名) 寄存器的值会被写入变量中输入寄存器:“代表寄存器的字母” (变量名) 用变量的值初始化寄存器,P81,常用寄存器加载代码,P83表5-3,嵌入汇编示例,#define _save_flags(x) _ asm_ _volatile_( “pushfl;” “popl %0” : “=a” (x) );,a代表寄存器eax。把eax和变量x绑定,往eax中写入值等于给变量x赋值,数字代表寄存器的序号。把输出寄存器和输入寄存器按照顺序进行编号,从0开始。%n代表第n个寄存器。,序号0,嵌入汇编示例,#define get_seg_byte(seg,addr)( register char _res; _ _asm_ _(“ push %fs; mov %ax, %fs; movb %fs:%2,%al; pop %fs”:“=a”(_res):“0”(seg), “m”(*(addr);_res;),序号0,序号1,序号2,宏的返回值,eax用seg的值进行了初始化,_set_gate宏,_set_gate宏(定义在system.h,P390的22行),用来设置中断描述符gate_addr:描述符的地址type:描述符的类型dpl: 使用描述符的最低权限addr:中断处理函数的地址,_set_gate宏,#define _set_gate(gate_addr,type,dpl,addr) _asm_ (movw %dx,%axnt movw %0,%dxnt movl %eax,%1nt movl %edx,%2 : : i (short) (0 x8000+(dpl13)+(type8), o (*(char *) (gate_addr), o (*(4+(char *) (gate_addr), d (char *) (addr),a (0 x00080000),0,1,2,3,4,_set_gate宏,0号立即数,1号内存地址指向描述符的基地址(前4个字节),2号内存地址指向描述符的基地址+4(后4个字节),处理程序高16位,处理程序低16位,edx,0 x0008,0 x0000,eax,处理程序低16位,0 x8000+(dpl13)+(type8),0 x8000+(dpl13)+(type8),段选择子,段内偏移(0-15),段内偏移(16-31),Type,0,DPL,1,00000000,设置中断描述符,#define set_trap_gate(n,addr) _set_gate(&idtn,15,0,addr)#define set_system_gate(n,addr) _set_gate(&idtn,15,3,addr),idt是head中定义的描述符表基地址,dpl=0,只有操作系统能够使用该中断,dpl=3,任何进程都可以使用该中断,异常初始化,trap.c中的trap_init()(P80的181行)函数负责再次初始化中断描述符表,该函数在main函数(P65的127行)中被调用异常的中断向量参见P76表5-2,中断初始化,在traps.c中的trap_init函数的最后( P81第207行)设置了并口(0 x27)的中断描述符在sche.c中的sched_init函数的最后(P103第409行)设置了时钟中断(0 x20)的中断描述符参见P9图2-5,系统调用初始化,在sche.c中的sched_init函数的最后(P103第411行)设置了系统调用(0 x80)的中断描述符,中断处理,中断处理完后,进程会按照被打断前的状态继续运行,所以需要保存该状态。,保存执行现场,调用中断处理函数,恢复执行现场,指令1指令2指令n,进程,中断处理,保存执行现场,调用中断处理函数,恢复执行现场,用户指令1用户指令2内核指令1内核指令2,进程,内核,硬中断异常系统调用,硬中断异常,中断处理,CS,EIP,SS,ESP,EFLAGS,通用寄存器,用户态指令,用户态堆栈,CPU,内核态指令,内核态堆栈,用户态,内核态,中断处理,当发生中断时,如果当前进程运行在内核态,CPU会将EFLAGS、CS、和EIP寄存器中的内容压入堆栈当发生中断时,如果当前进程运行在用户态,CPU除了会将EFLAGS、CS、和EIP寄存器中的内容压入堆栈外,还会把进程在用户态时使用的SS和ESP寄存器的内容压入堆栈,中断发生时的堆栈,CS,EFLAGS,EIP,CS,EFLAGS,EIP,用户态ESP,用户态SS,中断发生时进程在内核态,中断发生时进程在用户态,中断处理,异常处理系统调用中断处理,异常处理,异常发生时,可能带有出错码。如果有,则该出错码也会被CPU压入堆栈,CS,EFLAGS,EIP,用户态ESP,用户态SS,CS,EFLAGS,EIP,用户态ESP,用户态SS,出错码,不带出错码的异常处理,pushl $do_divide_errorno_error_code:xchgl %eax,(%esp)pushl %ebxpushl %ecxpushl %edxpushl %edipushl %esipushl %ebppush %dspush %espush %fspushl $0 lea 44(%esp),%edxpushl %edxmovl $0 x10,%edxmov %dx,%dsmov %dx,%esmov %dx,%fscall *%eax,do_divide_error,eax的值,ebx的值,eax保存do_divide_error的地址,EFLAGS,EIP,用户态ESP,用户态SS,ecx的值,edx的值,edi的值,esi的值,ebp的值,错误码0,初始esp位置,指向内核数据段,不带出错码的异常处理,addl $8,%esppop %fspop %espop %dspopl %ebppopl %esipopl %edipopl %edxpopl %ecxpopl %ebxpopl %eaxiret,eax的值,ebx的值,EFLAGS,EIP,用户态ESP,用户态SS,ecx的值,edx的值,edi的值,esi的值,ebp的值,错误码0,初始esp位置,异常处理流程,保存现场,准备参数,调用C语言函数,弹出参数,恢复现场,中断返回,发生中断,带出错码的异常处理,P75页第99行,CS,EFLAGS,EIP,用户态ESP,用户态SS,出错码,中断处理,异常处理系统调用中断处理,使用系统调用,P383第133行#define _syscall0(type,name)type name(void)long _res; _ _asm_ _ volatile(“int $0 x80 : “=a”(_res) : “0”(_NR_#name);if(_res =0) return (type)_res;errno = - _res;return -1;,系统调用的返回放在eax中,系统调用的功能号放在eax中(P381),使用系统调用,P63第23行static inline _syscall0(int,fork)int fork(void)long _res; _ _asm_ _ volatile(“int $0 x80 : “=a”(_res) : “0”(_NR_fork);if(_res =0) return (type)_res;errno = - _res;return -1;,使用系统调用,系统调用的输入把子功能号放入eax中如果还有其它参数(最多3个),则第1、第2和第3个参数分别放入寄存器ebx,ecx和edx中系统调用的返回值保存在eax中,数据结构及算法,函数指针数组 sys_call_talbe (P409)system_call(P86第80行)处理流程保存现场以子功能号作为索引查找sys_call_table,找到处理该功能的C语言函数,然后调用该函数。恢复现场,系统调用的处理,cmpl $nr_system_calls-1,%eaxja bad_sys_callpush %dspush %espush %fspushl %edxpushl %ecxpushl %ebxmovl %0 x10, %edxmov %dx,%dsmov %dx,%esmovl %0 x17, %edxmov %dx,%fscall _sys_call_table(,%eax,4)pushl %eax,EFLAGS,EIP,用户态ESP,用户态SS,EDX,ECX,EBX,系统调用参数,_sys_call_table+4*eax_sys_call_table在P409定义,EAX,保存系统调用返回值,系统调用的处理,3:popl %eaxpopl %ebxpopl %ecxpopl %edxpop %fspop %espop %dsiret,EFLAGS,EIP,用户态ESP,用户态SS,EDX,ECX,EBX,EAX,讲课内容,中断管理进程管理信号处理,进程的概念,进程是程序的一次执行,是由代码段、数据段和堆栈段组成动态的实体,在Linux中,把进程又称为任务(task)进程是系统资源分配的基本单位,也是使用CPU运行的基本调度单位,进程描述符,为了管理进程,操作系统需要清楚地知道每个进程的属性,Linux用一个称为进程描述符(task_struct)的数据结构来描述task_struct定义在sched.h中,参见P404第78行,进程描述符状态,P12图2-6,没有实现,进程描述符线性地址分布,代码,数据,bss,用户态堆栈,环境参数,nr*64M,(nr+1)*64M,start_code,end_code,end_data,brk,start_stack,进程描述符任务状态段,任务状态段(TSS)是保存任务的所有信息的内存段,共104B寄存器保存区域内层堆栈指针区域地址映射寄存器区域其它字段P116图5-8,进程描述符任务状态段,在任务切换过程中首先,CPU中各寄存器的当前值被自动保存到TR所指定的TSS中(当前任务的TSS中)然后,下一任务的TSS的选择子被装入TR最后,从TR所指定的TSS中取出各寄存器的值送到处理器的各寄存器中由此可见,通过在TSS中保存任务现场各寄存器状态的完整映象,实现任务的切换,进程描述符任务状态段,TSS的段描述符在GDT中,当前进程的TSS的段选择子被保存在TR寄存器中,TSS段描述符,GDT,TSS段,当前进程的TSS段选择子,TR,TSS段描述符,TSS段,进程描述符局部描述符表,局部描述符表(LDT)包含任务私有内存段的描述符(也是一个内存段)LDT的段描述符在GDT中,当前进程的LDT段选择子在ldtr中,进程描述符局部描述符表,Linux中LDT包含3个描述符,其中:第0个没用第1个描述的是代码段,该段的段选择子是0 xf(1111)第2个描述的是数据段,该段的段选择子是0 x17(10111),进程描述符局部描述符表,LDT段描述符,私有段段描述符,私有段段描述符,私有段段描述符,GDT,LDT,当前进程的LDT段选择子,ldtr,LDT段描述符,私有段段描述符,私有段段描述符,私有段段描述符,TSS段描述符,TSS段描述符,进程1,进程2,Linux内核堆栈,Intel平台上进程在不同的特权级别下使用不同的堆栈在Linux中为进程在内核态建立的堆栈与进程描述符公用一块4K的内存块参见shed.c(P95第53行),task_union,union task_union struct task_struct task;char stackPAGE_SIZE;/宏定义在mm.h(P401第4行);,内核堆栈栈顶,任务描述符基地址,4K,任务数组,Linux把所有任务的进程描述符的指针用一个指针数组管理起来,定义在sched.c中(P95第65行)struct task_struct *taskNR_TASKS;/NR_TASK=64,定义在sched.h中(P402第4行)FIRST_TASKLAST_TASK,taskn,task数组,tss,进程数据段描述符,其它属性,进程描述符,没用,内核代码段描述符,内核数据段描述符,没用,TSS0,LDT0,TSS1,LDT1,TSSn,LDTn,TSS(n+1),LDT(n+1),GDT,.,进程代码段描述符,没用,进程n,n*64,内核态堆栈,求进程n的TSS段的段选择子,#define FIRST_TSS_ENTRY 4#define _TSS(n) (unsigned long) n)4)+(FIRST_TSS_ENTRY3)定义在sched.h(P405第153和155行),4*8,16B,16B,GDT,Index,0,00,RPL,TI,(4+n*2)*8,进程管理,进程0的创建创建子进程调度进程进程睡眠终止进程,进程0,进程0是一个特殊的进程它是所有其他进程的祖先进程;所有其他的进程都是通过fork系统调用,复制进程0或者其后代进程产生的;只有进程0是静态产生的。,进程0,创建进程0的步骤申请一个进程描述符与进程内核堆栈的公用体把进程描述符的地址放在task数组的第0项在GDT中设置进程0的tss段描述符和ldt段描述符(sched_init函数, P102第392,393行)把进程0tss段描述符的选择子和ldt段描述符的选择子装载到tr寄存器和ldtr寄存器(404,405行),申请进程描述符与内核堆栈的公用体,static union task_union init_task = INIT_TASK,;/定义在sched.c中(P95第58行)/INIT_TASK定义在sched.h中(P405第113行)/进程0的代码段和数据段与内核代码段和数据段重合struct task_struct *taskNR_TASKS = /定义在sched.c中(P95第65行),task数组,tss,进程数据段描述符,其它属性,进程描述符,没用,内核代码段描述符,内核数据段描述符,没用,TSS0,LDT0,GDT,进程代码段描述符,没用,进程0,64M,0M,esp0,内核态堆栈,tr,ldt,1,2,2,3,4,加载进程0的tss和ldt的段选择子,ltr(0);lldt(0);#define ltr(n) _asm_(ltr %ax:a (_TSS(n)#define lldt(n) _asm_(lldt %ax:a (_LDT(n)/定义在sched.h中(P405第157行),把进程0移到用户态,sched_init函数运行完之后,进程0还是运行在内核态利用模拟中断返回的方法把进程0从内核态切换到用户态move_to_user_mode定义在system.h中(P389第1行),Iret指令,EIP,CS,FLAG,SS,ESP,esp,内核态堆栈,0 xf,0 x17,move_to_user_mode,#define move_to_user_mode() _asm_ ( “movl %esp,%eaxnt pushl $0 x17nt pushl %eaxnt pushflnt pushl $0 x0fnt pushl $1fnt iretn 1:tmovl $0 x17,%eaxnt movw %ax,%dsnt movw %ax,%esnt movw %ax,%fsnt movw %ax,%gs :ax),进程0用户态数据段选择子(0 x17),进程0用户态堆栈指针( user_stack ),EFLAGS,进程0代码段段选择子(0 x0f),EIP(iret返回后执行的代码),进程管理,进程0的创建创建子进程调度进程进程睡眠终止进程,fork系统调用,系统调用处理函数_sys_fork定义在system_call.s中(P89第208行),在task数组中寻找一个空位,有空位,拷贝当前进程,有,无,在task数组中寻找空位,int find_empty_process(void)定义在fork.c(P115第135行)作用:寻找task数组中的空位返回:如果找到则返回空位索引 否则返回错误,find_empty_process,int i;repeat:if (+last_pid)pid = last_pid) goto repeat;for(i=1 ; iNR_TASKS ; i+)if (!taski)return i;return -EAGAIN;,last_pid记录的是上一次创建进程的进程号。在Linux中,所有现存的进程的进程号不能相同。定义在fork.c中(P113第22行),拷贝当前进程,int copy_process(int nr, long ebp, long edi, long esi,long gs, long none,long ebx, long ecx, long edx,long fs, long es, long ds,long eip, long cs, long eflags, long esp, long ss)作用:拷贝当前进程的描述符到子进程描述符中返回:子进程的进程号(P114),调用copy_process的堆栈,CS,EFLAGS,EIP,用户态ESP,用户态SS,DS,ES,FS,EDX,ECX,EBX,call _sys_call_table的返回地址,GS,ESI,EDI,EBP,EAX,call _copy_process的返回地址,函数的参数,find_empty_process()的返回值,task数组空闲项的下标,系统调用的返回地址,Fork系统调用,int fork(void)long _res; _ _asm_ _ volatile(“int $0 x80 : “=a”(_res) : “0”(_NR_fork);if(_res =0) return (type)_res;errno = - _res;return -1;,eip,copy_mem,int copy_mem(int nr,struct task_struct * p)作用:修改子进程的私有段的基地址, 并拷贝当前进程的页目录项和页表参数:nr子进程在task数组中的索引 p 子进程描述符的指针返回:0表示无错,小于0表示有错,线性地址的分布,4G的线性地址给64个进程使用,每个进程占64M的线性地址task数组中的第n个进程占用的线性地址是n*64M(n+1)*64Mtask数组中的第n个进程的所有段的基地址设为n*64M,taskn2,taskn1,task数组,进程数据段,进程代码段,进程数据段,进程数据段,进程代码段,进程数据段,n2*64M,(n2+1)*64M,n1*64M,(n1+1)*64M,线性地址,子进程,父进程,task数组,线性地址,页目录,页表,copy_process,申请4K页面,设置task数组,拷贝父进程描述符,修改子进程描述符,拷贝父进程的页目录和页表,设置TSS和LDT,进程管理,进程0的创建创建子进程调度进程进程睡眠终止进程,任务切换的形式,ljmp TSS段选择子:偏移ljmp 8字节的内存首地址其中,头4个字节对应偏移(忽略),后4个字节的头2个字节对应TSS段选择子,忽略,任务切换的步骤,从指令JMP的操作数中获取新任务的TSS段选择子在当前任务的TSS中保存当前任务的状态为TR装载新任务TSS的段选择子,从新任务的TSS中装载新任务的状态到处理器中开始执行新任务,schedule,void schedule()/定义在sched.c(P96第104行)作用:选择一个任务并与当前任务进行切换,选择task数组中第一个所剩时间片最长的且状态为可运行的任务,与当前进程进行切换,所有的进程都已处理完,设置警告且警告超时,给当前进程设置alarm信号,有信号并处于可中断睡眠状态,唤醒进程,最长时间片=0,重新给所有任务分配时间片:counter/2+priority,是,否,是,否,是,否,否,是,switch_to宏,#define switch_to(n) struct long a,b; _tmp; _asm_(cmpl %ecx,currentnt je 1fnt movw %dx,%1nt xchgl %ecx,currentnt ljmp *%0nt cmpl %ecx,last_task_used_mathnt jne 1fnt cltsn 1: :m (* ,dx中存放的是新进程n的TSS段选择子,1,0,2,3,从这里开始,老进程退出CPU,新进程开始运行,除了刚用fork创建的进程,其它进程重新被运行时都从这里开始,调用schedule,时钟中断发生时发生中断前,进程处于用户态且时间片=0,被动放弃CPU(被抢夺),进程的状态仍然是可运行状态系统调用返回时发生中断前,进程处于用户态且时间片=0,被动放弃CPU(被抢夺),进程的状态仍然是可运行状态睡眠函数内进程在内核态主动放弃CPU,进程的状态是不可运行状态,时钟中断,Linux设置时钟中断频率为10毫秒,每次中断时调用timer_interrupt,定义在system_call.s(P88第176行),timer_interrupt:push %dspush %espush %fspushl %edxpushl %ecxpushl %ebxpushl %eaxmovl $0 x10,%eaxmov %ax,%dsmov %ax,%esmovl $0 x17,%eaxmov %ax,%fsincl jiffiesmovb $0 x20,%aloutb %al,$0 x20movl CS(%esp),%eaxandl $3,%eaxpushl %eaxcall do_timeraddl $4,%espjmp ret_from_sys_call,EFLAGS,EIP,用户态ESP,用户态SS,EDX,ECX,EBX,EAX,fs指向进程数据段,向8259A发送停止中断信号,取中断发生时CS的CPL,CPL,do_timer,中断时进程处于用户态,用户态运行时间+1,内核态运行时间+1,进程的时间片-1,进程时间片0,中断时进程处于内核态,返回,schedule,是,是,否,是,否,否,P100第305行,系统调用返回时,movl current,%eaxcmpl $0,state(%eax)jne reschedulecmpl $0,counter(%eax)je reschedule,P86第96行,进程管理,进程0的创建创建子进程调度进程进程睡眠终止进程,进程睡眠,当进程请求的资源无效时,进程将会进入睡眠状态,直到资源有效时。每个资源上都会有一个等待队列,在该队列上排队的就是为等待此资源而进入睡眠状态的进程。,资源,进程睡眠,进程可以以两种状态进入睡眠不可中断睡眠(sleep_on)可中断睡眠(interruptible_sleep_on),sleep_on,void sleep_on(struct task_struct *p)/定义在sched.c(P97第151行)作用:把任务设置为不可中断睡眠,并把任 务挂在某个等待队列上。参数:p等待队列的头指针,*p,p,进程1描述符,*p,p,进程2描述符,进程1描述符,*p,进程4,tmp,进程4描述符,进程3,tmp,进程3描述符,进程2,tmp,进程2描述符,进程1,tmp,进程1描述符,sleep_on,当资源有效时,在该资源等待队列上的进程全部都被唤醒。有可能还会发生资源冲突。常见的使用的方法是while(资源无效)sleep_on(资源等待队列头指针的指针);,interruptible_sleep_on,void interruptible_sleep_on( struct task_struct *p)/定义在sched.c(P97第167行)作用:把任务设置为可中断睡眠,并把任 务挂在某个等待队列上。参数:p指向等待队列的头指针的指针,*p,进程4,tmp,进程4描述符,进程3,tmp,进程3描述符,进程2,tmp,进程2描述符,进程1,tmp,进程1描述符,*p,进程4,tmp,进程4描述符,进程3,tmp,进程3描述符,进程2,tmp,进程2描述符,进程1,tmp,进程1描述符,有信号,interruptible_sleep_on,当资源有效时或是任一进程有信号时,在该资源等待队列上的进程全部都被唤醒。资源冲突没有信号而被唤醒常见的使用的方法是while(资源无效,进程管理,进程0的创建创建子进程调度进程进程睡眠终止进程,进程资源,进程终止时,要释放它占用的所有资源,包括:内存进程描述符和内核堆栈占用4K空间页表和页目录占用的物理页面代码和数据占用的物理页面GDT中的LDT和TSS描述符打开的文件,进程,task数组,线性地址,页目录,页表,TSSn,LDTn,进程终止的问题,通常父进程在子进程终止后,需要查询子进程的终止状态,但是该状态保存在子进程的进程描述符中。如果,子进程终止时释放了进程描述符,则无法查询终止状态。,通知,进程终止的问题,由父进程负责回收子进程的进程描述符。但是,如果父进程在子进程之前终止,谁来负责回收子进程的进程描述符?进程在终止时,把所有未终止的子进程过继给进程1。由进程1负责接收这些子进程终止信号,并回收子进程的进程描述符。,sys_exit,int sys_exit(int error_code)/定义在exit.c(P111第137行)作用:释放进程占用的物理内存并关闭进程打开的文件。参数:error_code 退出码返回:无意义,释放代码和数据占用的物理内存、页表,把子进程过继给进程1,关闭打开的文件和终端,修改状态为僵死,schedule,发送信号给父进程,sys_waitpid,int sys_waitpid(pid_t pid, unsigned long * stat_addr, int options)作用:如果任意一个符合pid条件的子进程僵死,则释放子进程占用的进程描述符;如果所有符合pid条件的子进程都不处于僵死状态,则按照option选项或者等待或立即返回。(P111第142行),sys_waitpid,参数:pidpid 0等待进程号为pid的子进程pid = 0等待进程组号等于当前进程 组号的任何一个子进程pid = -1等待任何一个子进程pid -1等待进程组号等于-pid的任 何一个子进程,sys_waitpid,参数:stat_addr:存放子进程的退出码(P67)options:options中WNOHANG置位:表示如果没有满足pid标识的子进程是僵死状态,则当前进程马上返回;否则当前进程挂起。返回:正常返回子进程的pid,出错返回负数。,所有进程都搜索完毕,是符合pid条件的子进程,子进程的状态是僵死,回收子进程的进程描述符,有符合pid条件的子进程,改状态为可中断睡眠schedule,设置WNOHANG,收到SIGCHLD信号,返回,是,否,是,否,否,是,是,否,是,否,是,否,讲课内容,中断管理进程管理信号处理,信号,信号是一种进程间通讯的方式,这种机制是异步的。 Linux支持32个信号,每个信号用一个整数值来标识。参见书P107表5-5。,信号处理数据结构,参见task_struct,P404第83行long signal;对应32个信号,向进程发送信号就是把singal中信号对应的那一位置1。signal |= 1(信号值-1)long blocked;对应32个信号,如果某个信号对应的那一位置1表示该信号被屏蔽。signal&blocked表示进程中未被屏蔽的信号。,信号处理数据结构,struct sigaction void (*sa_handler)(int); /信号处理函数指针,可以是用户自定义的函数sigset_t sa_mask;/在处理当前信号时需要屏蔽的信号,一般屏蔽本信号int sa_flags;/改变信号处理过程的标志void (*sa_restorer)(void); /恢复函数入口地址,用于清除用户堆栈这个/函数由libc提供,用户无法自行设置;,P361第48行,信号处理数据结构,信号响应函数是void(*)(int)类型的,内核在signal.h第45行定义了两个特殊的响应函数。#define SIG_DFL(void (*)(int)0)#define SIG_IGN(void (*)(int)1)进程对于SIG_DFL的处理一般是结束进程,对于SIG_IGN一般是忽略,信号处理数据结构,在signal.h第37行由sa_flags标志值的定义#define SA_NOMASK0 x40000000/在信号的处理后不保留信号屏蔽设置#define SA_ONESHOT 0 x80000000/信号处理函数一旦被调用过,就恢复到SIG_DFL,使用信号,其它模块,信号管理,系统调用,sys_sigactionsys_signalsys_kill,send_sig,注册信号处理函数,这一步可以省略,在省略情况下信号的处理函数是SIG_DFL。参见进程0的定义(P405第115行)。进程也可以注册自己的信号处理函数。,注册信号处理函数,void sig_handler(int sig) printf(signal n); signal(SIGTERM,sig_handler); int main() signal(SIGTERM,sig_handler); while(1); return 0; ,信号注册系统调用,int sys_signal(int signum, long handler, long restorer) /P105第48行作用:注册一个自定义信号处理函数参数:signum信号值 handler信号处理函数指针 restorer恢复函数返回:上一次注册的处理函数指针,注册信号处理函数,voidmysignal(intsigno)/*mysignalhandler*/int main(void)structsigactionact,old; act.sa_handler=mysignal;act.sa_flags=0;sigaction(SIGHUP,信号注册系统调用,int sys_sigaction(int signum, const struct sigaction * action,struct sigaction * oldaction)作用:注册自定义的信号处理结构参数:signum信号值 action自定义的sigaction指针 oldaction用于存放上次注册的 sigaction,发送信号,int sys_kill(int pid,int sig)/定义在exit.c(P109第60行)作用:向进程或进程组发送信号参数:pid0: 向指定进程发送信号 pid=0: 向当前进程所在组的所有进程发送信号 pid=-1:向除进程0之外的所有进程发送信号 pid-1:向进程组为-pid的所有进程发送信号 sig: 信号值,信号处理,信号是一种异步通信机制,当进程收到信号后,并不马上处理。只有等到进程从系统调用或者中断处理返回到用户态前,才处理信号。这时程序运行到system_call.s中的ret_from_sys_call标号地址处,先检查信号位图,找到第一个允许响应的信号值,将之压入内核堆栈,然后跳转到do_signal()函数。 (P86第101行),信号处理,do_signal根据信号值调用对应的信号处理函数。难点在于当进程注册了自定义信号处理函数时,内核怎么调用用户态的函数?通过构造堆栈,信号处理,void do_signal(.)/定义在signal.c(P106第82行)作用:根据信号值调用信号处理函数。,CS,EFLAGS,EIP,用户态ESP,用户态SS,DS,ES,FS,EDX,ECX,EBX,EAX,信号值,系统调用,系统调用下一语句,自定义信号处理函数,old_eip,eflags,edx,ecx,eax,原来的阻塞位图,signr,sa_restore,内核堆栈,用户进程,用户堆栈,.globle_sig_restore.globle_masksig_restore#若没有SA_NOMASK则使用这个restorer函数#改函数仅仅恢复寄存器值,并且弹出堆栈_sig_restore:addl $4, %esppopl %eaxpopl %ecxpopl %edxpopflret#若有SA_NOMASK则使用这个restorer函数#该函数中又会产生系统调用_masksig_restore:addl $4, %esp # 退掉栈顶的signr,这时栈顶为屏蔽码blocked,# 它是系统调用ssetmask()的参数call_ssetmask # ssetmask()系统调用addl$4, %esp # 退掉blockedpopl %eax # 恢复寄存器popl %ecxpopl %edxpopflret,int 0 x80下一个语句,信号处理函数restore函数,信号处理流程,屏蔽信号,int sys_ssetmask(int newmask)/定义在signal.c(P104第20行)作用:为进程设置信号屏蔽位图参数:newmask新的信号屏蔽位图返回:老的信号屏蔽位图,