zoukankan      html  css  js  c++  java
  • 第8章 信号(2)_信号的发送

    2. 信号发送的函数

    2.1 安装信号处理函数:signal

    (1)signal函数

    头文件

    #include <signal.h>

    函数

    void (*signal(int signo, void(*func)(int)))(int);

    返回值

    若成功则返回先前的信号处理函数指针,出错则返回SIG_ERR

    参数

    signo:要登记的信号值(如SIGCHLD)

    func:

      ①信号处理函数指针;

      ②如果设为SIG_IGN,表示忽略信号

      ③如果设置为SIG_DFL:采用系统默认的方式处理信号,执行默认操作。

    功能

    向内核登记信号处理函数

    备注

    signal是个系统函数,它有两个参数signo和func。返回值为先前的信号处理函数指针。

    ②typedef的方式

      typedef void(*sighandler_t)(int);

      sighandler_t signal(int signum, sighandler_t handler);

    【编程实验】捕获信号(如SIGUSR1、SIGSTOP、ctrl-z、ctrl-c等)

    //signal_catch.c

    #include <signal.h>
    #include <stdlib.h>
    #include <stdio.h>
    
    /*演示信号的捕获*/
    
    //定义信号处理函数
    //signo:进程捕获到的信号
    void sig_handler(int signo)
    {
        //输出进程ID和信号值
        printf("%d, %d ocurred
    ", getpid(), signo);
    }
    
    int main(void)
    {
        //向内核登记信号处理函数(用于捕获ctrl-z和ctrl-c信号)
        //1.忽略ctrl-Z信号
        // if(signal(SIGTSTP, SIG_IGN) == SIG_ERR){
        //2.ctrl-z的默认处理
        // if(signal(SIGTSTP, SIG_DFL) == SIG_ERR){
        //3.捕获ctrl-z信号
        if(signal(SIGTSTP, sig_handler) == SIG_ERR){
            perror("signal sigtstp error");
        }
        
        //捕获ctrl-c信号
        if(signal(SIGINT, sig_handler) == SIG_ERR){
            perror("signal sigint error");
        }
    
        //捕获SIGUSR1信号(可在另一终端中发送kill -SIGUSR1 进程号
        //来观察,默认是被忽略的)
        if(signal(SIGUSR1, sig_handler) == SIG_ERR){
            perror("signal sigusr1 error");
        }
    
        //捕获SIGUSR2信号(默认被忽略的)
        if(signal(SIGUSR2, sig_handler) == SIG_ERR){
            perror("signal sigusr2 error");
        }
        
        //捕获/忽略SIGKILL或SIGSTOP信号都会提示无效参数,
        //以下代码不能起作用!
        //1. 捕获SIGKILL,会提示错误
        //if(signal(SIGKILL, sig_handler) == SIG_ERR){
        //2. 忽略SIGKILL,会提示错误,也无法起作用
        if(signal(SIGKILL, SIG_IGN) == SIG_ERR){
            perror("signal sigkill error");
        }
    
        int i = 0;
        while(i<30){
            //不停输出信息,在这过程中,可在另一个终端给该进程发送
            //各种信号(如SIGUSR1,SIGKILL等),也可按ctrl-c或ctrl-z
            //观察信号被捕获的情况。
            printf("%d out %d 
    ", getpid(), i++);
            sleep(1);
        }
    
        return 0;
    }
    /*输出结果:
     1852 out 0 
     1852 out 1 
     1852 out 2 
     ^C1852, 2 ocurred
     1852 out 3 
     1852 out 4 
     1852 out 5 
     1852 out 6 
     1852 out 7 
     ^Z1852, 20 ocurred
     1852 out 8 
     1852 out 9 
     ...
     */

    (2)SIGCHLD信号

      子进程状态发生变化(子进程结束)产生该信号,父进程需要使用wait函数来等待子进程结束并回收它。

      ②避免僵尸进程

    【编程实验】避免僵尸进程

    //sig_child.c

    #include <signal.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <sys/wait.h>
    
    void sig_handler(int signo)
    {
        //子进程结束时,输出提示信息
        printf("child process deaded, signo: %d
    ", signo);
        //当父进程捕获到SIGCHLD信号后要调用wait来回收子进程
        //否则,子进程会成为僵尸进程。
        if(SIGCHLD == signo)
            wait(0);
    }
    
    void out(int n)
    {
        int i = 0;
        for(i=0; i<n; i++){
            printf("%d out %d 
    ",getpid(), i);
            sleep(1);
        }
    }
    
    int main(void)
    {
        //在父进程中捕获子进程结束的信号
        if(signal(SIGCHLD, sig_handler) == SIG_ERR){
            perror("signal sigchld error");
        }
    
        pid_t pid = fork();
        if(pid < 0){
            perror("fork error");
            exit(1);
        }else if(pid > 0){ //parent process
            out(20);
        }else{  //child process
            out(10); //让子进程先结束
        }
        return 0;
    }

    2.1 信号的发送

    (1)信号的发送

      ①除了内核和超级用户,并不是每个进程都可以向其他进程发送信号。

      ②一般的进程只能向具有相同uid和gid的进程发送信号,或向相同进程组中的其他进程发送信号。

      ③常用发送信号的函数有kill、raise、alarm、setitimer和abort

    (2)kill和raise函数

    头文件

    #include <signal.h>

    函数

    int kill(pid_t pid, int signo);//向指向进程发送一个信号

    int raise(int signo);  //向进程本身发送一个信号

    返回值

    若成功返回0,出错返回-1

    参数

    pid:接受信号进程的pid

      ①pid>0:将信号发给进程ID为pid的进程

      ②pid==0:将信号发给与发送进程同一进程组的所有进程

      ③pid<0:将该信号发送给进程ID等于pid的绝对值

      ④pid==-1:将该信号发送给发送进程有权限向他们发送信号的系统上所有进程。

    signo:要发送的信量值

    功能

    向指定进程或本身发送一个信号。

    备注

    ①kill函数将信号发送给进程或进程组。0为空信号,常用来检测特定的进程是否存在

    ②raise相当于kill(getpid(), sig);

    【编程实验】信号的发送

    //signal_send.c

    #include <signal.h>
    #include <stdlib.h>
    #include <stdio.h>
    
    /*演示信号的发送与捕获*/
    
    //定义信号处理函数
    //signo:进程捕获到的信号
    void sig_handler(int signo)
    {
        //输出进程ID和信号值
        printf("%d, %d ocurred
    ", getpid(), signo);
    }
    
    int main(void)
    {
        //向内核登记信号处理函数
        //捕获SIGUSR1信号
        if(signal(SIGUSR1, sig_handler) == SIG_ERR){
            perror("signal sigusr1 error");
        }
    
        //捕获SIGUSR2信号(默认被忽略的)
        if(signal(SIGUSR2, sig_handler) == SIG_ERR){
            perror("signal sigusr2 error");
        }
        
        int i = 0;
        while(i<10){
            //观察信号被捕获的情况。
            printf("%d out %d 
    ", getpid(), i++);
    
            /*进程自杀
            if(i == 5){
                kill(getpid(), SIGKILL);
            }
            */
            sleep(1);
        }
    
        //向进程自己发送SIGUSR1和SIGUSR2信号
        raise(SIGUSR1);
        kill(getpid(), SIGUSR2);
    
        return 0;
    }
    /*输出结果:
    2049 out 0 
    2049 out 1 
    2049 out 2 
    2049 out 3 
    2049 out 4 
    2049 out 5 
    2049 out 6 
    2049 out 7 
    2049 out 8 
    2049 out 9 
    2049, 10 ocurred
    2049, 12 ocurred
    */

    (3)alarm函数

    头文件

    #include <unistd.h>

    函数

    unsigned int alarm(unsigned int seconds);

    返回值

    返回0或以前设置的定时器时间余留秒数

    参数

    seconds为0表示取消以前设置的定时器

    功能

    定时器

    备注

    ①alarm函数可以设置定时器,当定时器超时,产生SIGALRM信号。

    ②信号是由内核产生的,在指定的seconds秒之后,给进程本身发送一个SIGALRM信号。

    ③alarm是一次性的定时器,每次触发后,要重新设置才能周期性的触发。

    【编程实验】alarm定时器

    //signal_alarm.c

    #include <unistd.h>
    #include <signal.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <math.h>
    
    void sig_handler(int signo)
    {
        if(signo == SIGALRM){
            printf("alarm clock timeout
    ");
            //由于alarm是一次性的,要重新设置定时器以达到周期性的目的。
            alarm(5);
        }
    }
    
    void out_data(void)
    {
        int i = 1;
        while(i <= 20){
            double d = drand48();
            printf("%-10d:%lf
    ", i++, d);
            if(i == 15)
                alarm(0);  //取消定时器
            sleep(1);
        }
    }
    
    int main(void)
    {
        //注册信号处理函数
        if(signal(SIGALRM, sig_handler) == SIG_ERR){
            perror("signal sigalrm error");   
        }
    
        //设置定时器
        alarm(5);
        printf("begin running main
    ");
        out_data();
        printf("end running main
    ");
     
        return 0;
    }

    (4)gettitimer/setitimer函数

    头文件

    #include <unistd.h>

    函数

    int getitimer(int which, struct itimerval *value); //获取定时器状态

    int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue); //设置定时器

    返回值

    成功调用返回0,错误返回-1

    参数

    ①which:

      ITIMER_REAL:按实际时间计时,计时到达将给进程发送SIGALRM信号。

      ITIMER_VIRTUAL:仅当进程执行时才进行计时。计时到达将发送SIGVALRM。

      ITMIER_PROF:当进程执行时和系统为该进程执行动作时都计时。与定时器经常用来统计进程在用户态和内核态花费的时间。计时到达将发送SIGPROF信号给进程。

    ②value:用来指时定时器的时间,其结构如下:

    struct itimerval{
        struct timeval it_interval; //下一次的取值,相当于以后每次触发的时间间隔
        struct timeval it_value;    //本次的设定值,相当于调用setitimer以后,将于多久后第1次触发。
    }

    ③timeval结构体

    struct timeval{
        long tv_sev; //
        long tv_usec; //微秒,1秒=1000000微秒
    }

    功能

    获取/设置定时器状态。

    备注

    ①定时器将it_value递减到0时,产生一个信号,并将it_value的值设定为it_interval的值,然后重新开始计时。

    ②it_value设定为0时,计时器停止,或者当它计时到期,而it_interval为0时停止。

    ③ovalue不为空,则其中保留的是上次调用设定的值。

    【编程实验】周期性定时器

    //signal_setitimer.c

    #include <signal.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <sys/time.h>
    
    /*周期性定时器*/
    
    void sig_handler(int signo)
    {
        switch(signo){
        case SIGALRM:
            printf("Catch a signal--SIGALRM(%d)
    ", signo);
            break;
        case SIGVTALRM:
            printf("Catch a signal--SIGVTALRM(%d)
    ", signo);
            break;
        }
    }
    
    int main(void)
    {
        struct itimerval value1, value2, ovalue;
    
        signal(SIGALRM, sig_handler);
        signal(SIGVTALRM, sig_handler);
    
        printf("process id is %d
    ", getpid());
        
        //按实际时间计时,超时发送SIGALRM信号
        value1.it_value.tv_sec = 5;   //it_value:调用settimer后,5秒以后第1次触发
        value1.it_value.tv_usec = 0;
        value1.it_interval.tv_sec = 1; //以后每次触发的时间间隔
        value1.it_interval.tv_usec = 0;
    
        setitimer(ITIMER_REAL, &value1, &ovalue);
        
        //仅当进程执行时才进行计时,超时发送SIGVTALRM信号
        value2.it_value.tv_sec = 0;
        value2.it_value.tv_usec = 500000; //0.5秒
        value2.it_interval.tv_sec = 0;
        value2.it_interval.tv_usec = 500000;
    
        setitimer(ITIMER_VIRTUAL, &value2, &ovalue);
    
        for(;;);
    
        return 0;
    }
    /*输出结果:
     process id is 2185
     Catch a signal--SIGVTALRM(26)
     Catch a signal--SIGVTALRM(26)
     Catch a signal--SIGVTALRM(26)
     Catch a signal--SIGVTALRM(26)
     Catch a signal--SIGVTALRM(26)
     Catch a signal--SIGVTALRM(26)
     Catch a signal--SIGVTALRM(26)
     Catch a signal--SIGVTALRM(26)
     Catch a signal--SIGVTALRM(26)
     Catch a signal--SIGALRM(14)
     Catch a signal--SIGVTALRM(26)
     Catch a signal--SIGVTALRM(26)
     Catch a signal--SIGALRM(14)
     Catch a signal--SIGVTALRM(26)
     ...
     */
  • 相关阅读:
    ceph集群jewel版本 rbd 块map 报错-故障排查
    基本的Ceph性能测试工具和方法
    dd命令的高级应用
    Ceph recover的速度控制
    Linux mount命令
    Centos7.2:搭建Ceph管理系统Inscope
    rpm --import /etc/pki/rpm-gpg/RPM* 有什么用?
    dd命令的解释
    Playbooks 中的错误处理
    Ansible之Playbooks的when语句
  • 原文地址:https://www.cnblogs.com/5iedu/p/6375426.html
Copyright © 2011-2022 走看看