一、信号的基本概念
1、中断:中止当前正在执行任务,转而执行其他任务(可能返回,也可能不返回),中断分为硬件中断(硬件设备产生的中断)和软件中断(其他程序产生的中断)
2、信号是一种软件中断,提供一种异步执行任务的机制
3、常见的信号
SIGINT(2) ctrl+c产生此信号
SIGQUIT(3)ctrl+\产生此信号
SIGABRT(6)调用abort函数,产生此信号
SIGFPE(8)表示一个算术运算异常,例如除以0、浮点溢出等
SIGKILL(9) 不能被捕获或忽略。常用于杀死进程
SIGSEGV(11)试图访问未分配的内存,或向没有写权限的内存写入数据
SIGTSTP(20)Ctrl+Z,产生此信号,并送至前台进程组的所有进程
SIGCHLD(17)子进程状态改变信号,在一个进程终止或停止时,将此信号发送给其父进程
注意:在终端中执行 kill-l可以显示终端中所有信号
4、不可靠信号和可靠信号
建立在早期机制上的信号被称为不可靠信号,1-31
不支持排队,可能会丢失,同一信号产生多次,进程可能只能接收一次
5、可靠信号
采用新的机制产生的信号
34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
支持排队不会丢失
6、信号的来源
硬件产生:除0/非法内存访问。这些异常是通过硬件检查到,并通知内核,然后内核再向引发这些异常的进程发送相应信号 软中断
软件产生:通过kill/raise/alarm/setitmer/sigqueue函数产生
7、信号的处理
1、忽略
2、终止进程
3、终止进程并产生core文件
4、捕获信号并处理
二、信号的捕获
#include<signal.h>
借助信号的注册 sighandler_t signal(int signum,sighandler_t handler)
功能:信号处理注册函数
signum:信号的编号,1~31 也可以是宏
handler:
SIG_IGN 忽略该信号。
SIG_DEL 默认处理
注意:在某些UNIX系统上signal注册的函数上之执行一次,执行完以后,回复成默认处理方式。
如果想长期使用该函数处理信号,可以在函数结束前再注册一次
SIGSTOP SIGKILL 不能被捕获也不能被处理
SIGSTOP 信号会让进程暂停,当再次收到SIGCONT信号会继续执行
普通用户只能给自己的进程发送信号,而root可以给任何进程发送信号
SIGTSTP 可以捕获但不能被处理
三、发送信号
1、键盘
2、错误
3、命令
4、函数
int kill(pid_t pid, int sig);
功能:向指定进程发送id
pid:pid>0 向进城号为pid的进程发送信号
pid=0 向同一进程组发送信号
pid=-1向所有有权利发送信号的进程发送信号
pid=0 向进程号为 abs(pid)的进程组发送信号
sig:信号的编号
sig=0 时kill不会发送信号,但会进行错误检查
四、暂停和休眠
1、int pause(void)
功能:一旦执行,进程就会进行无限的休眠(暂停)直到遇到信号,先执行信号处理函数
才会从休眠中醒来
2、unsigned int sleep(unsigned int seconds);
功能:休眠指定的秒数,当有信号来时会提前醒来返回剩余的秒数
或者睡够了,返回0
五、闹钟
unsigned int alarm(unsigned int seconds)
功能:告诉内核在SECOND秒后向当前进程发送SIGALRM信号
返回值:如果之前设定的时间还没有到,则会重新设置,并返回之前设定的剩余秒数
六、信号集与信号屏蔽
信号集:信号的集合,由128位二进制组成,每一位代表一个信号
int sigemptyset(sigset_t *set);
功能:清空信号集,把所有位设置为0
int sigfillset(sigset_t *set);
功能:填满信号集,把所有位设置为1
int sigaddset(sigset_t *set, int signum);
功能:向信号集中添加一个信号
int sigdelset(sigset_t *set, int signum);
功能:从信号集中删除一个信号
int sigismember(const sigset_t *set, int signum);
功能:判断信号集中是否存在该信号
信号屏蔽:当做一些特殊操作时会希望,有些信号来有些信号不来,
而与设置信号忽略不同的是,信号屏蔽只是暂时不来,而可以获取这段时间发生的信号
每个进程都有一个信号掩码(信号集),其中包括了需要屏蔽的信号,可以通过sigprocmask函数检查修改进程的信号验码
int sigprocmask(int how, const sigset_t* set, sigset_t* oldset);
SIG_BLOCK
设置当前信号集与set的并集为新的信号掩码,添加
SIG_UNBLOCK
当前的信号掩码与set补集的交集,从信号掩码中删除
SIG_SETMASK
把set的当做新的信号掩码,重新设置
set:可为空,则直接获取旧的信号掩码
oldest:旧的信号屏蔽掩码
int sigpending(sigset_t* set);
功能:获取信号屏蔽期间未决的信号,当信号屏蔽解除后,就没有了
注意:在信号屏蔽期间发生的信号,无论多少次,只捕获一次
练习:学生信息管理系统,保存数据和加载数据时,屏蔽ctrl+c ctrl+\等数据加载完成后再处理该信号
七、带附加信息的信号
int sigaction(int signum, const struct sigaction* act,
struct sigaction* oldact);
功能:向内核注册信号处理函数
signum:信号编码 1-31 34-64
act:信号处理方式
oldact:获取到此信号旧的处理方式,可以为空
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_RESETHAND 信号只处理一次,然后就恢复到默认处理方式
SA_RESTART 系统调用如果被signum信号中断,自行重启
SA_NOCLDSTOP 当子进程暂停时不通知父进程
SA_NOCLDWAIT
SA_NODEFER 当执行信号处理函数时,不屏蔽正常处理的信号
SA_ONSTACK 使用第二个函数指针
SA_SIGINFO (since Linux 2.2)
int sigqueue(pid_t pid, int sig, const union sigval value);
功能:信号发送函数,与kill不同的是可以额外附加一些数据
pid:目标进程号
sig:要发送的信号
value:联合,成员可以是指针或证书
union sigval {
int sival_int;
void *sival_ptr;
};
八、计时器
1、系统为每个进程维护三个计时器
ITIMER_REAL 真实计时器,程序运行实际所用时间
ITIMER_VIRTURE 虚拟计时器,程序运行在用户态所消耗的时间
ITIMER_PROF 实用计时器,程序在用户态和内核态所消耗的时间
实际时间 = 用户时间 + 内核时间 + 睡眠时间
int getitimer(int which, struct itimerval* curr_value);
功能:获取当前进程的定时器
which:选择使用哪些计时器
struct timeval it_interval;第一次触发始终信号所需的
struct timeval it_value
int setitimer(int which, const struct itimerval* new_value,
struct itimerval* old_value);
文件读写时为了提高效率,增加了缓冲区,所以当数据写入到文件中,数据并没有写入到文件,而那是暂时写入到缓存区中,只有达到某些条件才写入文件
1、由写入状态切换到读取
2、遇到\n符
3、缓冲区满4k
4、手动刷新fflush(FILE*)
5、文件关闭