当程序接收到信息后终止了当前正在执行的程序,转而执行其他的任务,等其他任务执行完成后可能再返回继续执行,这种执行模式称为中断执行
分为硬件中断和软件中断
硬件中断由外设硬件产生的中断,例如键盘、鼠标都可以产生,可以屏蔽
软件中断是程序执行了中断指令后产生的中断
信号是一种软件中断,是由操作系统发出的中断信号,被程序就收后执行相应的操作
kill -l 显示所有信号
SIGINT(2) Ctrl+c 终止
SIGQUIT(3) Ctrl+\ 终止+core(内存映像 内存崩溃时)
SIGFPE(8) 除0\溢出 终止+core(内存映像 内存崩溃时)
SIGKILL(9) 用于杀死进程 终止
SIGSEGV(11) 非法访问内存 终止
建立在早期的信号处理机制上的信号(1~31),称为不可靠信号
不支持排队机制,可能会丢失信号,如果同一个信号连续发送多次,进程可能只接收到了一次
建立在新的信号处理机制上的信号(34~64),称为可靠信号
支持排队机制,如果信号连续发送,也不会丢失
硬件异常:除0,非法访问内存,使用了未定义的指令,总线错误等
软件异常:通过一些命令、函数产生异常
1、忽略
2、终止进程
3、终止进程+产生core文件
4、捕获并处理信号(在信号发出前,向内核注册一个信号处理函数,绑定该信号与信号处理函数,当信号发出后,会处理信号处理函数)
信号捕获和注册
typedef void (*sighandler_t)(int);
注意:信号处理函数的格式
参数int表示是导致执行该函数的信号的ID
sighadler_t signal(int signum,sighadler_t headler)
功能:向内核提出绑定一个信号处理函数
signal:信号编号
headler:信号处理函数的函数名
还可以写成:
SIG_IGN 忽略处理
SIG_GFL 按默认方式处理
返回值:返回绑定之前的信号处理函数指针,一般用于记录还原
注意:有个别操作系统通过signal注册的信号只能执行一次信号处理函数,如果想要持续有效,可以在信号处理函数中再重新signal注册一次。
注意:虽然有些信号可以捕获,但是产生该信号的错误原因依旧还在(段错误、除0),如果信号处理函数执行完后不终止进程,就会返回到产生错误的位置,导致死循环,正确操作是保存数据后,终止程序
信号发出的方式:
键盘:
Ctrl+c
Ctrl+\
Ctrl+z 暂停\挂起 fg命令恢复最后暂停的进程
错误:
非法访问内存
除0
硬件的故障、总线错误
(捕获后要及时终止进程)
命令:
kill -信号id 进程号
killall -信号id 进程名
发送信号给所有名字为同名的进程
函数:
int kill(pid_t pid, int sig);
功能:向指定的进程发送信号
pid:进程号
sig:信号
int rasie(int sig);
功能:给进程自己发送信号
void abort(void);
功能:给进程自己发送SIGABRT(6)信号
unsigned int alarm(unsigned int seconds);
功能:让内核在seconds秒后想进程自己发送SIGALRM(14)信号
返回值:上一次调用后,闹钟剩余的秒数
注意:如果重复调用该函数,只会覆盖之前的设置时间,而不会产生多次闹钟信号
int pause(void);
功能:让调用者进程进入休眠态,直到遇到信号后就会唤醒
返回值:要么一直不返回,要么信号来了返回-1
unsigned int sleep(unsigned int seconds);
功能:让调用者进程进入休眠态指定秒数,当遇到信号时会提前返回
返回值:还在休眠中不返回,唤醒后返回剩余休眠秒数
信号集:是一种数据类型,定义出来的变量可以存储表示多个信号
sigset_t 128位的二进制数
用每一个二进制位来表示某种信号有无
相关函数:
int sigemptyset(sigset_t *set);
功能:清空信号集
int sigfillset(sigset_t *set);
功能:填满信号集
int sigaddset(sigset_t *set, int signum);
功能:向信号集中添加某个信号
int sigdelset(sigset_t *set, int signum);
功能:从信号集中删除某个信号
int sigismember(const sigset_t *set, int signum);
功能:测试查看信号集中是否有某个信号
返回值:
0 不存在
1 存在
-1 信号非法
信号屏蔽:
当程序执行一些特殊操作时是不合适处理信号的,此时可以让内核先屏蔽信号处理,等特殊操作完成后再继续处理信号
当信号产生时,内核会在该进程中的信号维护表中为该进程标记此信号,一旦完成标记进程就开始响应信号,次过程称为递送
从信号产生到完成递送过程有一个时间间隔,处于这个时间间隔的信号的状态叫未决
信号屏蔽就是让信号处于未决状态,暂停递送,当屏蔽解除时再继续进行递送
一般每个进程都有一个信号集用于存储要屏蔽的信号,称为信号屏蔽集
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
功能:设置信号屏蔽集
how:信号屏蔽的方式
SIG_BLOCK
把set中信号添加到信号屏蔽集中
SIG_UNBLOCK
从信号屏蔽集中删除set中的信号
SIG_SETMASK
把信号屏蔽集的信号全部替换成set中的信号
set:准备设置的信号集
oldset:获取旧的信号屏蔽集
int sigaction(int signum, const struct sigaction *act,
struct sigaction *oldact);
功能:向内核处理一个信号处理函数,相当于signal的升级版
signum:要捕获的信号
act:
struct sigaction {
void (*sa_handler)(int);//不附带数据的信号处理函数
void (*sa_sigaction)(int, siginfo_t *, void *);//附带附加数据的信号处理函数
sigset_t sa_mask; //信号屏蔽集
//在该函数执行时,默认会屏蔽当前信号,如果想要屏蔽其他信号,可以向sa_mask中增加信号
int sa_flags; //绑定信号的标志位
SA_SIGINFO 使用第二个函数指针进行绑定
SA_NODEFER 在信号绑定过程中不要屏蔽该信号
SA_RESETHAND 在该信号处理方式执行完后,还原回默认的处理方式
SA_RESTART 系统调用一旦被该绑定的信号打断后可以自动重启系统调用
void (*sa_restorer)(void); //保留项
};
int sigqueue(pid_t pid, int sig, const union sigval value);
功能:向指定的进程发送附带有数据的信号
pid:进程号
sig:信号ID
value:要附带的数据
union sigval {
int sival_int; //整数
void *sival_ptr; //指针
};
注意:通过这样附带信息的操作,可以让不同的进程进行数据通信
定时器:
int getitimer(int which, struct itimerval *curr_value);
功能:获取当前定时方案
which:选择计时器
ITIMER_REAL 真实计时器 程序的总运行时间SIGALRM
ITIMER_VIRTUAL 虚拟计时器 用户态的运行时间SIGVTALRM
ITIMER_PROF 实际计时器 用户态+内核态的运行时间SIGPROF
真实计时器 = 实际计时器+切换时间+休眠时间
int setitimer(int which, const struct itimerval *new_value,
struct itimerval *old_value);
功能:设置新的定时方案
struct itimerval {
struct timeval it_interval; // 每次计时器产生信号的时间间隔
struct timeval it_value; // 第一次信号产生的时间
};
struct timeval {
time_t tv_sec; // 秒
suseconds_t tv_usec; // 微秒
};