linux 环境编程学习笔记 第18、19天 信号(2)


 

接着上上次

 

一、信号


3.信号的应用(实现多任务)

 

使用定时器实现多任务

例子:同时显示随机数与时间

 

#include <curses.h>
#include <unistd.h>
#include <time.h>
#include <stdlib.h>
#include <sys/time.h>
#include <signal.h>

WINDOW *wtime,*wnumb;

void showtime(int s)
{
    time_t t;
    struct tm *tt;
    time(&t);
    tt=localtime(&t);
    mvwprintw(wtime,1,1,"%02d:%02d:%02d",tt->tm_hour,tt->tm_min,tt->tm_sec);

    refresh();
    wrefresh(wtime);
    wrefresh(wnumb);


}

int main(int argc, const char *argv[])
{
    initscr();
    box(stdscr,0,0);

    curs_set(0);

    wtime=derwin(stdscr,3,10,0,COLS-10);
    wnumb=derwin(stdscr,3,9,(LINES-3)/2,(COLS-9)/2);

    box(wtime,0,0);
    box(wnumb,0,0);

    refresh();
    wrefresh(wtime);
    wrefresh(wnumb);

    signal(SIGALRM,showtime);

    struct itimerval val={0};
    val.it_interval.tv_sec=1;
    val.it_value.tv_sec=0;
    val.it_value.tv_usec=1;
    setitimer(ITIMER_REAL,&val,0);

    int n;
    int numb;
    while(1)
    {
        n = 7;
        numb=0;
        while(n--)
        {
            numb=numb*10+rand()%10;
        }
        mvwprintw(wnumb,1,1,"%07d",numb);
        usleep(10000);

        refresh();
        wrefresh(wtime);
        wrefresh(wnumb);

    }

    getch();

    endwin();
    return 0;
}

 

 

sleep与pause函数被信号影响后,sleep不在继续睡眠, pause不在暂停。

例子:显示随机数,并且用空格控制暂停

 

#include <curses.h>
#include <unistd.h>
#include <signal.h>

WINDOW *wtime;
int isstop=0;

void handle(int s)
{
    isstop^=1;
}

int main(int argc, const char *argv[])
{
    initscr();
    box(stdscr,0,0);

    curs_set(0);
    noecho();

    wtime=derwin(stdscr,3,9,(LINES-3)/2,(COLS-9)/2);

    box(wtime,0,0);

    refresh();
    wrefresh(wtime);
    if(fork())
    {
        signal(SIGUSR1,handle);
        while(1){
            if(isstop==1){
                pause(); //pause 会被信号终端,它是受信号控制
            }
            int numb=0;
            int n=7;
            while(n--)
            {
                numb=numb*10+rand()%10;
            }

            mvwprintw(wtime,1,1,"%07d",numb);

            refresh();
            wrefresh(wtime);

            usleep(100000);
        }
    }else{
        while(1)
        {
            char key;
            key=getch();
            if(key==' ')
                kill(getppid(),SIGUSR1);  //getppid() 获取父进程的 pid 
        }
    }

    endwin();
    return 0;
}

 

其他信号函数

  int raise(int sig);  等同于   kill(getpid(), sig);  向自己发送一个信号

 

4.信号的可靠与不可靠以及信号的含义

引出的例子:

 

#include <stdio.h>
#include <signal.h>

void handle(int s)
{
    printf("信号!\n");

}

int main(int argc, const char *argv[])
{
    signal(SIGINT,handle);
    printf("%d\n",getpid());
    while(1);
    return 0;
}

生成main1

#include <stdio.h>
#include <signal.h>

int main(int argc, const char *argv[])
{
    int n=5;
    while(n--)
    {
        kill(3973,SIGINT);
    }
    return 0;
}

 

生成main2

当main2运行时,main1不会显示5个信号。

 

 

信号有丢失(信号压缩)

由于历史的缘故:信息有压缩的需求

提出了可靠信号(实时信号)与不可靠信号(非实时信号)

 

早期 1~31 信号,这些信号都是不可靠(这些信号基本上与系统有关)

SIGWINCH (28) 窗口大小发送改变时发送的信号

后期提出的信号 34~64  可靠信号(用户信号)

 

5.信号的操作

 (1). 信号屏蔽

       int sigprocmask(int how,    // 操作方式   SIIG_BLOCK 设置屏蔽   SIG_UNBLOCK 解除屏蔽   SIG_SETMASK

                                 const sigset_t *set,  //信号集合

                                 sigset_t *oldset);// 返回原来操作的信号集合。

             步骤(1)声明信号集合

                           sigset_t sigs;

                    (2)加入屏蔽信号

                           一组信号集合维护函数

                                1. 信号集合   sigemptyset

                                2. 添加信号到集合  sigaddset

                                3. 从集合删除信号   sigdelset

                                4. 添加所有信号到集合 sigfillset

                                5. 判定信号是否在集合 sigismember                        

                    (3)屏蔽信号

                    (4)解除屏蔽

