信号
信号是比较复杂的通信形式,用来通知进程有某种事件要发生,除了用于进程之间的通信之外,信号还可以用来发送信号给进程本身。
信号的来源有两个,一个来自于硬件,比如我们的键盘和鼠标,而一个则来自于软件,比如我们的函数signal,kill,alarm函数等等。同时,一些非法的操作和设备故障也会引发信号。
同步信号&异步信号
由进程的某个操作产生的信号称为同步信号(synchronous signals),例如除0;由象用户击键这样的进程外部事件产生的信号叫做异步信号。(asynchronous signals)。
进程不知道异步信号具体什么时候会发生!!!
进程对信号的回应
1>忽略处理
2>捕捉处理,用户自定义函数,捕捉到信号的时候,执行自己定义的函数。
3>默认处理,linux对于特定的信号有自己的默认处理。
几种常见的信号
信号的路径:
cd /usr/include/bits/signum.h
这个文件里面放了所有的信号和对应的宏。
几种常见的信号
信号 | 数字 | 操作 | 效果 |
---|---|---|---|
SIGINT | 2 | CTRL+C | interrupt |
SIGABRT | 6 | Abort | 程序异常终止 |
SIGKILL | 9 | Kill | 杀死一个进程 |
SIGALRM | 14 | alarm | 发闹钟 |
SIGCHID | 17 | —— | 子进程状态改变发送给父的信号(或者进程1) |
发送信号的函数
常见的发送信号的函数
:raise函数 ,kill函数,alarm函数,abort函数,sigqueue函数,setitimer函数等。
1 raise函数
NAME
raise - send a signal to the caller
//向进程自身发送一个信号
SYNOPSIS
#include <signal.h>
int raise(int sig);
DESCRIPTION
The raise() function sends a signal to the calling process or thread.
In a single-threaded program it is equivalent to
kill(getpid(), sig);
单线程相当于kill函数
In a multithreaded program it is equivalent to
pthread_kill(pthread_self(), sig);
返回值
成功返回0,失败返回非0
raise(9)相当于进程杀死自己。
2 kill函数
NAME
kill - send a signal to a process or a group of processes
//向进程或者进程组发送信号
SYNOPSIS
#include <signal.h>
int kill(pid_t pid, int sig);
可以指定某个进程,也可以指定具体的信号。
DESCRIPTION
The kill() function shall send a signal to a process or a group of pro‐
cesses specified by pid. The signal to be sent is specified by sig and
is either one from the list given in <signal.h> or 0. If sig is 0 (the
null signal), error checking is performed but no signal is actually
sent. The null signal can be used to check the validity of pid.
alarm函数 向进程发送SIGALRM信号
#include <unistd.h>
unsigned int alarm(unsigned int seconds)
专门为SIGALRM信号而设,在指定的时间seconds秒后,
将向进程本身发送SIGALRM信号,又称为闹钟时间。
进程调用alarm后,任何以前的alarm()调用都将无效。
如果参数seconds为零,那么进程内将不再包含任何闹钟时间。
返回值,如果调用alarm()前,进程中已经设置了闹钟时间,
则返回上一个闹钟时间的剩余时间,否则返回0。
pause函数
NAME
pause - suspend the thread until a signal is received
//在收到信号之前挂起进程或者线程
SYNOPSIS
#include <unistd.h>
int pause(void);
DESCRIPTION
The pause() function shall suspend the calling thread
until delivery of a signal whose action is either to
execute a signal-catching function or to terminate
the process.//pause将挂起线程直到受到信号,信号可能是执行一个信
号捕捉函数或者终止进程。
If the action is to terminate the process,
pause() shall not return.
//如果是终止进程,pause函数没有返回值
If the action is to execute a signal-catching function,
pause() shall return after the signal-catching function
returns.
如果动作是执行信号捕捉函数,那么pause函数会在信号捕捉函数返回
之后返回
信号关联函数
如果进程要处理某一信号,那么就要在进程中安装该信号。安装信号主要用来确定信号值及进程针对该信号值的动作之间的映射关系,即进程将要处理哪个信号;该信号被传递给进程时,将执行何种操作。
linux主要有两个函数实现信号的安装:signal()、sigaction()。其中signal()在可靠信号系统调用的基础上实现, 是库函数。而sigaction()是较新的函数(由两个系统调用实现:sys_signal以及sys_rt_sigaction),有三个参数,支持信号传递信息,主要用来与 sigqueue() 系统调用配合使用,当然,sigaction()同样支持非实时信号的安装。sigaction()优于signal()主要体现在支持信号带有参数。
sigaction比signal更加全面!!
signal 函数
#include <signal.h>
void (*signal(int signum, void (*handler))(int)))(int);
如果该函数原型不容易理解的话,可以参考下面的分解方式来理解:
typedef void (*sighandler_t)(int);
//返回值为0 函数参数是int型的函数指针
sighandler_t signal(int signum, sighandler_t handler));
第一个参数指定信号的值,第二个参数指定针对前面信号值的处理,可以忽略该信
号(参数设为SIG_IGN);可以采用系统默认方式处理信号(参数设为SIG_DFL);
也可以自己实现处理方式(参数指定一个函数地址)。
如果signal()调用成功,返回最后一次调用的自定义关联函数额地方值。
而调用signal()时的handler值;失败则返回SIG_ERR。
signal函数调用示例:
#include<stdio.h>
#include<signal.h>
#include<unistd.h>
void func1(int sig)
{
printf("我的闹钟响了!\n");
}
int main()
{
signal(SIGALRM,&func1);
alarm(3);
sleep(4);
return 0;
}
执行效果:
[kiosk@foundation45 Desktop]$ gcc alarm.c
[kiosk@foundation45 Desktop]$ ./a.out
我的闹钟响了!
sigaction函数
#include <signal.h>
int sigaction(int signum,const struct sigaction *act,
struct sigaction*oldact));
//如果不关心旧的处理,那么传NULL指针。
//也可以只关心旧的!
sigaction也用于注册一个信号处理函数。
参数signum为需要捕捉的信号;
参数 act是一个结构体,里面包含信号处理函数地址、处理方式等信息。
参数oldact是一个传出参数,sigaction函数调用成功后,oldact里面包含以前对signum的处理方式的信息。
如果函数调用成功,将返回0,否则返回-1
结构体 struct sigaction(注意名称与函数sigaction相同)的原型为:
struct sigaction {
void (*sa_handler)(int); // 老类型的信号处理函数指针
void (*sa_sigaction)(int, siginfo_t *, void *);//新类型的信号处理函数指针
sigset_t sa_mask; // 将要被阻塞的信号集合
int sa_flags; // 信号处理方式掩码
void (*sa_restorer)(void); // 保留,不要使用。
}
第二个参数最为重要,其中包含了对指定信号的处理、信号所传递的信息、
信号处理函数执行过程中应屏蔽掉哪些函数等等。
sigaction函数应用示例:
实现自己的sleep函数:
#include<stdio.h>
#include<signal.h>
#include<unistd.h>
void func1(int sig)
{
}
void my_sleep(int seconds)
{
//定义一个参数类型的结构体
struct sigaction act;
//结构里面的响应函数指针绑定用户自定义的函数
act.sa_handler = func1;
//调用信号关联函数,不关心旧的函数执行情况,置NULL
sigaction(SIGALRM,&act,NULL);
//发闹钟信号
alarm(seconds);
//挂起进程知道信号捕捉函数返回之后再返回
pause();
}
int main()
{
my_sleep(3);
printf("haha\n");
return 0;
}