zoukankan      html  css  js  c++  java
  • Linux -- 信号编程

    进程捕捉到信号对其进行处理时,进程正在执行的正常序列就被信号处理程序临时中断,它首先执行该信号处理程序中的指令。如果从信号处理程序返回(例如没有调用exit或longjmp),则继续执行在捕捉到信号时进程正在执行的正常指令序列。

    1. 在信号处理程序中,我们要保证调用”异步信号安全”的函数,即可重入的函数

    不可重入的函数大多(a)已知它们使用静态数据结构。(b)它们调用malloc或free(c)它们是标准I/O函数

    2. 由于每个线程只有一个errno变量,所以信号处理程序可能会修改其原先值。因此,所有信号处理程序应当在函数的起始保存errno,结尾恢复errno

    3. 每个进程都有一个信号屏蔽字(signal mask),它规定了当前要阻塞传递送到该进程的信号集

    信号集signal set

    int sigemptyset(sigset_t *set); //初始化由set指向的信号集,清除其中所有信号
    
    int sigfillset(sigset_t *set);       //初始化由set指向的信号集,使其包括所有信号

    所有应用程序在使用信号集前,要对该信号集调用sigemptyset或sigfillset一次。

    信号集初始化之后,可在该信号中增删特定的信号。

    int sigaddset(sigset_t *set,int signo)
    
    int sigdelset(sigset_t *set,int signo)

    进程的信号屏蔽字

    int sigprocmask(int how,const sigset_t *restrict set, const sigset_t *restrict oset)

    Oset若是非空指针,那么进程的当前信号屏蔽字通过oset返回

    How的三种取值决定了如何修改当前信号屏蔽字:

    SIG_BLOCK : 向当前信号屏蔽字中添加参数set包含的信号

    SIG_UNBLOCK : 把当前信号屏蔽字中参数set包含的信号删去

    SIG_SETMASK : 把参数set设为进程的信号屏蔽字。

    请注意,sigprocmask仅为单线程进程定义的。处理多线程进程中信号的屏蔽使用另一个函数

    执行信号的处理程序称为信号递达,信号从产生到递达之间的状态称为信号未决。被阻塞的信号将保持在未决状态,直到进程解决对此信号的阻塞。

    int sigpending(sigset_t *set)

    Set返回当前的未决信号

    信号处理的范式

    static int pipefd[2];
    
    int signal_module_init()
    
    {
             struct sigaction act;
    
             //信号处理程序指定为sig_handler
             act.sa_handler = sig_handler;
    
             //在进入信号处理程序前,把act.sa_mask信号集加到进程的信号屏蔽字中。调用sigfillset把所有信号加入这个信号集。这表示当进入信号处理程序后,阻塞一切信号
             sigfillset(&act.sa_mask)
    
             if(0 > sigaction(SIGINT,&act,0) ||
                0 > sigaction(SIGCHLD,&act,0) ||
                ...... ) {
                       write_log("failed to init signal:sigaction()");
                       return -1;
             }
             return signal_pipe_init();
    }
    
     
    
    static int signal_pipe_init()
    {
             if( 0 < pipe(pipefd,O_CLOEXEC|O_NONBLOCK) ){
                       write_log("failed to init pipe");
                       return -1;
             }
             return 0;
    }
    
     
    
    static void sig_handler(int signo)
    {
             //定义一个数组,将你注册的每个信号的signo映射成一个唯一的字符
             static const char sig_chars[NSIG+1] = {
                       [SIGINT] = 'I',
                       [SIGCHLD] = 'C',
                       .....
             };
    
             char s;
             int saved_errno;
    
             //保存当前的errno。每个线程仅有一个errno变量,不应让信号处理程序中的errno影响正常流程中的errno。因此我们需要在信号处理程序的起始存储errno,在末尾恢复errno
             saved_errno = errno;
             s = sig_chars[signo];
             write(pipefd[1],&s,sizeof(s));
             errno = saved_errno;
    
    }
    
    //然后在Reactor中监听pipefd[0]. 其回调函数如下:
    void got_signal(ev)
    {
             int res,ret;
             char c;
             int fd = ev->fd;
             for(;;){
                       //fd是非阻塞的
                       do {
                                res = read(fd,&c,1);
                       } while(res == -1 && errno == EINTR);
    
                       //pipe中没有可读数据
                       if(res <= 0){
                                break;
                       }
    
                       switch(c){
                                case 'I':
                                         dosomething1();
                                         break;
                               case 'C':
                                        dosomething2();
                                         break;
                                ......
                       }
             }
             return;
    }
  • 相关阅读:
    删除表数据drop、truncate和delete的用法
    List,DataTable实现行转列的通用方案
    C#正则表达式简单限制输入11位手机号
    Oracle数据库byte存放汉字,9个汉字x3=27个字节
    SQL 语句
    $("p").fadeOut("fast")设置淡出效果
    神奇的 toLocaleString
    C#中精确计时的一点收获
    Web service stop after running serveral hours
    SQL Server中like匹配下划线的方法
  • 原文地址:https://www.cnblogs.com/tuowang/p/9398987.html
Copyright © 2011-2022 走看看