例子

 

#include <stdio.h>
#include <signal.h>

int main(int argc, const char *argv[])
{
    int i;
    int sum=0;
    sigset_t sigs;
    sigemptyset(&sigs);
    sigaddset(&sigs,SIGINT);
    sigprocmask(SIG_BLOCK,&sigs,0);

    for(i=0;i<10;++i){
        sum+=i;
        sleep(1);
    }
    printf("sum=%d\n",sum);
    sigprocmask(SIG_UNBLOCK,&sigs,0);
    printf("over!\n");
    return 0;
}

运行时,按下ctrl+C(SIGINT) 没效果,当解除屏蔽信号马上起效,如果按了ctrl+C   “over!”不会输出

 

 (2). 信号屏蔽的切换

       int sigsuspend(const sigset_t *mask); 

       sigsuspend是阻塞函数,对参数信号屏蔽,但是对参数没有指定的信号,但当没有屏蔽的信号处理函数调用完毕sigsuspend函数返回

例子:

 

#include <stdio.h>
#include <signal.h>
#include <unistd.h>

void handle(int s)
{
    printf("非屏蔽信号发生!\n");
}

int main(int argc, const char *argv[])
{
    printf("pid:%d\n",getpid());
    sigset_t sigs;
    sigemptyset(&sigs);
    sigaddset(&sigs,SIGINT);

    signal(SIGUSR1,handle);

    printf("屏蔽开始\n");
    sigsuspend(&sigs);
    printf("屏蔽解除\n");

    return 0;
}

 

 

运行到 sigsuspend  程序阻塞,只有发出SIGUSR1 的信号 (kill -10 pid)才能继续进行

 

#include <stdio.h>
#include <signal.h>
#include <unistd.h>

void handle(int s)
{
    printf("非屏蔽信号发生!\n");
}

int main(int argc, const char *argv[])
{
    printf("pid:%d\n",getpid());
    sigset_t sigs;
    sigemptyset(&sigs);
    sigaddset(&sigs,SIGINT);
    
    signal(SIGUSR1,handle);
    sigaddset(&sigs,SIGUSR1); //把SIGUSR1加入屏蔽集


    printf("屏蔽开始\n");
    sigsuspend(&sigs);
    printf("屏蔽解除\n");

    return 0;
}


这样发出SIGUSR1 信号也不能进行下去。因为SIGUSR1加入了屏蔽的范畴

 

 

sigsuspend返回条件:1.信号发生并且信号是非屏蔽信号

                                      2.信号必须处理,而且处理函数返回后,sigsuspend在返回

sigsuspend主要用途:设置新的屏蔽信号,保存旧的屏蔽信号,而且但sigsuspend返回的时候自己会恢复旧的屏蔽信号

例子:

 

 

#include <stdio.h>
#include <signal.h>

void handle(int s)
{
    printf("抽空处理SIGINT信号\n");
}

int main(int argc, const char *argv[])
{
    int i;
    int sum=0;
    
    sigset_t sigs;
    sigset_t sigp;
    sigset_t sigq;

    signal(SIGINT,handle);

    sigemptyset(&sigs);
    sigemptyset(&sigp);
    sigemptyset(&sigq);
    sigaddset(&sigs,SIGINT);
    sigprocmask(SIG_BLOCK,&sigs,0);

    for(i=0;i<10;++i){
        sum+=i;
        sigpending(&sigp);
        if(sigismember(&sigp,SIGINT)){
            printf("SIGINT在排队!\n");

            sigsuspend(&sigq);
        }

        sleep(1);
    }
    printf("sum=%d\n",sum);
    sigprocmask(SIG_UNBLOCK,&sigs,0);
    printf("over!\n");
    return 0;
}

 

 

不想背SIGINT信号所干扰,又想偷偷的处理一下SIGINT信号
这样就可以把信号控制在每个局部,使信号可控(如果不这样做信号在那里都有可能发生,导致程序不可控)

                  

 

 (3). 查询被屏蔽的信号

       int sigpending(sigset_t *set); 

例子:

 

#include <stdio.h>
#include <signal.h>

int main(int argc, const char *argv[])
{
    int i;
    int sum=0;
    
    sigset_t sigs;
    sigset_t sigp;


    sigemptyset(&sigs);
    sigaddset(&sigs,SIGINT);
    sigprocmask(SIG_BLOCK,&sigs,0);

    for(i=0;i<10;++i){
        sum+=i;
        sigpending(&sigp);
        if(sigismember(&sigp,SIGINT)){
            printf("SIGINT!\n");
        }

        sleep(1);
    }
    printf("sum=%d\n",sum);
    sigprocmask(SIG_UNBLOCK,&sigs,0);
    printf("over!\n");
    return 0;
}

当按下ctrl+C (SIGINT) 时就会显示 “SIGINT!”