何为信号:信号就是由用户、系统或进程发送给目标进程的信息,以通知目标进程中某个状态的改变或是异常。
信号产生:总体来说,其产生的条件有两种,分别是:硬件和软件原因,又称为:硬中断和软中断。可细分为如下几种原因:
①系统终端Terminal中输入特殊的字符来产生一个信号,比如按下:ctrl+\会产生SIGQUIT信号。
②系统异常。比如访问非法内存和浮点数异常。
③系统状态变化。如设置了alarm定时器,当该定时器到期时候会引起SIGVTALRM信号。
④调用了kill命令或是kill函数。
include
include
include
include
using namespace std;
void sig_handler(int signum)
{
if(0 > signum)
{
fprintf(stderr,"sig_handler param err. [%d]\n",signum);
return;
}
if(SIGINT == signum)
{
printf("Received signal [%s]\n",SIGINTsignum?"SIGINT":"Other");
}
if(SIGQUIT == signum)
{
printf("Received signal [%s]\n",SIGQUITsignum?"SIGQUIT":"Other");
}
return;
}
int main(int argc,char **argv)
{
printf("Wait for the signal to arrive.\n ");
/*登记信息*/
signal(SIGINT,sig_handler);
signal(SIGQUIT,sig_handler);
pause();
pause();
signal(SIGINT,SIG_IGN);
return 0;
}
程序运行后会一直等待用户的输入,当在终端按下ctrl+c时候会打印 Received signal [SIGINT]
说明捕获到了SIGINT信号,接着程序继续等待,当按下ctrl+\时候会打印Received signal [SIGQUIT],表明捕获到了SIGQUIT信号:
头文件
头文件
1、类型:sig_atomic_t
sig_atomic_t是 int 类型,在信号处理程序中作为变量使用。它是一个对象的整数类型,该对象可以作为一个原子实体访问,即使存在异步信号时,该对象可以作为一个原子实体访。
2、宏:
SIG_DFL 默认的信号处理。
SIG_ERR 表示一个信号错误。
SIG_IGN 表示忽略信号。
有效的信号包括:
SIGABRT 异常终止,如调用abort()。
SIGFPE 算术运算出错,如除数为0或溢出。
SIGILL 非法函数映象,如非法指令。
SIGINT 交互式信号,如中断。
SIGSEGV 非法访问存储器,如访问不存在的内存单元。
SIGTERM 发送给本程序的终止请求信号。
#include
int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
参数:
- 参数1:要捕获的信号
- 参数2:接收到信号之后对信号进行处理的结构体
- 参数3:接收到信号之后,保存原来对此信号处理的各种方式与信号(可用来做备份)。如果不需要备份,此处可以填NULL
返回值:
- 成功时:返回0
- 出错时:返回-1,并将errno设置为指示错误
与signal的不同,有哪些新功能?
- signal只能捕获信号,对信号进行处理。但是不能获取信号的其它信息
- sigaction可以使用sigaction结构体的sa_handler函数对信号进行处理(此处等同于signal函数),也可以使用sa_sigaction函数查看信号的各种详细信息
- 并且sigaction函数还可以通过sa_mask、sa_flags对信号处理时进行很多其他操作
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
sa_handler成员:
- 对捕获的信号进行处理的函数,函数参数为sigaction函数的参数1信号(概念上等同于单独使用signal函数)
- 也可以设置为后面两个常量:常数SIG_IGN(向内核表示忽略此信号)或是常数SIG_DFL(表示接到此信号后的动作是系统默认动作)
sa_mask成员:
- 功能:sa_mask是一个信号集,当接收到某个信号,并且调用sa_handler函数对信号处理之前,把该信号集里面的信号加入到进程的信号屏蔽字当中,当sa_handler函数执行完之后,这个信号集中的信号又会从进程的信号屏蔽字中移除
- 为什么这样设计??这样保证了当正在处理一个信号时,如果此种信号再次发生,信号就会阻塞。如果阻塞期间产生了多个同种类型的信号,那么当sa_handler处理完之后。进程又只接受一个这种信号
- 即使没有信号需要屏蔽,也要初始化这个成员(sigemptyset()),不能保证sa_mask=0会做同样的事情
- sigset_t数据类型见文章:https://blog.csdn.net/qq_41453285/article/details/89228297
sa_restorer成员:
- 已经被抛弃了,不再使用
sa_flags成员:
- 指定了对信号进行哪些特殊的处理
SA_INTERRUPT 由此信号中断的系统调用不自动重启动
SA_NOCLDSTOP 若signo是SIGCHLD:
当一子进程停止(暂停时)时(作业控制), 不产生此信号
当一子进程终止时,仍旧产生此信号
若已设置此标志,则当停止的进程继续运行时,作为XSI扩展,不产生SIGCHLD信号
(参照的下面的SA_NOCLDWAIT选项)
SA_NOCLDWAIT 若signo是SIGCHLD,则当调用进程的子进程终止时, 不创建僵死进程
若调用进程在后面调用wait,则阻塞到它所有子进程都终止,此时返回-1,errno设置为ECHILD
SA_NODEFER 当捕捉到此信号时,在执行其信号捕捉函数时,系统不自动阻塞此信号(除非sa_mask成员包括了此信号)
注意,此种类型的操作对应于早期的不可靠信号
SA_NOMASK 同SA_NODEFER SA_ONSTACK 若用sigaltstack已声明了一替换栈,则此信号递送给替换栈上的进程 SA_STACK 同SA_ONSTACK SA_RESETHAND 在此信号捕捉函数的入口处,将此信号的处理方式重置为SIG_DFL,并清除SA_SIGINFO标志
注意,此种类型的信号对应于早期的不可靠信号。但是不能重置SIGILL和SIGTRAP信号的配置
设置此标志使sigaction的行为如同设置了SA_NODEFER标志
SA_ONESHOT 同SA_RESETHAND SA_RESTART 由此信号中断的系统调用自动重启动 SA_SIGINFO 此选项对信号处理程序提供了附加信息:一个指向siginfo结构的指针以及一个指向进程上下文标识符的指针
- 关于SA_INTERRUPT、SA_RESTART说明:
- 某些早期系统(如SunOS)定义了SA_INTERRUPT标志,这些系统的默认方式是重新启动被中断的系统调用,而制定此标志则使系统调用被中断后不再重新启动。Linux定义SA_INTERRUPT标志,以便与使用该标志的应用程序兼容。但是,如若信号处理程序是用sigaction设置的,那么其默认方式是不重新启动系统调用
- Single UNIX Specifiaction的XSI扩展规定,除非说明了SA_RESTART标志,否则sigaction函数不再重启动被中断的系统调用
- 关于系统调用中断请参阅:https://blog.csdn.net/qq_41453285/article/details/89216990
sa_sigaction成员:
- 当sa_flags成员是SA_SIGINFO标志时,就调用此函数,可以来获取该信号的很多详细信息(而不是用来对信号进行处理)
- 参数1:参数为sigaction函数的参数1信号
- 参数2:一个结构体包,含了信号产生的各种详细信息
- si_signo:信号的值
- si_code:信号代码
- si_pid:信号来自于哪一个进程(哪个进程发送来的)
- si_uid:信号来自于哪个用户(发送来的进程的用户)
- si_value(重点):传递的信息。应用程序在传递信号时,可以在si_value.sival_int传递一个整型或者si_value.sival_ptr传递一块内存(下面会有一个案例)
- 若信号时SIGCHLD:则将设置si_pid、si_status、si_uid
- 若信号时SIGBUS、SIGILL、SIGFPE、SIGSEGV:则si_addr包含造成故障的根源地址(该地址可能并不正确)
siginfo_t {
int si_signo; /* Signal number */
int si_errno; /* An errno value */
int si_code; /* Signal code */
int si_trapno; /* Trap number that caused
hardware-generated signal(unused on most architectures) */
pid_t si_pid; /* Sending process ID */
uid_t si_uid; /* Real user ID of sending process */
int si_status; /* Exit value or signal */
clock_t si_utime; /* User time consumed */
clock_t si_stime; /* System time consumed */
sigval_t si_value; /* Signal value */
int si_int; /* POSIX.1b signal */
void *si_ptr; /* POSIX.1b signal */
int si_overrun; /* Timer overrun count; POSIX.1b timers */
int si_timerid; /* Timer ID; POSIX.1b timers */
void *si_addr; /* Memory location which caused fault */
long si_band; /* Band event (was int inglibc 2.3.2 and earlier) */
int si_fd; /* File descriptor */
short si_addr_lsb; /* Least significant bit of address(since Linux 2.6.32) */
}
- 参数3:此参数为void*类型,可以自己传入一些参数进去,但是一般强转为ucontext_t结构体类型使用,该结构体标识信号传递时进程的上下文。下面是ucontext_t结构体内的一些部分成员
ucontext_t *uc_link; /* pointer to context resumed when this context returns */
sigset_t uc_sigmask; /* signals blocked when this context is active */
stack_t uc_stack; /* stack used by this context */
mcontext_t uc_mcontext; /* machine-specific representation of saved context */
//uc_stack字段描述了当前上下面使用的栈,至少包括下列成员
void *ss_sp; /* stack base or pointer */
size_t ss_size; /* stack size */
int ss_flags; /* flags */
- 案例一:处理SIGQUIT信号
#include
#include
#include
void myHandler(int sig);
int main(int argc,char *argv[])
{
struct sigaction act, oact;
act.sa_handler = myHandler;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGQUIT, &act, &oact);
while (1)
{
printf("running...\n");
pause();
}
}
void myHandler(int sig)
{
printf("I got signal: %d.\n", sig);
}
- 按下ctrl+\之后,就会调用myHandler函数
- 案例二:接到SIGUSR1信号时,获取其信息
#include
#include
#include
#include
void func(int signo, siginfo_t *info, void *p)
{
printf("signo=%d\n",signo);
printf("sender sigal pid=%d\n",info->si_pid);
}
int main(int argc,char *argv[])
{
struct sigaction act, oact;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_SIGINFO; //设置此项
act.sa_sigaction=func;
sigaction(SIGUSR1, &act, &oact);
while (1)
{
printf("My pid=%d\n",getpid());
pause();
}
}
- 我们按下ctrl+c之后,就会显示信号值和发送信号的进程
重启动版本
- 系统的signal函数原型太复杂,如下所示
void (*signal(int signo, void (*func)(int)))(int);
- 为了简化起见,我们定义了自己的Sigfunc类型,因此调用函数时只需要传入一个信号值即可
typedef void Sigfunc(int);
Sigfunc *signal(int signo, Sigfunc *func);
Sigfunc *signal(int signo, Sigfunc *func)
{
struct sigaction act, oact;
act.sa_handler = func;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
if (signo == SIGALRM){
#ifdef SA_INTERRUPT
act.sa_flags |= SA_INTERRUPT;
#endif
}
else {
#ifdef SA_RESTART
act.sa_flags |= SA_RESTART;
#denif
}
if (sigaction(signo, &act, &oact) < 0)
return(SIG_ERR);
return(oact.sa_handler);
}
- 函数的使用:
- 参数传入一个信号和一个信号处理函数指针
- 返回值:在函数内部调用sigaction函数,并将相应信号的旧行为作为signal函数的返回值
- 几点说明:
- 要用sigemptyset函数初始化act结构的sa_mask成员,不能简单的设置act.sa_mask=0
- 如果信号是SIGALEM,我们就将sa_flags设置为SA_INTERRUPT,不希望重启动由SIGALRM信号中断的系统调用,原因是:我们希望为IO操作设置超时(可以参考)
- 如果是其他信号,sa_flags标志就加入SA_RESTART,希望重启被中断的系统调用
不重新启动版本
- 如果想要系统调用被中断后不重新启动,则可以使用这一版本
typedef void Sigfunc(int);
Sigfunc *signal_intr(int signo, Sigfunc *func);
Sigfunc *signal_intr(int signo, Sigfunc *func)
{
struct sigaction act, oact;
act.sa_handler = func;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
#ifdef SA_INTERRUPT
act.sa_flags |= SA_INTERRUPT;
#endif
if (sigaction(signo, &act, &oact) < 0)
return(SIG_ERR);
return(oact.sa_handler);
}
- 如果系统定义了SA_INTERRUPT,那么为了提高可移植性。我们在sa_flags中增加该标志,这样就可以组织被中断的系统调用的重启动
演示案例:
- 我们用一个进程给另外一个进程发送SIGINT信号,并且同时将一个整型100发送出去
//发送进程:向一个进程发送SIGINT信号,并且将整型100也传进去
#include
#include
#include
#include
int main(int argc,char *argv[])
{
if(argc!=2)
{
printf("arguments error!");
exit(0);
}
pid_t pid=atoi(argv[1]);//将进程号转化为整数
union sigval v;
v.sival_int=100; //发送整型100
sigqueue(pid,SIGINT,v);
return 0;
}
//此进程等待接受信号的传入,并有SIGINT的信号处理函数
#include
#include
#include
#include
void handler(int,siginfo_t *,void *);
int main(void)
{
struct sigaction act;
act.sa_sigaction=handler;
sigemptyset(&act.sa_mask);
act.sa_flags=SA_SIGINFO;
if(sigaction(SIGINT,&act,NULL)<0)
{
printf("error");
exit(0);
}
for(;;)
pause();
return 0;
}
void handler(int sig,siginfo_t * info,void *ctx)
{
//打印信息
printf("recv a sid=%d data=%d data=%d\n",sig,info->si_value.sival_int,info->si_int);
}