中断:停止当前执行的程序而去执行新的程序或处理意外的过程;
中断分为两大类:硬件中断,软件中断
信号的处理:
本质上就是一种软件中断,即可以作为两个进程间通信的一种机制;
更重要的是,信号总是可以终止一个程序的执行,他更多的被用与处理意外情况;
信号的特性:
1.信号是异步的,进程不知到什么时候知道信号回来;
2.进程即可以发送信号,也可以处理信号;
3.每个信号都有一个名字,用SIG开头
信号的基本命令和分类:
kill -l:查看当前系统所支持的所有信号;
要求掌握的信号:
SIGINT: 信号2,使用ctril + c 产生该信号,默认终止进程
SIGQUIT: 信号3,使用ctril + \ 产生该信号,默认终止进程
SIGKILL: 信号9,使用kill + 9 产生该信号,默认终止进程
(该信号只能采用默认处理,不能被用户捕捉,也就是不能用signal函数处理)
基本分类:
1~31之间的信号叫做不可考信号,不支持排队,信号可能会丢失,也叫做实时信号;
34~64之间的信号叫做可靠信号,支持排队,信号不会丢失,也叫做实时信号;
信号的处理方式:
1.默认处理:绝大多数信号的默认处理方式都是终止进程
2.忽略处理:
3.自定义处理/捕获处理:
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
第一个参数:具体的信号值/信号名称
第二个参数:具体的处理方式
SIG_IGN:忽略处理
SIG_DFL:默认处理
自定义函数的地址:自定义处理
返回值:成功返回之前的处理方式,失败返回SIG_ERR;
函数功能:主要用于设置指定信号的处理方式
父子进程对信号处理方式的比较:
1.对于fork函数创建的子进程来说,完全照搬父进程对信号的处理方式;
即父进程怎么处理子进程就怎么处理;
2.对于vfor函数和execl函数启动的子进程来说
父进程忽略,子进程也忽略
父进程默认,子进程叶默认
父进程子定义,子进程默认处理
发送信号的主要方式
1.采用键盘发送信号(只能发送部分特殊的信号)
2.程序出错发送信号(只能发送部分特殊的信号):段错误 发送信号SIGSEGV 11
3.使用kill命令发送信号(所有信号都可以发送):kill -信号值 进程号
4.采用系统函数发送信号:kill()/raise()/alarm()/......
发送信号系统函数的解析:
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
第一个参数:进程编号
第二个参数:信号值 如果是0,表示不会发送信号;只是检查进程是否存在
函数功能:主要用于给指定进程发送参数指定的信号
返回值:成功0;失败-1;
#include <signal.h>
int raise(int sig);
函数功能:主要用于给当前正在调用的进程/线程发送参数指定的信号;即给自己发送信号
对于单线程的程序来说,等价于kill(getpid(),sig);
返回值:成功返回0;失败返回非0
sleep():主要用于当前正在调用的线程进入睡眠状态;
直到指定的秒数睡了,或者一个不能被忽略的信号达到了;
如果参数指定的秒数过去了,则返回0;否则返回还没有来得及的秒数,也就是剩余的秒数;
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
函数功能:主要用于经过参数指定的秒数之后,给当前正在调用的进程发送SIGALRM信号;
每次重新设置闹钟之后都会取消之前的闹钟,当前参数为0时,专门用于取消闹钟;
如果之前没有闹钟,则返回0;否则返回之前闹钟剩余的秒数;
信号集的基本概念:本质上就是若干个信号组成的集合
操作系统内部使用数据类型:sigset_t 表示信号集的数据类型,该数据类型的大小:128个字节
信号集的基本操作:
1.sigemptyset(): 清空信号集
2.sigfillset(): 填满信号集
3.sigaddset(): 添加信号到信号集中
4.sigdelset(): 删除信号集中的信号
5.sigismember(): 判断信号是否存在
#include <signal.h>
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);
*************信号的屏蔽****************
在某些特殊程序的执行过程中,可能不允许被信号打断,
此时就需要使用信号的屏蔽技术来解决该问题;
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
第一个参数:具体的屏蔽方式
SIG_BLOCK:使用当前屏蔽集 + 参数set屏蔽集
即 ABC + CDE(set) => ABCDE
SIG_UNBLOCK:当前屏蔽集 - 参数set屏蔽集
即 ABC - CDE(set) => AB
SIG_SETMASK:使用参数set屏蔽集 替换 当前屏蔽集
即 ABC + CDE(set) => CDE
第二个参数:信号集类型的指针,用于指定新的信号集
第三个参数:信号集类型的指针,用于带出之前屏蔽的信号集
如果不想带出之前的屏蔽集,该参数给NULL即可;
函数功能:
主要用于检查/修改即将屏蔽的信号集
返回值:成功返回0;失败返回-1;
注意:信号的屏蔽并不是删除信号,而是相当于用一个隔板把信号挡起来;
对于可靠信号来说,产生多少次,则在隔板的另一侧会排队等待多少个;
而对于不可考信号来说,无论产生了多少次,在隔板的另一侧只有一个信号在排队等待,
当信号的屏蔽解除时,相当于将隔板移开,只要进程不结束,则全部依次处理;
#include <signal.h>
int sigpending(sigset_t *set);
函数功能:
主要用于获取信号屏蔽期间来过但没有来得及处理的信号,通过参数set返回;
****************************************************
计时器:
在linux系统中,操作系统会为每一个进程管理3中计时器;
分别是:真实计时器,虚拟计时器,实用计时器;
一般才有真实计时器;
#include <sys/time.h>
int getitimer(int which, struct itimerval *curr_value);
int setitimer(int which, const struct itimerval *new_value,
struct itimerval *old_value);
第一个参数:计时器的类型
ITIMER_REAL: 真实计时器,产生SIGALRM信号
ITIMER_VIRTUAL: 虚拟计时器,产生SIGVTALRM信号 进程在用户态下消耗的时间
ITIMER_PROF: 实用计时器,产生SIGPROF信号 进程在用户态和内核态消耗的总时间
第二个参数:机构体指针,用于指定计时器的新值
第三个参数:结构体指针,用于带出设置之前的旧值
struct itimerval {
struct timeval it_interval; /* 间隔时间/next value */
struct timeval it_value; /* 启动时间/current value */
};
struct timeval {
long tv_sec; /* 秒数/seconds */
long tv_usec; /* 微秒/microseconds */
};