信号(软件中断)编程

注:目前只讨论前32个信号(1~31)

ps aux 可以查看进程状态

当信号停止之后,用 fg 1可以让进程继续运行;bg 1可以让它在后台运行

1.信号机制(下面五种情况触发信号)

man 7 signal 可查看信号机制

终端特殊按键:在终端中 ctl+c SiGINT(程序终止信号)   ctl+z SIGSTP(停止信号)   ctl+\  SIGQUT(退出信号)

硬件异常

eg1:除0操作

   答:会报错,浮点数例外的情况;会用SIGFPE信号

eg2:访问非法内存 (只读数据段进行写操作)

   答:会报段错误,用SIGSEGV信号

kill函数或kill命令:向一个进程发送一个信号

  函数原型 int kill(pid_t pid,int sig)

  三种情况: pid>0  sig发送给ID为pid的进程

                     pid==0 sig发送给与发送进程同组的所有进程

                     pid<0  sig发送给组ID为|-pid|的进程,并且发送进程具有向其发送信号的权限

                     pid==-1   sig发送给发送进程有权限向他们发送信号的系统上的所有进程

sig==0时,用于检测,特的那个为pid进程是否存在,若不存在,返回-1

rase和abort函数:raise向自己发送某个信号,abort会终止和结束自己的信号

某种软件条件已发生

操作系统为每一个进程分配一个定时器alarm,当定时器到达时间的时候会产生信号,unsigned int alarm(unsigned int seconds)    

作用:定时指定的秒数,时间到了之后会向指定进程发送SIGALRM信号

2.信号功能

3.进程处理信号的方式

SIG_IGN

SIG_DFL

a signal handling function

三种行为:

①默认处理动作 (term、core、ign、stop、cont)

②忽略

③捕捉(用户自定义信号处理函数)

信号集:

收到一个信号之后执行信号处理函数,在执行信号处理函数的过程中如果又来了一个相同的信号,那么这个信号将会被阻塞,直到信号处理函数执行完之后,再响应被阻塞的信号,注意如果信号被阻塞期间又收到了该信号,那么多个信号的处理会被合并为1次
比如正在执行SIGUSR1的信号处理函数,此时又收到了一个SIGUSR1信号,那么该信号将会被阻塞,直到信号处理函数执行完之后,再次执行该函数。如果在阻塞期间又收到了SIGUSER1信号,接下来只再调用一次SIGUSR1的信号处理函数

信号集就是用来记录当前收到了哪个信号,会把当前信号的标志位置成“正在处理”,如果此时再收到该信号,那么信号就阻塞等待
使用数据类型sigset_t表示信号集,在Linux中该类型是一个32位无符号整数,这是因为在Linux中定义了32种信号,每一个信号用32位无符号整型变量中的一位来标志,如果该位置为1,那么表示正在处理该信号,如果置为0表示可以处理该信号。注意:有的操作系统,信号个数多于32,此时就不能用32位整数表示一个信号集了。

Linux内核的进程控制块PCB是一个结构体,task_struct, 除了包含进程id,状态,工作目录,用户id,组id,文件描述符表,还包含了信号相关的信息,主要指阻塞信号集和未决信号集

阻塞信号集(信号屏蔽字)

将某些信号加入集合,对他们设置屏蔽,当屏蔽x信号后,再收到该信号,该信号的处理将推后(处理发生在解除屏蔽后)。

未决信号集

信号产生,未决信号集中描述该信号的位立刻翻转为1,表示信号处于未决状态。当信号被处理对应位翻转回为0。这一时刻往往非常短暂。

信号产生后由于某些原因(主要是阻塞)不能抵达。这类信号的集合称之为未决信号集。在屏蔽解除前,信号一直处于未决状态。

                                     阻塞信号集中为1时,表示阻塞,不能通过

前32个信号,不支持排队(多次同一个信号算一个),后32个信号支持排队

4.信号集处理函数

sigset_t为信号集,

int sigemptyset(sigset_t *set)  //清空设置一个信号集,置0

int sigfillset(sigset_t *set)   //把一个信号集全部置1

int sigaddset(sigset_t *set,int signo) //把信号集某一个信号位置1

int sigdelset(sigset_t *set,int signo) //把信号集某一个信号位置0

int sigismember(const sigset_t *set,int signo)  //判断信号集某个信号是否置1

5.1 sigprocmask

注册函数,可以读取或更改进程的信号屏蔽字

#include<signal.h>

int sigprocmask(int how,const sigset_t *set,sigset_t *oset);
返回值:若成功为0,出错为-1,set作为传入参数,oset作为传出参数

how参数:

5.2 sigpending读取当前进程的未决信号集,通过set参数传出

#include<signal.h>
int sigpending(sigset_t *set);
通过set参数传出,成功返回0,出错返回-1

  注意:SIGKILL和SIGSTOP不允许被阻塞

