Linux信号处理

Linux信号处理
一、基本概念
1、中断
当程序接收到消息后中止当前正在执行的程序,转而执行
其他任务,等其他任务执完成后再返回,这种执行模式叫做
中断
分为硬件中断和软件中断
2、信号
是一种软件中断,由操作系统发出,程序接收后会执行相应的
操作
3、常见的信号
命令 kill -l 查看所有信号
SIGINI(2) Ctrl+c 终止
SIGQUIT(3) Ctrl+\ 终止+core
SIGFPE(8) 除零 终止+core
SIGKILL(9) 终止信号 终止 不能捕获、忽略
SIGSEGV(11) 非法访问内存 终止+core
4、不可靠信号和可靠信号
建立在早期的信号处理机制上(1~31)的信号是不可靠信号
不支持排队、可能会丢失,如果同一个信号连续产生多次,
进程可能只响应一次
建立在新的信号处理机制上(34~64)的信号是可靠信号
支持排队、信号不会丢失
5、信号的来源
硬件异常:除零、非法访问内存、未定义的指令、总线错误
软件异常:通过一些命令、函数产生的信号
6、信号的处理方式
1、忽略
2、终止进程
3、终止进程并产生core文件(记录内存映像)
4、停止
5、捕获
6、捕获并处理 (在信号发生前,先向内核注册一个函数,当信号
来临时系统会自动执行该函数)

二、信号捕获
#include
typedef void (*sighandler_t)(int);
功能:说明信号处理函数的格式

   sighandler_t signal(int signum, sighandler_t handler);
    功能:向内核注册一个信号处理函数
    signum:信号编号
    handler:函数指针
        也可使用以下参数
        SIG_IGN 忽略
        SIG_DFL 按默认方式处理
    返回值:
        之前的信号处理方式
    注意:
        1、SIGKILL(9)、SIGSTOP(19)信号不能被捕获和忽略处理
        2、当信号处理完后可能会返回产生信号的代码继续运行,如果
        我们捕获并处理段错误、算术异常等信号可能会产生死循环,
        正确的处理段错误、算术异常信号应该是备份数据并直接结束
        程序
        3、有些系统通过signal注册的信号处理函数只能执行一次,如果
        想要持续有效,可以在信号处理函数中再重新注册一次
        4、子进程会继承父进程的信号处理方式,但是通过exec系列函数
        创建的子进程,会恢复默认的信号处理方式

信号的发送方式:
1、键盘
    Ctrl+c 
    Ctrl+\
    Ctrl+z  暂停、挂起 fg 继续
2、错误
    除零
    非法访问内存
    总线错误
3、命令
    kill -信号编号 进程号
    功能:向指定的进程发送信号
    killall -信号编号 进程名
        可以给同名的进程发送同一个信号
4、函数
    #include 
   #include 

   int kill(pid_t pid, int sig);
   功能:向指定进程发送指定信号
    pid:进程号
    sig:信号编号  

int raise(int sig);
功能:向进程自己发送信号sig

 void abort(void);
 功能:向进程自己发送信号SIGABRT

#include 

unsigned int alarm(unsigned int seconds);
功能:让内核在seconds秒后向进程发送SIGALRM信号
返回值:上次alarm设置的剩余秒数

三、进程休眠信号
#include

   int pause(void);
   功能:让调用者进入休眠状态,直到进程遇到信号
   才会唤醒
   返回值:要么不返回在休眠,要么唤醒后返回-1
   相当于没有时间限制的sleep

    #include 

   unsigned int sleep(unsigned int seconds);
    功能:让调用者进入休眠指定的秒数,当遇到信号时
    会提前唤醒返回
    返回值:剩余的休眠时间

四、信号集和信号堵塞
信号集:是一种数据类型,定义的变量可以存储多个信号
sigset_t 128位的二进制数,每一位都固定代表了一
种信号
相关函数:
#include

   int sigemptyset(sigset_t *set);
    功能:清空信号集
   int sigfillset(sigset_t *set);
    功能:填满信号集
   int sigaddset(sigset_t *set, int signum);
    功能:向信号集set中添加信号signum
   int sigdelset(sigset_t *set, int signum);
    功能:从信号集set中删除信号signum
   int sigismember(const sigset_t *set, int signum);
    功能:测试信号集中是否存在signum信号
    返回值:
        0   不存在
        1   存在
        -1  signum信号非法

