标签归档:异常

非本地跳转

摘自《深入理解计算机系统》
C提供一种形式的用户级异常控制流,称为非本地跳转。它可以将控制直接从一个函数转移到另一个当前正在执行的函数。而不需要经过正常的调用-返回序列。在linux c中非本地跳转是通过 setjmp longjmp函数实现。
函数所属头文件 #include < setjmp.h>
函数原型:int setjmp(jmp_buf env);
int sigsetjmp(sigjmp_buf env,int savesigs);
返回值: setjmp 返回 0 ,longjmp返回非零
函数原型:void longjmp(jmp_buf env, int retval);
void siglongjmp(sigjmp_buf env,int retval);
setjmp函数的主要功能是在env缓冲区中保存当前栈的内容,供后面longjmp使用,并返回0。
longjmp函数从env缓冲区恢复栈的内容,然后触发一个从最近一次初始化env的setjmp调用返回,然后setjmp返回,并带用非零的返回值retval。
1 非本地跳转中一个很重要的应用是允许一个深层嵌套的函数调用中立即返回,通常是由检测到某个错误情况引起。如果在一个深层嵌套的函数调用中发现一个错误情况,就可以直接使用本地跳转直接返回到一个普通的本地化的错误处理程序,而不用去调用栈。
例1:
setjmp函数应用

从例1 我们可以看出,当我们在fun1中验证发现系统存在错误时,可以通过longjmp将函数返回至发最近一次调用setjmp的位置 ,图中 应该为返回至setjmp下的printf(“test information success!”);

2 非本地跳转的另一个重要应用是使一个信号处理程序转移到一个特殊的代码位置,而不是返回到被信号到达中断了的指令位置。
sigsetjmp函数应用
从例2中,我们可以看出使用非本地跳转的方式,可以使用改变信号处理函数的跳转至发生中断的位置。

综上所述:非本地跳转的方式,使程序可以不用调用其他函数,直接返回至最近一次设置setjmp的位置重新执行程序。

显示阻塞信号

在linux c中通过使用sigpromask函数可以显式地阻塞和取消阻塞选择的信号。
#include < signal.h>
int sigprocmask(int how,const sigset_t *set,sigset_t *oldset); //改变已阻塞信号
int sigemptyset(sigset_t *set); //初始化set为空集合
int sigfillset(sigset_t *set); //将信号添加到set中
int sigaddset(sigset_t *set); //将set添加到signum
int sigdelset(sigset_t *set); //从signum中删除set
int sigismember(const sigset_t *set,int signum); //判断set是否存在于signum中,如果存在则返回1 否则返回0

sigprocmask函数的功能是改变已阻塞信号:
how参数定义:
SIG_BLOCK:添加set中的信号到阻塞中;
SIG_UNBLOCK: 从阻塞中删除set中的信号;
SIG_SETMASK: 如果oldset非空,阻塞中以前的值为保存在其中;


例:父进程在一个作业列表中记录着子进程,当父进程创建一个新的子进程时,它就把这个子进程添加到作业列表中,当父进程在SIGCHLD处理程序中回收一个僵死子进程时,它就从作业列表中删除这个子进程。
显示阻塞信号

信号处理完毕后,重启系统调用

摘自《深入理解计算机系统》
在不同系统之间,信号处理语义的差异--例:一个被中断的慢速系统调用是否重启或永久放弃--是linux信号处理的一个缺陷。为了处理这个问题,Posix标准定义了一个sigaction函数,它允许Posix兼容系统用户,例:Linux和Solaris用户,可以显示的制定他们想要的信号处理语义。
#include < signal.h>
int sigaction( int signum,struct sigaction *act,struct sigaction *oldact);
在sigaction函数中,它和signal函数的调用方式一样。signal封装函数设置了一个信号处理程序:
* 只有这个处理程序当前正在处理的那种类型的信号被阻塞;
* 和所有信号一样,信号不会被排队等待;
* 只有可能,被中断的系统调用会自动重启
* 一旦设置了信号处理程序,它会一直保持,直到signal带着信号处理函数 参数为 SIG_IGN 或 SIG_DEL被调用,信号才会被恢复默认行为


1 重定义Signal函数
handler_t *Signal(int signum,handler_t *handler){
struct sigaction action,old_action;
action.a_handler = handler;
sigemptyset(&action.sa_mask);
action.sa_flags = SA_RESTART;

if(sigaction(signum,&action,&old_action) <0){ error(""); } return (old_action.sa_handler); } sa_flags标志参数说明: SA_RESETHAND 当调用信号处理函数时,将信号的处理函数重置为缺省值SIG_DFL SA_RESTART 如果信号中断了进程的某个系统调用,则系统自动启动该系统调用 SA_NODEFER 当信号处理函数运行时,内核将阻塞该给定信号。 如果设置SA_NODEFER标记,那么在该信号处理函数运行时,内核将不会阻塞该信号 通过sa_flags设置,当信号处理函数处理完毕后,系统会自动重启系统调用。 相关阅读 linux c 信号处理
信号简介
linux c接收信号
linux c创建进程
linux c回收子进程