Linux环境编程——信号处理

一、基本概念

1、中断
    当程序接收到消息后会中止当前正在执行的程序,转而执行其他任务,等其他任务执行完成后再返回,这种执行模式叫做中断
    分为硬件中断和软件中断

2、信号
    是一种软件中断,由操作系统发出,程序接收后会执行相应的操作

3、常见的信号
    命令kill+l 查看所有信号
    SIGINT(2)   Ctrl+c          终止
    SIGQUIT(3)  Ctrl+\          终止+core
    SIGFPE(8)   除0             终止+core
    SIGKILL(9)  终止信号         终止        不能捕获忽略
    SINSEGV(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:信号编号 
        返回值:成功0,失败-1

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

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

三、进程休眠信号

#include 
int pause(void);
功能:让调用者进入休眠状态,直到进程遇到信号才会被唤醒
返回值:要么不返回在休眠,要么唤醒后返回-1

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

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

四、信号集合信号阻塞

信号集:是一种数据类型,定义的变量可以存储多个信号
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:   设置要处理的动作
olact: 获取原来的处理动作

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) */
       }

#include 
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     真实计时器 程序总的计时时间
        SIGVTALRM(26) 
        
    ITIMER_VIRTUAL  虚拟计时器 用户态的计时
        SIGVTALRM(26)
    ITIMER_PROF     实际计时器 用户态+内核态计时
        SIGPROF(27)
    真实计时器=实际计时器+休眠时间+切换时间

int setitimer(int which, const struct itimerval *new_value,struct itimerval *old_value);
功能:设置定时器计时方案

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

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