Linux学习之旅(19)-----信号(1)

信号:Linux的信号机制是从unix的基础上发展来的,在linux中一共有64个信号(实际只有62个,另外两个被废弃了),其中前32是从unix中继承来的,用来控制进程。另外32个是liunx的信号有被称为实时信号,用来操作硬件。

在linux系统中通过kill -l命令可以查看这写信号:

操作系统对信号的处理一般有3种方式:

一、默认 二、忽略   三、捕捉

默认方式:操作系统为每个进程都设置了默认的动作(5种),当进程接收到信号后系统就会使用该信号对应的动作。

这五种动作分别是:

(1)Term:终止当前进程。

(2)Core:终止当前进程并且生成Core文件(用于gdb调试)

(3)Ign:   忽略该信号

(4)Stop:暂停当前进程

(5)Cont:继续执行暂停的程序。

(*最后一行为该信号在什么情况下会产生)

通过man 7 signal 命令可以查看signal的具体信息。

忽略方式:当某个进程接收到信号,会忽略它,即不去处理。一般的信号都可以被设置忽略,但SIGKILL和SIGSTOP是不能被忽略。(如果可以忽略就会产出内核无法杀死的进程,这会非常的危险)。

捕捉方式:即当进程接收到信号后,不会去使用默认的方法,而是使用用户自定义的方式去处理这个信号。

 

那信号是如何产生的那?

信号编号信号名称产生原因
1SIGHUP当用户退出Shell时,由该Shell启动的所有进程将收到这个信号,默认动作为终止进程
2SIGINT当用户按下Ctrl+C组合键时,用户终端向该终端启动并正在运行的进程发送此信号,默认终止进程
3SIGQUIT当用户按下Ctrl+\组合键时,用户终端向该终端启动并正在运行的进程发送此信号,默认终止进程
4SIGILLCPU检测到某进程执行了非法指令。默认终止进程并产生Core文件
5SIGTRAP由断点或者其他trap指令产生
6SIGABRT调用abort函数时产生
7SIGBUS非法访问内存地址,包括内存对齐出错
8SIGFPE在发生致命的运算错误时发出。包括浮点数运算错误,溢出及除数为0等所有算法错误
9SIGKILL无条件终止进程
10SIGUSE1用户自定义信号,可以在程序中使用
11SIGSEGV无效内存访问
12SIGUSR2用户自定义信号,可以在程序中使用
13SIGPIPE向没有读端的管道写数据
14SIGALRM定时器超时
15SIGTERM程序结束信号
16SIGSTKFLTLinux专用,数学协处理器的栈异常
17SIGCHID子进程结束时,父进程接收的信号
18SIGCONT继续运行暂停进程执行
19SIGSTOP暂停一个进程
20SIGTSTP交互停止进程(Ctrl+z)
21SIGTTIN当一个后台进程试图读控制终端时,终端驱动程序产生
22SIGTTOU当一个后台进程试图写控制终端时,终端驱动程序产生
23SIGURG通知进程已经发生了一个紧急情况。在网络连接上接到非规定的波特率的数据时,此信号可选择的产生。
24SIGXCPU当CPU时间限制超时的时候
25SIGXFSZ超过文件的最大长度设置。
26SIGVTALRM虚拟时钟超时时产生该信号,只计算CPU使用时间
27SIGPROF和SIGVTALRM类似,计算CPU和执行系统调用的时间
28SIGWINCH窗口大小发生变化时产生该信号
29SIGIO向进程发出一个异步的I/O事件
30SIGGPWR关机
31SIGSYS无效的系统调用

在进程的PCB中存储两个信号集(未决信号集和阻塞信号集),当进程接收到一个信号时,操作系统内核会将该进程未决信号集中的对应的信号位置为1,表示这个信号还没有被真正的执行,然后去查看对应的阻塞信号集,如果阻塞信号集中对应的状态位为0,那么该信号执行,对应未决信号集中的1重新置为0(递达态)。如果为1,那么这个信号被阻塞。并且前32个信号不支持排队,即如果未决信号集中的状态一旦为1,那么之后同样的信号不会被接收。后32为支持排队。

这时我们知道如果需要忽略进程某个信号,只需要将该进程对应的阻塞信号集对应信号位置上的状态位置为1,就可以了,那具体如何操作那?实际上我们时无法直接去修改PCB中的阻塞信号集的,不过linux系统为我们提供了信号集处理的函数。

信号集的处理函数:

sigset_t为信号集(结构体,占128个字节,可以通过sizeof()查看)
int sigemptyset(sigset_t *set); //将信号集的状态为全部置为0
int sigfillset(sigset_t *set);  //将信号集的状态为全部置为1
int sigaddset(sigset_t *set);   //将信号集中指定的状态置为1
int sigdelset(sigset_t *set);   //将信号集中指定的状态置为0
int sigismember(const sigset_t *set,int signo);//查看信号集中对应信号的状态位
int sigprocmask(int how,const sigset_t *set,sigset_t *oset);
/*
函数功能:读取或更改进程的信号阻塞信号集
参数说明:
(1)how
SIG_BLOCK  :将set信号集添加到当前信号集,相当于 mask=mask|set
SIG_UNBLOCK:使用set信号集解除阻塞信号集的信号,相当于 mask=mask&(~set)
SIG_SETMASK:将阻塞信号集替换为set信号集。相当于mask=set
(2)set
set信号集,更改阻塞信号集。
(3)oset
oset传出参数,将阻塞信号集通过oset传出
返回值:
若成功返回0,出错返回-1
*/

我们可以通过sigset_t定义一个信号集,然后通过相应的函数对信号集进行设置,然后将它通过sigprocmask()函数注册进系统中。

int sigpending(sigset_t *set);
/*
函数功能:
读取当前进程的未决信号集,通过set参数传出。
返回值:
成功返回0,失败返回-1
*/

程序:设置阻塞SIGINT(ctrl+c)和SIGQUIT (ctrl+\)10秒

#include <signal.h>
#include <sys/types.h>
#include <stdio.h>
void printsignal(const sigset_t *set)
{
    int i;
    for(i=1;i<32;++i)
    {
        //检测阻塞信号是否为1
        if(__sigismember(set,i)==1)
        {
            putchar('1');
        }
        else 
        {
            putchar('0');
        }
    }
    puts("");
}
int main()
{
    
    sigset_t set,oset;
    printf("信号集子节数:%d\n",sizeof(set));
    //将自定义信号集中的信号全部清零
    sigemptyset(&set);
    sigaddset(&set,SIGINT);//ctrl+c
    sigaddset(&set,SIGQUIT); //ctrl+\
    //将信号集注册到系统的信号集中,NULL的意思为不接收传出的参数
    sigprocmask(SIG_BLOCK,&set,NULL);
    int i=0;
    while(1)
    {
        //读取当前进程的未决信号集,oset为传出参数
        sigpending(&oset);
        printsignal(&oset);
        if(i==10)
        {
            //解除quit的阻塞
            //sigdelset(&s,SIGQUIT);
            //解除阻塞
            sigprocmask(SIG_UNBLOCK,&set,NULL);
        }
        sleep(1);
        i++;
    }
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值