信号操作总结

信号的基本概念

** 为啥要有信号**

    信号概念:Linux系统响应某些状况而产生的事件,进程在接受到信号后采取相应的动作。 
说白了,操作系统要管理进程

进程在受到某些信号的影响后,也会做出相应的动作,而信号有些是人为的,有些是系统为的。

一、信号产生的几种方法

方法示例
终端按键 产生Ctrl-C,Ctrl-Z等等组合键
硬件异常产生进程执行除0,野指针等导致CPU异常
kill命令发送给某个进程kill -9 进程pid
软件条件pipe读端不读了,并把它的文件描述符关了,而写端依然在写;alarm(3)闹钟
系统调用函数kill(pid,signo),raise(signo),abort()等系统调用

信号产生了,就要处理,咋处理呢?啥时候处理呢,合适的时候,啥是合适的时候呢?后边说

二、信号处理方法

1.忽略此信号
2.执行该信号的默认处理动作(大多数是终止该进程)
3.提供一个信号处理函数,要求内核在处理该信号的时候切换到用户态执行这个处理函数,这就叫捕捉(catch)信号

一些细节

1.SIGQUIT的默认处理动作是终止进程并且Core Dump,生成core文件;
2.系统默认不允许产生core文件,core文件中可能包含一些敏感信息。
3.开发调试阶段可用命令 ulimit 命令改变这个限制,生成core文件,方便时候调试

在内核中(非实时信号)1~31
每个

1.信号都有两个标志位,分别表示阻塞(block)和未决(pending),还有一个函数指针表示处理动作;
2.当一个信号产生时,内核在进程控制块中(PCB)中设置其pending 表,直到信号递达才会清除该标志位;
3.若该信号正在被阻塞,则暂缓递达,直到阻塞解除后,才能递达;
3.当信号被递达之前产生多次只记一次,而实时信号在递达之前产生多次会依次放在一个队列里。

捕捉信号
如果信号的处理动作是用户自定义的函数,在信号递达的时候就调用这个函数,这就叫捕捉信号

其流程如下
在这里插入图片描述
注意:当信号的处理函数被注册时,内核检测到有信号递达,所以内核返回用户态后不是恢复main()函数的上下文,而是执行自定义处理函数,所以sighandler和main函数使用不同的堆栈空间,因此它们不存在调用和被调用的关系,是两个独立的控制流程。

关键点:

1.从哪来到哪去;
2.当sighandler被调用时,内核将当前信号加入进程的信号屏蔽字,当处理函数返回时自动恢复为原来的信号屏蔽字,这就可以保证在处理某个信号的时候,如果这个信号再次产生,那么它会被阻塞到当前处理结束为止。

三、信号的操作

int kill (pid_t pid, int signo);
给指定进程发信号,成功返回0,失败返回-1

int raise(int signo);
给当前进程发信号,成功返回0,失败返回-1

#include <stdlib.h> void abort();
是当前进程收到信号而终止,没有返回值

1.信号集(sigset_t)
由于block和pending只记录有或没有,实质上是一个位图;因此可以用相同的数据类型来存储 ,所以 有了 sigset_t 类型,称为信号集。而阻塞信号集也称为信号屏蔽字。

2.信号集操作函数
#include <signal.h>

int sigemptyset(sigset_t* set);
让set指向的信号集所有bit位清零;

int sigfillset(sigset_t* set);
让set指向的信号集所有bit位置1;

int sigaddset(sigset_t* set, int signo);
在该信号集中添加一个signo信号

int sigdelset(sigset_t* set, int signo);
在该信号集中删除一个signo信号

这四个函数都是成功返回0,出错返回-1

int sigismember(const sigset_t* set, int signo);
判断set执向的信号集中是否包含signo信号,若包含,返回1,否则返回0,出错返回-1

int  sigprocmask(int  how,  const  sigset_t* set, sigset_t*  oset);
作用:读取或更改进程中的信号屏蔽字
参数:
	how:要做什么; 用法如下,set:指向新的信号屏蔽字,oset:输出型参数,当前的信号屏蔽字通过该参数传出;
    返回值:成功返回0,失败返回-1