6 信号捕捉设定

捕捉函数 sigaction

int sigaction(int signum,const struct sigaction *act, struct sigaction *oldact);

第一个参数为哪一个信号设置捕捉;第二个参数为信号设定动作;第三个为信号原有动作,如果不关心为null

struct sigaction{

void (*sa_handler)(int);      //捕捉函数,早期的(函数指针)

void (*sa_sigaction)(int ,siginfo_t *,void *);   //新诞生的捕捉函数(函数指针)

sigset_t sa_mask;//阻塞信号集中的信号(信号屏蔽字)

int sa_flags;//选择用第一还是第二个捕捉函数

void (*sa_restorer)(void);//过时了   

};

希望只在主线程中处理信号:
用作在主调线程里控制信号掩码
int pthread_sigmask (int how,const sigset_t *newmask, sigset_t *oldmask)

How:
SIG_BLOCK:     结果集是当前集合参数集的并集
SIG_UNBLOCK: 结果集是当前集合参数集的差集
SIG_SETMASK: 结果集是由参数集指向的集

信号掩码:信号掩码是进程的属性,即阻塞的信号列表。信号掩码 在POSIX下,每个进程有一个信号掩码(signal mask)。简单地说,信号掩码是一个"位图",其中每一位都对应着一种信号。如果位图中的某一位为1,就表示在执行当前信号的处理程序期间相应的信号暂时被"屏蔽",使得在执行的过程中不会嵌套地响应那种信号。所谓"屏蔽",与将信号忽略是不同的,它只是将信号暂时"遮盖"一下,一旦屏蔽去掉,已到达的信号又继续得到处理。

示例:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>


//信号处理函数
void sig_usr(int signo)
{         
    if(signo == SIGUSR1)
    {
        printf("收到了SIGUSR1信号,我休息10秒......!\n");
        sleep(10);
        printf("收到了SIGUSR1信号,我休息10秒完毕,苏醒了......!\n");
    }
    else if(signo == SIGUSR2)
    {
        printf("收到了SIGUSR2信号,我休息10秒......!\n");
        sleep(10);
        printf("收到了SIGUSR2信号,我休息10秒完毕,苏醒了......!\n");
    }
    else
    {
        printf("收到了未捕捉的信号%d!\n",signo);
    }
}

int main(int argc, char *const *argv)
{
    if(signal(SIGUSR1,sig_usr) == SIG_ERR)  //系统函数,参数1:是个信号,参数2:是个函数指针,代表一个针对该信号的捕捉处理函数
    {
        printf("无法捕捉SIGUSR1信号!\n");
    }
    if(signal(SIGUSR2,sig_usr) == SIG_ERR) 
    {
        printf("无法捕捉SIGUSR2信号!\n");
    }
    
    for(;;)
    {
        sleep(1); //休息1秒    
        printf("休息1秒~~~~!\n");
    }
    printf("再见!\n");
    return 0;
}


其中有两个信号自定义函数:使用kill发送USR1、USR2会分别反应:

(1)执行信号处理函数被卡住了10秒,这个时候因为流程回不到main(),所以main中的语句无法得到执行;
(2)在触发SIGUSR1信号并因此sleep了10秒种期间,就算你多次触发SIGUSR1信号,也不会重新执行SIGUSR1信号对应的信号处理函数,而是会等待上一个SIGUSR1信号处理函数执行完毕才 第二次执行SIGUSR1信号处理函数;
换句话说:在信号处理函数被调用时,操作系统建立的新信号屏蔽字(sigprocmask()),自动包括了正在被递送的信号,因此,保证了在处理一个给定信号的时候,如果这个信号再次发生,那么它会阻塞到对前一个信号处理结束为止;
(3)不管你发送了多少次kill -usr1信号,在该信号处理函数执行期间,后续所有的SIGUSR1信号统统被归结为一次。比如当前正在执行SIGUSR1信号的处理程序但没有执行完毕,这个时候,你又发送来了5次SIGUSR1信号,那么当SIGUSR1信号处理程序执行完毕(解除阻塞),SIGUSR1信号的处理程序也只会被调用一次(而不会分别调用5次SIGUSR1信号的处理程序)。

//kill -usr1,kill -usr2
(1)执行usr1信号处理程序,但是没执行完时,是可以继续进入到usr2信号处理程序里边去执行的,这个时候,相当于usr2信号处理程序没执行完毕,usr1信号处理程序也没执行完毕;此时再发送usr1和usr2都不会有任何响应;
(2)既然是在执行usr1信号处理程序执行的时候来了usr2信号,导致又去执行了usr2信号处理程序,这就意味着,只有usr2信号处理程序执行完毕,才会返回到usr1信号处理程序,只有usr1信号处理程序执行完毕了,才会最终返回到main函数主流程中去继续执行;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值