信号阻塞:
    当程序执行到一些特殊操作时,不适合处理信号,此时
    可以让内核先屏蔽信号,等操作执行完成后再解除屏蔽
    重新发送信号
    当信号产生时,内核会在其维护的信号表中为对应的进程
    设置与该信号对应的标记,这个过程叫做递送
    从信号产生到完成递送有个时间间隔,处于这个间隔的信号
    状态称为未决
    信号屏蔽\阻塞就是让被屏蔽的信号先处于未决状态、暂停
    递送,当屏蔽解除时在继续递送

    #include 
     int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
     功能:设置要屏蔽的信号、这些信号是存储在信号集里面
     how:信号屏蔽的方式
        SIG_BLOCK   把set中的信号添加到要屏蔽集里
        SIG_UNBLOCK 从信号屏蔽集中删除set中的信号 解除
        SIG_SETMASK 把set替换之前的信号屏蔽集
    set:准备好的信号集
    oldset:获取旧的信号屏蔽集

五、附带信息的信号处理
#include

int sigaction(int signum, const struct sigaction *act,
                struct sigaction *oldact);
功能:向内核注册一个信号处理函数 
signum:要捕获的信号编号
act:设置要处理的动作
oldact:获取原来的处理动作

struct sigaction {
           void     (*sa_handler)(int);// 不附带其他信息的信号处理函数
           void     (*sa_sigaction)(int, siginfo_t *, void *);
           // 附带其他信息的信号处理函数
           sigset_t   sa_mask;//信号屏蔽集
           int        sa_flags;//信号处理动作标志
                SA_NODEFER  在信号处理过程中不要屏蔽当前信号
                SA_SIGINFO  使用sa_sigaction函数指针
           void     (*sa_restorer)(void);//保留函数
       };

siginfo_t {
int si_signo; /* Signal number /
int si_errno; /
An errno value /
int si_code; /
Signal code /
int si_trapno; /
Trap number that caused
hardware-generated signal
(unused on most architectures) /
pid_t si_pid; /
Sending process ID /
uid_t si_uid; /
Real user ID of sending process /
int si_status; /
Exit value or signal /
clock_t si_utime; /
User time consumed /
clock_t si_stime; /
System time consumed /
sigval_t si_value; /
Signal value /
int si_int; /
POSIX.1b signal */
void si_ptr; / POSIX.1b signal /
int si_overrun; /
Timer overrun count;
POSIX.1b timers /
int si_timerid; /
Timer ID; POSIX.1b timers */
void si_addr; / Memory location which caused fault /
long si_band; /
Band event (was int in
glibc 2.3.2 and earlier) /
int si_fd; /
File descriptor /
short si_addr_lsb; /
Least significant bit of address
(since Linux 2.6.32) */
void si_call_addr; / Address of system call instruction
(since Linux 3.5) /
int si_syscall; /
Number of attempted system call
(since Linux 3.5) /
unsigned int si_arch; /
Architecture of attempted system call
(since Linux 3.5) */
}

     int sigqueue(pid_t pid, int sig, const union sigval value);
     功能:向指定的进程发送信号并附加信息一起发送
     value:附加的信息
      union sigval {
           int   sival_int;//整数
           void *sival_ptr;//指针
       };

六、定时器
#include

   int getitimer(int which, struct itimerval *curr_value);
   功能:获取当时的定时方案
   which:选择使用的计时器
         ITIMER_REAL    真实计算器  程序总的计时时间
         ITIMER_VIRTUAL 虚拟计算器  用户态的计时
         ITIMER_PROF    实际计时器  用户态+内核态计时
         真实计时器=实际计时器+休眠时间+切换时间
   int setitimer(int which, const struct itimerval *new_value,
                 struct itimerval *old_value);
    功能:设置定时器的计时方案
    which:选择使用的计时器
         ITIMER_REAL    真实计算器  程序总的计时时间
         ITIMER_VIRTUAL 虚拟计算器  用户态的计时
         ITIMER_PROF    实际计时器  用户态+内核态计时
         真实计时器=实际计时器+休眠时间+切换时间

struct itimerval {
struct timeval it_interval; //每次时钟信号产生的间隔时间
struct timeval it_value; //第一次产生时钟信号的时间
};

       struct timeval {
           time_t      tv_sec;         //设置秒
           suseconds_t tv_usec;        //设置微秒
       };

你可能感兴趣的:(linux,运维,服务器)