Linux 信号 longjmp,Linux编程---信号处理

三.阻塞信号

阻塞信号意味着告诉操作系统保持该信号并推迟它的发送.被阻塞的信号不会丢失,他们只是暂时被悬挂,直到阻塞解除.

1.sigset_t类型的信号操作

int sigemptyset(sigset_t *set);

int sigfillset(sigset_t *set);

int sigaddset(sigset_t *set,int signo);

int sigdelset(sigset_t *set,int signo);

int sigismember(const sigset_t *set,int signo);

主要是针对信号集合的操作.

第一个是清空集合,第二个则是填满所有类型信号.返回值总是0.

第三个则是增加一个信号到集合中.第四个是从集合中删除一个信号,成功返回0,

第五个则是检测一个信号是否为集合中的信号,在里面则返回1,否则返回0.

2.设置信号屏蔽

int sigprocmask(int how,const sigset_t *set,sigset_t *oset);

how的值有三种:

SIG_BLOCK      把set信号集中的信号加入到阻塞信号集中

SIG_UNBLOCK  解除set中的全部阻塞信号

SIG_SETMASK  使用set集合为屏蔽信号集合.

如果第二个参数为NULL,那么则什么都不做,how也没有用,只是得到原有屏蔽信号集合.

注意不能阻塞SIGKILL和SIGSTOP.如果set集合中有这两个信号,那么sigprocmask只是忽略他们但并不返回错误.

3.检查悬挂信号

int sigpending(sigset_t *set);

通过set返回已经被悬挂的信号.

四.等待信号

int pause();

这个就是阻塞进程直到有信号来.并且直到这个信号的句柄执行完成之后才返回.返回值为-1,并设置errno为EINTR.

pause在很多时候并不太可靠,所以最好不要用pause来等待新号到达,之后再开始做正真的工作.这样做不安全.即使通过设置标志来安排新号句柄合作也仍然不能可靠地使用pause.

如果在

static void sig_usr(int signo)

{  flag=1;

}

if(!flag)

pause();

这种情况下,在flag判断完到执行pause中间出现一个信号并且之后一直没有信号,那么就会造成进程永久阻塞.所以为这种情况封装了另外一个函数.

int siguspend(const sigset_t *sigmask);

这个函数把sigmask中的信号集合临时代替调用进程的信号屏蔽.然后挂起调用进程直到有不属于sigmask的信号到达为止.注意!是不属于sigmask集合的信号来才恢复阻塞!

五.使用分开的信号栈

对于大程序,调用层次比较深的可能需要用这个吧.为了避免爆栈,只好从堆中拿出一部分当作栈了.特别是对于SIGSEGV这种内存相关的信号了,可以通过中断来让程序更加强壮.

int sigaltstack(const stack_t *ss,stack_t *oss);

stack_t结构体包含

int ss_flags  栈状态标志

void *ss_sp  栈地址

size_t ss_size  栈大小

通过malloc一个空间.放入stack_t结构体中.

其中ss_flags这个有两种宏标志.SS_ONSTACK和SS_DISABLE.前者表示用新的地址.后者表示不用这个栈.感觉DISABLE好鸡肋...估计是用在调用完之后,用来查看状态的吧.

同时可以用宏参数SIGSTKSZ和MINSTKSZ来确定系统允许的信号栈空间大小.通常ss_size用SIGSTKSZ即可.

执行成功返回0,失败返回-1并设置errno为错误原因.

sigaltstack只是准备好一个栈,具体那个句柄使用这个栈还不知道.在调用sigaction的时候使用了宏SA_ONSTACK才可以来指定用哪个栈.

六.信号句柄编程技巧

1.句柄内非局部控制转移

主要是用过setjmp和longjmp或sigsetjmp和siglongjmp,来实现句柄中实现非局部控制转移.

int sigsetjmp(sigjmp_buf env,int savemask);

void siglongjmp(sigjmp_buf env,int val);

在UNIX标准中没有规定setjmp,longjmp是否保存屏蔽信息...

但在Linux下sigsetjmp,siglongjmp与setjmp,longjmp都保存屏蔽信息.

这个非局部控制转移估计多用于错误恢复吧.

2.可重入函数与异步信号安全函数

可重入函数需要满足三个条件:

--不修改自身代码

--只修改自己的局部数据

--不调用其他任何非可重入函数

这里为什么要提到可重入函数呢..因为很多时候信号会中断程序,并且信号句柄也可能调用一些函数造成破坏.

malloc就是不可重入的.如果在malloc执行的时候被信号中断,并且句柄中调用了malloc.那么很可能造成malloc所维护内存分配表就会写乱了.

所以在句柄中用不可重入函数要小心.我一开始觉得可以通过锁来解决,但是仔细一想,锁可能还不能完全的解决问题,还应有一个延时一段时间,重新发送信号的过程.

可重入函数可以在信号句柄内安全地调用,这种函数也称为异步信号安全函数.

任何函数,除非特殊声明,否则不是异步信号安全函数.

不可重入函数一般具有以下特点:

1) 使用了静态数据结构

2) 调用了malloc或free.

3) 是标准IO库中的函数.因为标准库中通常使用全局数据结构.

七.实时信号

Linux一共支持30个实时信号从34到63.和一般的信号相比,主要有如下差别:

1) 实时信号可以排队

2) 如果信号用sigqueue发送的,那么随信号可以附带一个额外的值.如果接收进程设置SA_SIGINFO标志为这个信号安装了句柄,它可以从信号句柄类型为siginfo_t的第二个参数成员si_value中得到这个值.

3) 当信号阻塞时,受阻的多个实时信号是按确定的顺序交付的.同类型信号是顺序交付,不同类型按信号值从小到大依次交付.

这里提到了一个函数sigqueue.

int sigqueue(pid_t pid,int signo,const union sigval value);

第三个参数就是上面说的额外的值.

联合体如下union sigval{

int sival_int;

void *sival_ptr;

}

也就是说这是个整数或是指针.

如果实时信号还用sigsuspend就不合适了.因为其延长了信号回应的时间.这里专门提供了两门等待实时信号的函数

int sigwaitinfo(const sigset_t *set,siginfo_t *info);

int sigtimedwait(const sigset_t *set,siginfo_t *info,const struct timesoec * timeout);

第一个参数是等待信号的集合,第二个参数是用来得到信号信息的参数.timedwait的最后一个参数则是用来限制等待时间的,如果为NULL,那么就和waitinfo一样了.

信号处理就写到这里了.基本该写的都写了.

欢迎指出错误~

0b1331709591d260c1c78e86d0c51c18.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值