how参数用法:

SIG_BLOCKset包含了我们希望添加到当前信号屏蔽字的信号
SIG_UNBLOCKset包含了我们希望从当前信号屏蔽字中解除阻塞的信号
SIG_SETMASK设置当前信号屏蔽字为set所指向的值

注意:如果调用sigprocmask解除了对当前若干个未决信号的阻塞,则在sigprocmask返回前,至少要将其中一个信号递达

sigpending()函数

int sigpending(sigset_t* set);
读取当前进程的未决信号集,通过set参数传出,成功返回0,出错返回-1

sigaction()函数
    int sigaction(int  signo, const  struct  sigaction* act, struct  sigaction*  oact);
    作用:读取和修改与指定信号相关联的处理动作,成功返回0,出错返回-1
    参数:
    	 signo是指定信号的编号。
   		 若act指针非空,则根据act修改信号的处理动作。
   		 若oact为非空,则通过oact传出该信号原来的处理动作。

struct sigaction {
                       void     (*sa_handler)(int);  //函数指针,自定义处理动作
                       void     (*sa_sigaction)(int, siginfo_t *, void *);
                       sigset_t   sa_mask;         //设置信号屏蔽字
                       int        sa_flags;             //一般缺省为0 
                       void     (*sa_restorer)(void);
                   };

注意:sa_handler赋值为常数SIGIGN传给sigaction表示忽略信号,赋值为SIG_DFL表示执行系统默认动作,赋值为一个函数指针表示用自定义函数捕捉该信号,显然,这是一个回调函数。其不是被main函数调用,而是被系统调用。

3.等待函数

#include <unistd.h> int pause(void);
作用:pause函数是调用进程挂起知道有信号递达;
如果信号的处理动作是终止进程,则进程终止,pause没机会返回;
如果信号的处理动作是忽略,则进程继续挂起,pause不返回;
如果信号的处理动作是捕捉,则调用处理函数之后,pause返回-1,所以它只有出错返回

#include <signal.h> int sigsuspend(const sigset_t* sigmask);
作用:具有和pause一样的挂起等待功能。具体是临时解除对某个信号的屏蔽,然后挂起等待,当它返回时,进程的信号屏蔽字才会恢复。

mysleep实现

代码如下:

#include <iostream>
#include <unistd.h>
#include <signal.h>
using namespace std;

void handler(int signo)         //啥也不做
{
   ; 
}

unsigned int mysleep(size_t sec)
{
    sigset_t newmask,oldmask,suspmask;
    struct sigaction act, oact;        //创建两个结构体
    act.sa_handler = handler;       //定义处理方式 sa_handler = void (*handler)(int)
    sigemptyset(&act.sa_mask);      //清空信号集
    sigaction(SIGALRM,&act,&oact);  //收到 SIGALRM 信号,执行handler动作 ,oact传出该信号原来的动作
    //屏蔽14号
    sigemptyset(&newmask);
    sigaddset(&newmask,SIGALRM);
    sigprocmask(SIG_BLOCK,&newmask,&oldmask);
    alarm(sec);          //设置闹钟  
    suspmask = oldmask;
    //取消屏蔽
    sigdelset(&suspmask,SIGALRM);
    //挂起等待14号
    sigsuspend(&suspmask);     //原子操作,临时解除屏蔽,并挂起
    
    unsigned int ret = alarm(0);
    // pause();                        //挂起 
    sigaction(SIGALRM,&oact,NULL);  //恢复默认信号处理动作(以后收到14号信号,则默认退出)

    sigprocmask(SIG_SETMASK,&oldmask,NULL);  //恢复以前的信号屏蔽字
    // cout << "alarm" << endl;
    return ret;
}

int main()
{
    while (1){
        mysleep(1);
        cout << "i am back!!! "<< getpid() << endl;
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值