• Linux多进程开发III


    1.信号,事件发生时对进程的通知机制,有时称为软件中断,是在软件层次上对中断机制的一种模拟,是一种异步通信方式。发送进程的信号一般来源于内核。
       引发内核为进程产生信号的事件:
    • 对于前台进程,用户可以通过键盘输入特殊的中断字符来给它发送信号。
    • 硬件发生异常,即硬件检测到一个错误条件并通知内核,再由内核发送相应信号给相关进程。
    • 系统状态变化,如alarm定时器到期,进程执行的CPU时间超限等。
    • 运行kill命令或调用kill函数。
    2.使用信号的目的:
    • 让进程知道发生了特定的事情。
    • 强迫进程执行它自己代码中的信号处理程序。
    3.信号的特点:
    • 简单
    • 不能携带大量信息
    • 满足特定条件才发送
    • 优先级比较高
    4.查看系统定义的信号列表。前31为常规信号,其余为实时信号。
    kill -l
     
    5.常用信号。
    SIGIN          <Ctrl + C>组合键,用户终端向正在运行的由此终端启动的程序发送信号          终止进程
    SIGQUIT        <Ctrl + >组合键,用户终端向正在运行的由此终端启动的程序发送信号          终止进程
    SIGKILL        无条件终止进程,不能被忽略,处理和阻塞                                  终止进程,可以杀死任何进程
    SIGSEGV        知识进程进行了无效内存访问(段错误)                                   终止进程并产生core文件
    SIGPIPE        破裂管道,向一个没有读端的管道写数据                                   终止进程
    SIGCHILD       子进程结束时,父进程会收到该信号                                       忽略该信号
    SIGCONT        如果进程已停止,则使其继续运行                                         继续/忽略
    SIGSTOP        停止进程的执行。信号不能被忽略,处理和阻塞                               终止进程
    

      

    6.信号的5中默认处理动作。
    • Term        终止进程
    • Ign           当前进程忽略该信号
    • Core         终止进程并生成一个core文件
    • stop         暂停当前进程
    • Cont        继续执行当前被暂停的进程
     
    7.信号的几种状态。
    • 产生
    • 未决
    • 递达
     
    8.SIGKILL和SIGSTOP信号不能被捕捉、阻塞和忽略。只能执行默认的动作。
     
    9.生成core文件并查看。
    ulimit -a                     // 查看core文件大小
    ulimit -c 1024                // 修改core文件大小
    (gdb) core-file core          // 在gdb调试下查看core文件中的错误
    

      

    10.信号相关的函数。
    #include <sys/types.h>
    #include <signal.h>
    int kill(pid_t pid, int sig);
        - 作用:给某个进程pid发送某个信号
        - pid: 需要发送信号的进程,>0 发送给指定进程;=0 发送给当前进程组;=-1 发送给每一个有权限接收该信号的进程;<-1 pid=某个进程组的ID取反
        - sig: 需要发送信号的编号或宏值,0表示不发送任何信号
     
    int raise(int sig);
        - 作用:给当前的进程发送信号
        - sig: 要发送的信号
        - 返回值:成功返回0,失败返回非0
        - kill(getpid(), sig);
     
    void abort(void);
        - 作用:发送SIGABRT信号给当前进程,杀死当前进程
        - kill(getpid(), SIGABRT);
     
    #include <unistd.h>
    unsigned int alarm(unsigned int seconds);
        - 作用:设置定时器,函数调用,开始倒计时,当倒计时为0时,函数会给当前的进程发送一个信号:SIGALARM
        - seconds: 倒计时的时长,单位:秒。如果参数为0,定时器无效(不进行倒计时,不发送信号)。取消一个定时器,通过alarm(0)。
        - 返回值:之前没有定时器返回0,之前有定时器返回之前的定时器剩余的时间
      -SIGALARM:默认终止当前进程,每一个进程都有且只有唯一一个定时器。alarm(100) -> 该函数是非阻塞的
    定时器,与进程的状态无关(自然定时法),无论进程处于什么状态,alarm都会计时。
     
    int setitimer(int which, const struct itimerval *new_val, struct itimerval *old_val);
        - 作用:设置定时器。可以代替alarm函数,精读微秒,可以实现周期性定时
        - which: 定时器漆以什么时间计时,ITIMER_REAL 真实时间,时间到达,发送SIGALRM;ITIMER_VIRTUAL 用户时间,时间到达,发送SIGVTALRM;ITMIER_PROF 以该进程在用户态和内核态所消耗的时间计算,               时间到达,发送SIGPROF;
        - new_val: 设置定时器的属性
        - old_val: 上一次定时的时间参数,一般不使用指定为NULL
        - 返回值:成功返回0,失败返回-1并设置errno
    struct itimerval {                   // 定时器
        struct timeval it_interval;      // 周期时间
        struct timeval it_value;         // 第一次延迟多长时间执行定时器 
    };
    struct timeval {                    // 时间
        time_t      tv_sec;             // 秒数
        suseconds_t tv_usec;            // 微秒
    };
    

      

    11.运行程序的时间
        实际的时间 = 内核时间 + 用户时间 + 消耗的时间
        进行文件IO操作的时候比较浪费时间
     
    12.信号捕捉。信号捕捉要注册在信号之前。
    #include <signal.h>
    typedef void (*sighandler_t)(int);
    sighandler_t signal(int signum, sighandler_t handler);
        - 作用:设置某个信号的捕捉行为
        - signum: 要捕捉的信号
        - handler: 捕捉到的信号要如何处理,SIG_IGN:忽略信号;SIG_DFL:使用信号默认的行为;回调函数:这个函数是系统调用,程序员只负责写,捕捉到信号后如何处理信号
        - 返回值:成功返回上一次注册的信号处理函数的地址,第一次调用返回NULL;失败返回SIG_ERR,并设置错误号
        - 回调函数:程序员提前实现,函数类型根据需求,看函数指针定义;当信号产生时,由内核调用;函数指针是实现回调的手段,函数实现后,将函数名放到函数指针的位置
     
    int sigaction(int signum, const struct sigaction *act, struct sigaction *odlact);
        - 作用:检查或者改变信号的处理。信号捕捉
        - signum: 需要捕捉的信号的编号或者宏值
        - act: 捕捉到信号之后的处理动作
        - oldact: 上一次对信号捕捉相关的设置,一般设置为NULL
        - 返回值:成功返回0,石板返回-1
    struct sigaction {
        void     (*sa_handler)(int);                                        // 函数指针,指向的函数是信号捕捉到之后的处理函数
        void     (*sa_sigaction)(int, siginfo_t *, void *);                 // 不常用
        sigset_t   sa_mask;                                                 // 临时阻塞信号集,信号捕捉函数执行过程中临时阻塞某些信号
        int        sa_flags;                                                // 使用哪一个信号处理对捕捉到的信号进行处理,0表示使用sa_handler,SA_SIGINFO表示使用sa_sigaction
        void     (*sa_restorer)(void);                                      // 被废弃了
    };
     
    13.信号集。多个信号课使用一个称之为信号集的数据结构表示,系统数据类型为 sigset_t。PCB中有两个信号集。
    • 阻塞信号集, 信号的阻塞是让系统暂时保留信号留待以后发送。由于另外有办法让系统忽略信号,所以一般情况下信号的阻塞只是暂时的,只是为了防止信号打断敏感操作。 
    • 未决信号集,信号的“未决”是一种状态,指的是从信号的产生到信号被处理前的一段时间。
    • 这两个信号集都是内二使用位图机制实现的,无法直接进行操作。需要借助信号集操作函数来对这两个信号集修改。
       
    14.信号处理过程。
    1. 用户通过键盘 Ctrl + C,产生2号信号SIGINT(信号被创建)
    2. 信号产生但是没有被处理(未决)
      1. 在内核中将所有没有被处理的信号存储在一个集合中(未决信号集)
      2. SIGINT信号状态被存储在第二盒标志位上
        1. 这个标志位的值为0,表示信号不是未决
        2. 这个标志位的值为1,表示信号处于未决
    3. 这个未决状态的信号,需要被处理,处理之前需要和另一个信号集(阻塞信号集),进行比较:
      1. 阻塞信号集默认不阻塞任何信号集
      2. 如果想要阻塞某些信号需要用户调用系统API
    4. 在处理的时候和阻塞信号集中的标志位进行查询,看是不是对该信号设置了阻塞:
      1. 如果没有阻塞,这个信号被处理
      2. 如果阻塞了,这个信号就继续处于未决状态,直到阻塞解除,这个信号就被处理
    // 以下信号集相关的函数都是对自定义的信号集进行操作。
     
    #include <signal.h>
    int sigemptyset(sigset_t *set);
        - 作用:清空信号集中的数据,将信号集中的所有标志位置为0
        - set: 传出参数,需要操作的信号集
        - 返回值:成功返回0,失败返回-1并设置errno
     
    int sigfillset(sigset_t *set);
        - 作用:将信号集中的所有标志位置为1
        - set: 传出参数,需要操作的信号集
        - 返回值:成功返回0,失败返回-1并设置errno
     
    int sigaddset(sigset_t *set, int signum);
        - 作用:设置信号集中的某一个信号对应的标注为1,表示阻塞该信号
        - set: 传出参数,需要操作的信号集
        - signum: 需要设置阻塞的信号
        - 返回值:成功返回0,失败返回-1并设置errno
     
    int sigdelset(sigset_t *set, int signum);
        - 作用:设置信号集中的某一个信号对应的标注为0,表示不阻塞该信号
        - set: 传出参数,需要操作的信号集
        - signum: 需要设置不阻塞的信号
        - 返回值:成功返回0,失败返回-1并设置errno
     
    int sigismember(const sigset_t *set, int signum);
        - 作用:判断某个信号是否属于信号集set
        - set: 需要操作的信号集
        - signum: 需要判断的信号
        - 返回值:属于返回1,不属于返回0,调用失败返回-1并设置errno
    

      

     
    15.处理系统信号集。(只能改变内核阻塞信号集,不能改变未决信号集)
    int sigprocmask(int how, cosnt sigset_t *set, sigset_t *oldset);
        - 作用:将自定义信号集中的数据设置到内核中(设置阻塞,解除阻塞,替换)
        - how: 如何对内核阻塞信号集进行处理,
            - SIG_BLOCK 将用户设置的阻塞信号集添加到内核中,内核原来的数据不变,即:mask | set;(假设内核中默认阻塞信号集为mask)
            - SIG_UNBLOCK 根据用户设置的数据,对内核中的数据进行解除阻塞,即:mask &= ~set
            - SIG_SETMASK 覆盖内核中原来的值
        - set: 已经初始化好的用户自定义的信号集
        - old_set: 保存设置之前的内核中的阻塞信号集的状态,可以使NULL
        - 返回值:成功返回0,失败返回-1并设置错误号:EFAULT、EINVAL
     
    int sigpending(sigset_t *set);
        - 作用:获取内核中的未决信号集
        - set: 传出参数,保存的是内核中的未决信号集中的信息 
     
    16.以下三种条件都会给父进程发送SIGCHILD信号,父进程默认忽略该信号。SIGCHILD信号产生的条件:
    • 子进程终止
    • 子进程接收到SIGSTOP信号停止时
    • 子进程处在停止态,接收到SIGCONT后唤醒
     
    17.在父进程注册完信号捕捉之前,子进程就结束了,会出现段错误。所以需要提前设置好阻塞信号集,阻塞SIGCHILD。
    sigset_t set;
    sigemptyset(&set);
    sigeaddset(&set, SIGCHLD);
    sigprocmask(SIG_BLOCK, &set, NULL);        // 阻塞
    sigprocmask(SIG_UNBLOCK, &set, NULL);      // 解除阻塞
    

      

     
     
     
     
     
  • 相关阅读:
    利用 PHP 导出 Git 某个分支下,新增或修改过的文件
    [翻译] 10 个实用的 Git 高级命令
    Django web project
    install virtualenv
    js原型继承
    HTML 学习杂记
    IDEA 文件列表隐藏某后缀文件
    coocsCreator杂记
    mac install brew
    c编程:输入一个数字n,则n代表n行,每行输入2个数字a,b计算每行的a+b问题。
  • 原文地址:https://www.cnblogs.com/tristatl/p/15120522.html
Copyright © 2020-2023  润新知