Linux下信号的基本使用与分析

1.基本概念

信号是事件发生时对进程的通知机制,一个进程可以想另一个进程发送信号,此做法可以实现进程间的同步,然而给进程发信号的通常都是内核

(1)内核一般都会在发生以下事件时给进程发信号
.硬件发生异常
.用户键入了能够产生信号的终端特殊字符
.发生了软件事件
针对每一个信号,都定义了一个唯一的整数

(2)如果内核要调度该进程,等待信号就会马上送达,然而有时进程不想中断代码,进程可以将该信号添加到进程的信号掩码中,此信号将处于等待状态,知道进程将其从信号掩码中解除出来

(3)信号到达后,进程根据具体信号执行如下操作
.忽略信号:内核将信号丢弃,信号对进程不产生影响终止进程:进程异常终止
.停止进程:暂停进程的执行

具体的信号都是由信号处理函数来执行的

2.信号类型和默认行为

信号名称描述
SIGABRT终止进程
SIGALRM实时定时器过期
SIGBUS内存访问出错
SIGCHLD终止子进程
SIGCONT若停止则继续
SIGEMT硬件错误
SIGFPE算数异常
SIGHUP刮起
SIGTLL非法指令
SIGINT终端中断
SIGIOI/0时可能产生
SIGKILL必杀
SIGPIPE管道断裂
SIGPROF性能分析定时器过时
SIGQUIT终端退出
SIGSEGV无效的内存访问
SIGSTKFLT协议处理栈错误
SIGSTOP确保停止
SIGSYS无效的系统调用
SIGUSR用户自定义信号

3.自定义信号处理函数signal()

#include<signal.h>
sighandler_t signal(int sig,sighandler_t handler);

第一个参数sig,标注希望修改的信号编号,第二个参数handler,则指明信号到达时,所调用的处理函数,singal的返回值为之前改信号的处理函数
特别注意:我们可以用SIG_IGN来取代handler参数,它的作用是忽略该信号,如果信号专为此进程而生,则内核会丢弃此信号

4.信号处理器

信号处理器程序(信号捕捉器)是指当指定信号传递给进程时会调用一个函数
调用信号处理器程序会打断主程序流程,内核代表进程来处理程序,当处理完之后,主程序会从打断的位置恢复执行

具体实例

//为SIGINT信号重设的处理函数
#include<stdio.h>
#include<signal.h>

void sigHanler(int sig)
{
    printf("hello,world\n");
}

int main()
{
    int i;

    if(signal(SIGINT,sigHanler) == SIG_ERR)
    {
        printf("error\n");
    }

    for(i=0;i<3;i++)
    {
        printf("haha\n");
        sleep(2);
    }
}

运行结果如下:

5.发送信号:kill()

#include<signal.h>
int kill(pid_t,int sig);
//成功返回0,失败-1

该调用将会杀掉指定pid的进程,sig则指定了要发送的信号
.如果pid大于0,那么信号发给pid指定的进程
.pid等于0发给同组的每个进程
.pid小于-1,发送给pid绝对值下属的所有进程
.pid等于-1,调用进程有权将信号发给每个进程

6.用kill()检查进程的存在

若将sig参数置为0,则无信号发送,相反kill()仅回去执行错误检查,查看是否可以向进程发送信号。这意味着,可以用其来检查一个进程是否存在,若调用成功那么进程存在。但是这并不能保证该pid的进程就一定存在,有可能该pid指的进程已不是先前的那个进程了

判断进程存在与否的其他方法:
(1)wait()系统调运:监控子进程是否存在
(2)信号量和文件锁:如果进程持有这些,那么如果不能取得这些,就以为这进程存在
(3)/proc/PID接口:如果进程存在,目录proc/12345将存在

7.显示信号描述

#include<signal.h>
char *strsignal(int sig);
//返回可打印字符串

8.信号集

当系统调用需要一组不同的信号时,就用到了信号集,信号集的数据结构为sigset_t
(1)信号集的初始化

#include<signal.h>
int sigemptyset(sigset_t *set);

(2)向set中添加或删除单个信号

#include<signal.h>
int sigaddset(sigset_t *set,int sig);
int sigdelset(sigset_t *set,int sig);

(3)判断sig是否在set中

#include<signal.h>
int sigismember(const sigset_t *set,int sig);

9.信号掩码

内核会为每个进程维护一个信号掩码,即一组信号,并将阻塞其对进程的传递。如果将遭阻塞的信号发送给进程,对该信号的传递将延后,直至从信号掩码中移除该信号

sigprocmask系统调用:

#include<signal.h>
int sigprocmask(int how,const sigset_t *set,sigset_t *oldset);

sigprocmask()调运即可修改进程的信号掩码,又可获取现有掩码,或者俩重功效兼具,how参数指定给信号掩码带来的变化

how参数具体描述
SIG_BLOCK将set指向信号集中的指定信号添加到信号掩码中
SIG_UNBLOCK将set指向信号集中的信号从信号掩码中删除
SIG_SETMASK将set指向的信号集赋给信号掩码

10.等待状态的信号以及排队

若进程接收了一个正在阻塞的信号,则它会将该信号添加到进程的等待信号集中,不管该信号被发了多少此,在该信号被解除阻塞时,都只会给进程传递一次

11.改变信号处置:sigaction()

#include<signal.h>
int sigaction(int sig,const struct sigaction *act,struct sigaction *oldact);

.sig参数标识想要获取或改变的信号编号
.act参数是指针,指向描述信号新处置的数据结构
.oldact参数用来返回之前信号处置的相关信息,若不想获取设为NULL
sigaction结构定义如下

struct sigaction
{
    void (*sa_handle)(int);
    sigset_t sa_mask;
    int sa_flags;
    void (*sa_restorer)(void);//不给应用程序使用
}

其中sa_flags字段是一个位掩码,指定用于控制信号处理过程的各种选项(可以相或)

sa_flag字段具体描述
SA_NOCLDSTOP若sig为SIGCHLD(终止子进程),则恢复或停止子进程时,将不再产生此信号
SA_NOCLDWAIT若sig为SIGCHLD信号,则当子进程终止时,不会将其转换为僵尸
SA_NODEFEP捕获该信号时,不会在执行处理器程序时将该信号自动添加到进程掩码中
SA_ONSTACK针对此信号调用处理器函数时,使用了由sigaltstack()安装俄被选栈
SA_RESTART自动重启由信号处理器程序中断的系统调用

12.pause()

#include<unistd.h>
int pause(void)

调用pause()函数将暂停进程的执行,直至信号处理函数中断该调用为止

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值