一、信号的基础。
1、信号的基本概念。
@可以用kill -l来查看系统中的信号,这些信号的定义在signal.h文件中。
@信号的编号没有0信号,是从1开始的。
2、产生信号。
@产生进程有5种方式。
用户按下按键;硬件异常产生信号(被0除);调用kill(2)函数可以发送信号给另一个进程;kill(1)命令发送信号;内核检测到某种软件条件发生。如alarm
@当就绪或者是阻塞的进程,遇到信号都会被激活来处理信号。
3、处理信号。
@有三种处理方法。忽略。注册一个处理函数。系统默认执行。
4、常用信号的使用方法。
@Ctrl +c向当前进程发送一个SIGINT信号。
@Ctrl +/ 使程序结束运行,而产生core文件。可以利用gdb来调试core文件。
二、信号的影响。
1、重入。
一个函数符合以下条件之一都是不可重入的。
@使用全局变量。例如全局变量或静态变量。
@调用了动态方法得到内存。
@使用了标准的IO库。标准的IO库的很多实现都以不可重入的方式使用全局数据结构。
总结:使用全局的东西的函数都不行。称为非纯代码。
2、原子操作。
如果多个流程访问一个全局资源,那么这个全局要定义为:
如: volatile sig_atomic_t a=0;
3、中断系统调用。
@使用sigactions可以设置信号处理程序返回时是否重新启动被中断的系统调用。
三、信号处理函数。
1、设置信号处理函数。
@使用signal函数来处理函数加载。
#include<signal.h>
void (*signal (int signo,void(*fuc)(int))) (int);
第一个参数,表示要加载处理的信号的编号。例如:SIGKILL其本质是整数。
第二个参数,表示一个函数的指针。也可以是以下三个之一。
SIG_IGN:表示忽略该信号。(STGKILL SIGSTOP信号不够忽略。)
SIG_DFL表示使用默认的信号处理方式。
用定义好的函数指针。
一般用法:
void signal_handle(int signo)
{
switch(signo){
case SIG :
。。。。。
}
@有两个信号没有特殊的意义的:SIGUSR1,SIGUSR2
2、发送信号。
@使用kill向进程或进程组发送信号。
#include<signal.h>
int kill(pid_t pid, int signo);
pid有4种不同的写法。
大于0:信号发给pid 的进程。
等于0:发送给进程组ID和该进程相同的进程。
小于0:发送给进程组内进程ID为pid的进程。
等于-1:信号发送给所有的进程。
注意事项:
@系统进程不能接收信号。
进程之间发送信号需要权限的检查。
(1)uid=uid:可以,同一用户的进程之间的信号通信。
(2)uid=eid:接收者得到了发送者的授权,暂时成为与发送者同一个用户的进程。
(3)eid=uid:发送者得到接收者的暂时成为与接收者同一个用户的进程。
(4)eid=eid:发送者的接收者都暂时属于同一用户,等同地第一种情况。
根用户可以向任何用户发送信号。如果成功则返回0,不成功返回-1,
@可以向自己发送信号。如kill(getpid(),signo);
3、向进程本身发送信号。
int raise(int signo);
@可以使进程退出。但是只是没有做善后的工作。如关闭文件,冲洗流。
4、设置Linux定时器。
@#include<unistd.h>
uinsigned int alarm(unsigned int seconds);
@当系统时间超过该时间后,就会调用alarm函数的进程发送一个SIGALRM的函数的进程发送一个信号SIGALRM的信号。终止调用alarm函数的进程。
@当之前没有设置过定时器,或者设置过的定时器已过时,则返回0;
@当之前设置过,还没有过时,则返回剩余的秒数。
@当alarm的参数为0时,取消一个定时器,返回剩余的秒数。
5、定时等待IO。
@当在等待io时可以用alarm用设置定时阻塞。
6、挂起进程。
@进程有三个状态:运行;就绪和阻塞。
@当一个进程 在就绪态,但是我想他进入阻塞态时。可以调用。pause函数。
#include<unistd.h>
int pause(void);
直到有一个信号到来,并且执行了一个信号处理程序从其返回后,pause函数返回-1,表示执行正确。挂起后不再响应SIGTERM等很多信号。唯一能够保证用kill.
7、进程休眠。
@pause使进程无时间限制的挂起,若想在一定时间恢复运行则使用sleep.
unsigned int sleep(unsigned int nsec);
返回值有两种:当挂起的时间超过了指定时间,返回0;当时间还没有到就被信号唤醒,还返回挂起来的时间。
四、信号集与屏蔽信号。
1、信号集和信号处理函数。
@进程能够捕捉并且处理的信号集合成为信号集。
@#include<signal.h>
int sigemptyset(sigset_t *set):清空set,设置为0;
int sigfillset(sigset_t *set);设置为1;
int sigaddset(sigset_t* set, int signo);指定信号编号设置为1
int sigdelset(sigset_t * set ,int signo);指定信号编号设置为0
@使用sigismember函数测某个信号所对应的位是否被设置,
int sigismember(sigset_t *set,int signo);
被设置返回1,未设置返回0,失败则返回-1.
@信号的编号是由1开始的,但是信号集是从0开始的。signo的值应该是信号编码减去1;
2、屏蔽信号。
@信号的屏蔽就是阻塞一个信号 。信号屏蔽字的本质同信号集一样,是一个位向量。信号编码对应的位为1表示屏蔽该信号,对应的位为0表示处理该信号。
@Linux环境sigprocmask函数设置信号屏蔽字,其函数原型如下:
#include <signal.h>
int sigprocask(int how,cosnt sigset_t *restrict set, sigset_t* restrict oset)
第二个参数set是一个信号集。
第三个参数oset被设置为原来的信号屏蔽字。
第一个参数how有三种取值情况。
SIG_BLOCK:set包含了希望添加到当前屏蔽信号集的信号。相当于mask=mask | set;
SIG_UNBLOCK:set包含了希望从当前屏蔽信号中解除阻塞的信号,相当于mask=mask & ~ set;
SIG_SETMASK:设置当前屏蔽信号集为set所指向的值,相当于mask=set;
如果set的值是NULL,则无论how是何值都不会更改信号屏蔽字。这种方法用于得到当前进程的信号屏蔽字。
如: sigset_t oset;
sigprocmask(0,NULL,&oset);//信号屏蔽字保存在oset中。如oset为NULL,则表示忽略原来信号屏蔽字。成功就返回0,否则返回-1;
@注意SIGKILL和SIGSTOP这两个信号不能被屏蔽。
3、处理未决信号。
@当屏蔽一个信号,但是进程在某处还是接收到此信号。这种信号叫做未决信号。
int sigpending (sigset_t *set);
参数表示为当前进程所有未决的信号 。
4、高级信号注册函数。
int sigaction(int signo,const struct sigaction *restrict act,struct sigaction *restict oact);
第一人参数表示注册 的信号编号。
后面的参数表示act指向的结构设置信号处理函数,将原来的值保存在oact中。
struct sigation{
void (*sa_handler) (int);//信号处理函数
sigset_t sa_mask;屏蔽信号集
int sa_flags;信号选项
void (*sa_sigaction) (int siginfo_t *,void *);替代sa_hanler的信号处理函数。
}