中断,中止(非终)当前正在运行的任务,转而执行其他任务(可能返回也可能不返回),中断分为硬件中断(硬件设备产生的中断)和软件中断(其他程序产生的中断)
信号:是一种软件中断,提供了一种异步执行任务的机制
常见的信号
SIGINT(2)
Ctrl+CSIGQUIT(3)
Ctrl+\SIGABRT(6)
调用abort函数,产生此信号SIGFPE(8)
表示一个算术运算异常,例如除以0、浮点溢出等SIGKILL(9)
不能被捕获或忽略。常用于杀死进程SIGSEGV(11)
试图访问未分配的内存,或向没有写权限的内存写入数据SIGCHLD(17)
在一个进程终止或停止时,将此信号发送给其父进程SIGTSTP(20)
Ctrl+Z不可靠信号
kill -l
可以显示所有信号
建立在早期机制上的信号被称为不可靠信号,1~31
不支持排队可能丢失,同一个信号产生多次,系统可能只接收到一次
可靠信号
采用新的机制产生的信号,34~64
支持排队,不会丢失
信号的来源
硬件产生:除0,非法内存访问
这些异常是硬件(驱动)检测到,并通知内核,内核再向引发这些异常的进程发送相应信号
软件产生:通过kill/raise/alarm/setitmer/sigqueue
函数
信号的处理
信号 | 解释 | 产生条件 | 默认动作 |
---|---|---|---|
SIGHUP(1) | 连接断开信号 | 如果终端接口检测一个连接断开,则将此信号发送给与该终端相关的控制进程(会话首进程) | 终止 |
SIGINT(2) | 终端中断符信号 | 用户按中断键(Ctrl+C),产生此信号,并送至前台进程组的所有进程 | 终止 |
SIGQUIT(3) | 终端退出符信号 | 用户按退出键(Ctrl+\),产生此信号,并送至前台进程组的所有进程 | 终止+core |
SIGILL(4) | 非法硬件指令信号 | 进程执行了一条非法硬件指令 | 终止+core |
SIGTRAP(5) | 硬件故障信号 | 指示一个实现定义的硬件故障。常用于调试 | 终止+core |
SIGABRT(6) | 异常终止信号 | 调用abort函数,产生此信号 | 终止+core |
SIGBUS(7) | 总线错误信号 | 指示一个实现定义的硬件故障,常用于内存故障 | 终止+core |
SIGFPE(8) | 算术异常信号 | 表示一个算术运算异常,例如除以0、浮点溢出等 | 终止+core |
SIGKILL(9) | 终止信号 | 不能被捕获或忽略。常用于杀死进程 | 终止 |
SIGUSR1(10) | 用户定义信号 | 用户定义信号,用于应用程序 | 终止 |
SIGSEGV(11) | 段错误信号 | 试图访问未分配的内存,或向没有写权限的内存写入数据 | 终止+core |
SIGUSR2(12) | 用户定义信号 | 用户定义信号,用于应用程序 | 终止 |
SIGPIPE(13) | 管道异常信号 | 写管道时读进程已终止,或写SOCK_STREAM类型套接字时连接已断开,均产生此信号 | 终止 |
SIGALRM(14) | 闹钟信号 | 以alarm函数设置的计时器到期,或以setitimer函数设置的间隔时间到期,均产生此信号 | 终止 |
SIGTERM(15) | 终止信号 | 由kill命令发送的系统默认终止信号 | 终止 |
SIGSTKFLT(16) | 数协器栈故障信号 | 表示数学协处理器发生栈故障 | 终止 |
SIGCHLD(17) | 子进程状态改变信号 | 在一个进程终止或停止时,将此信号发送给其父进程 | 忽略 |
SIGCONT(18) | 使停止的进程继续 | 向处于停止状态的进程发送此信号,令其继续运行 | 继续/忽略 |
SIGSTOP(19) | 停止信号 | 不能被捕获或忽略。停止一个进程 | 停止进程 |
SIGTSTP(20) | 终端停止符信号 | 用户按停止键(Ctrl+Z),产生此信号,并送至前台进程组的所有进程 | 停止进程 |
SIGTTIN(21) | 后台读控制终端信号 | 后台进程组中的进程试图读其控制终端,产生此信号 | 停止 |
SIGTTOU(22) | 后台写控制终端信号 | 后台进程组中的进程试图写其控制终端,产生此信号 | 停止 |
SIGURG(23) | 紧急情况信号 | 有紧急情况发生,或从网络上接收到带外数据,产生此信号 | 忽略 |
SIGXCPU(24) | 超过CPU限制信号 | 进程超过了其软CPU时间限制,产生此信号 | 终止+core |
SIGXFSZ(25) | 超过文件长度限制信号 | 进程超过了其软文件长度限制,产生此信号 | 终止+core |
SIGVTALRM(26) | 虚拟闹钟信号 | 以setitimer函数设置的虚拟间隔时间到期,产生此信号 | 终止 |
SIGPROF(27) | 虚拟梗概闹钟信号 | 以setitimer函数设置的虚拟梗概统计间隔时间到期,产生此信号 | 终止 |
SIGWINCH(28) | 终端窗口大小改变信号 | 以ioctl函数更改窗口大小,产生此信号 | 忽略 |
SIGIO(29) | 异步I/O信号 | 指示一个异步I/O事件 | 终止 |
SIGPWR(30) | 电源失效信号 | 电源失效,产生此信号 | 终止 |
SIGSYS(31) | 非法系统调用异常 | 指示一个无效的系统调用 | 终止+core |
sighandler_t signal(int signum, sighandler_t handler);
typedef void (*sighandler_t)(int);
SIG_IGN
忽略该信号SIG_DEL
默认处理注意 在某些UNIX系统上,signal注册的函数只执行一次,执行完后恢复默认处理方式,如果想长期使用处理函数处理,在处理函数结尾再注册一遍
SIGTSTP(20)
可以被捕获,但不能被处理SIGKILL(9)/SIGSTOP(19)
既不能被捕获,也不能被处理SIGSTOP
信号可以让进程暂停,当再次收到SIGGCONT
信号时会继续执行kill -[信号] [PID]
练习1 实现一个"死不掉的进程",当收到信号后给出信号产生的原因
homework
#include
#include
#include
#include
void sigfunc(int signum){
printf("我是进程%u,我收到了内核发送的%d\n",getpid(),signum);
exit(0);
}
int main(){
signal(SIGSEGV,sigfunc);
int* p = NULL;
*p = 20; //信号处理完后返回到收到信号前的那行
for(;;);
}
SIGINT(2)
SIGQUUIT(3)
SIGTSTP(20)
SIGFPG(8)
SIGSEGV(11)
kill -signum pid
ps -aux
查看所有进程int kill(pid_t pid, int sig);
int raise(int sig);
int pause(void);
unsigned int sleep(unsigned int seconds);
unsigned int alarm(unsigned int seconds);
信号集 信号的集合,由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);
信号屏蔽 当做一些特殊操作时,会希望有些信号来,而有些信号不要来,而与设置信号忽略 不同 的是:信号屏蔽只是暂时不来,而可以获取到着一段时间发生了哪些信号
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
int sigpending(sigset_t *set);
注意 在信号屏蔽期间发生的不可靠信号只捕获一次
练习2 学生信息管理系统,在保存数据和加载数据时屏蔽Ctrl+c Ctrl+,等数据加载,保存完后再处理该信号
homework
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
struct sigaction {
void (*sa_handler)(int); //简单的信号处理函数指针
void (*sa_sigaction)(int, siginfo_t *, void *); //可以带附加信息的信号处理函数指针
sigset_t sa_mask; //当执行信号处理函数时需要屏蔽的信号
int sa_flags;
/*
SA_NOCLDSTOP //当子进程暂停时,不通知父进程
SA_NOCLDWAIT //
SA_NODEFER //当执行信号处理函数时,不屏蔽正常处理的信号
SA_ONSTACK //
SA_RESETHAND //信号只处理一次就恢复默认处理方式
SA_RESTART //系统调用如果被signum信号中断,自行重启
SA_RESTORER //
SA_SIGINFO //使用第二个函数指针处理信号
*/
void (*sa_restorer)(void); //保留
};
#include
#include
#include
void sa_sigint(int sig, siginfo_t * info, void *ptr){
printf("%d,%s,%p",sig,info->si_ptr,ptr);
}
int main(){
struct sigaction act = {};
act.sa_signation = siginit;
act.sa_flags = SA_SIGINFO;
sigaction(SIGINT,&act,NULL);
union sigval value;
value.sival_ptr = "hehe";
sigaction(int signum, const struct sigaction *act,
struct sigaction *oldact);
}
int sigqueue(pid_t pid, int sig, const union sigval value);
int getitimer(int which, struct itimerval *curr_value);
struct itimerval {
struct timeval it_interval; /* Interval for periodic timer */
struct timeval it_value; /* Time until next expiration */
};
struct timeval {
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds 0~999999 */
};
int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);
#include
#include
#include
#include
void sigalrm(int sig){
printf("its time\n");
}
int main(){
signal(SIGALRM,sigalrm);
//struct timeval it_value = {0,999999};
//struct timeval it_interval = {0,999999};
struct itimerval curr_value = {{0,999999},{0,999999}};
setitimer(ITIMER_REAL,&curr_value,NULL);
for(;;);
}
练习 使用setitimer实现秒表
homework
由于文件读写时为了提高效率,增加了缓冲区,所以当进行写操作时,数据并没有立即写入文件,而是暂时存储缓冲区中,只有达到某些条件时才写入文件。