linux系统编程之信号(signal)的使用方法案例

信号处理过程:
中断源-》中断屏蔽-》保护现场-》中断处理程序-》中断恢复
信号称为软中断
//kill -l 查看所有signal信号 共计64个信号
//man 7 signal 查看signal信号的意思
//信号提供了一种异步处理的一种能力;
//action 是默认操作 在man 7 signal里面的表里面
//ctrl + \ 可以杀死3) SIGQUIT 退出程序
//signal(SIGINT,SIG_DFL) //从新关联了默认程序

一)信号与中断
(1)信号与中断的相似点
1)都采用异步通讯
2)都可以处理相应的信号(中断)服务程序
3)都可以返回
4)对信号或中断信号都可以进行屏蔽
(2)信号与中断的区别
1)中断有优先级,信号没有优先级
2)信号是用户态运行,中断是内核态运行
3)中断相应是及时的,信号是有延时的!
(3)信号的三种相应
1)忽略信号
2)捕获并处理信号
3)执行默认操作
二)signal信号的注册及简单应用

#include 
#include 
#include 
#include 
#include 
using namespace std;
#define ERR_EXIT(m) \
     do \
   {\
    perror(m);\
    exit(EXIT_FAILURE);\
   }while(0)
void handler(int num)
{
    cout<<"输出 num:"<<num<<endl;
}
int main(void)
{   
    // signal(SIGINT,handler); //安装(注册)一个信号,就是说将一个SIGINT信号和handler函数关联起来
    if(signal(SIGINT,handler) == SIG_ERR) //也可以对返回值进行处理
    {
        ERR_EXIT("signal error");
    }
    while(1);
    return 0;
}

小插曲:
/ /queue::iterator it1; //单端队列不可以遍历,没有迭代器
deque::iterator it2; //双端队列可以遍历
string::iterator i3; //string肯定可以遍历
二)信号分为可靠信号和不可靠信号
SIGRT runtime 实时信号也叫可靠信号 从34到64之后


1) signal 向父进程发送一个信号
#include 
#include 
#include 
#include 
#include 

using namespace std;

#define ERR_EXIT(m) \
     do \
	 {\
		perror(m);\
		exit(EXIT_FAILURE);\
	 }while(0)

void handler(int num)
{
    cout<<"输出 num:"<<num<<endl;
}

int main(void)
{   
    signal(SIGUSR1,handler); //安装(注册)一个信号,就是说将一个SIGINT信号和handler函数关联起来
    pid_t pid = fork();
    if(pid == 0)
    {
        kill(getppid(),SIGUSR1);//向父进程发送一个信号

        //pid = getpgrp();
        //kill(-pid,SIGUSR1);//向进程组发送一个信号;

        exit(EXIT_SUCCESS);
    }
    int n = 2;
    do
    {
       n = sleep(n); //剩余时间
    } while (n > 0);
    
    return 0;
}

三)pause函数在signal中的使用

1) pause函数使用方法;
//pause会睡眠,使调用者进程挂起,直到信号被唤醒才可以继续运行
#include 
#include 
#include 
#include 
#include 

using namespace std;

void handler(int num)
{
    cout<<"输出 num:"<<num<<endl;
    sleep(1);
}

int main(void)
{   
    signal(SIGINT,handler); //安装(注册)一个信号,就是说将一个SIGINT信号和handler函数关联起来
    while(1)
    {
        pause(); //pause函数进入中断睡眠状态
        cout<<"pause return"<<endl;
    }
    return 0;
}

四)更多信号学习
alarm SIGALRM //发送一个时钟信号
setitimer SIGALRM SIGVTALRM SIGGPROF
abort SIGABRT
给一个a.out的进程发送一个alrm信号的方法:
kill -ALRM ps aux|grep a.out|grep -v vi|grep -v grep|awk '{print $2}'
alarm函数的使用方法:

//alarm函数使用方法
#include 
#include 
#include 
#include 
#include 

using namespace std;

void handler(int num)
{
    cout<<"输出 num:"<<num<<endl;
    sleep(1);
}

int main(void)
{   
    signal(SIGALRM,handler); //安装(注册)一个信号,就是说将一个SIGINT信号和handler函数关联起来
    alarm(1);
    while(1)
    {
        pause(); //pause会睡眠,使调用者进程挂起,直到信号被唤醒才可以继续运行
        alarm(1); //每个1妙发送一个信号
    }
    return 0;
}

//在信号处理函数中,应使用可重入函数!!!
//信号从产生到递达的过程;
man sigpending 获取当前信号的未决状态集!
//信号集操作函数如下:
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int signum);
int sigdelset(sigset_t *set, int signum);
int sigismember(const sigset_t *set, int signum);

信号产生的过程:
信号从产生状态到递达状态,中间经过了一个未决状态;
未决状态产生的条件是信号发生的时候,信号被阻塞,如果阻塞被解除,则信号会变成递达状态;
五)sigprocmask sigemptyset sigaddset sigpending等函数的综合应用
测试代码如下:

#include 
#include 
#include 
#include 
#include 

using namespace std;

void handler(int sig)
{
    if(sig == SIGINT)
        cout<<"recv sig:"<<sig<<endl;
    else if(sig == SIGQUIT)
    {
        sigset_t uset;
        sigemptyset(&uset);
        sigaddset(&uset,SIGINT);
        sigprocmask(SIG_UNBLOCK,&uset,NULL); //去除屏蔽,
    }
}

void sigprint(sigset_t* set)
{
    int i;
    for(i = 1; i<NSIG; i++)
    {
        if(sigismember(set,i))
            putchar('1');
        else 
        {
            putchar('0');
        }
    }
    printf("\n"); //没有这句话,上面的不会正确输出,因为数据都在缓存里面呢!
}
int main(void)
{   
    signal(SIGINT,handler); //安装(注册)一个信号,就是说将一个SIGINT信号和handler函数关联起来
    signal(SIGQUIT,handler);
    sigset_t pset; //p pending 的意思
    sigset_t bset; //b应该是block 阻塞的意思
    sigemptyset(&bset);
    sigaddset(&bset,SIGINT); //把SIGINT信号加入到集合中
    sigprocmask(SIG_BLOCK,&bset,NULL); //把SIGINT信号使能阻塞,模拟信号触发后,会屏蔽,这样就不会递达
    while(1)
    {
        sigpending(&pset);
        sigprint(&pset);
        sleep(1);
    }
    return 0;
}

上述代码功能测试操作:
ctrl+c,差生sigint 信号,此时会产生屏蔽,“1”,ctrl+,会去除屏蔽,
killall a.out //杀死a.out进程
六)sigaction信号安装函数的使用方法
//信号被屏蔽了,就是被阻塞了
//按ctrl+c,则会输出结果
//sigaction即是结构体,又是函数名!

用sigaction安装sigint信号的案例
#include 
#include 
#include 
#include 
#include 

using namespace std;

void handler(int sig)
{
    cout<<"recv sig:"<<sig<<endl;
}

int main(void)
{   
    struct sigaction act;
    act.sa_handler = handler; //函数指针
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    sigaction(SIGINT,&act,NULL); //注册SIGINT信号函数
    while(1)
    {
        pause();
    }
    return 0;
}

七)sigaction的sa_mask的使用方法:
注:sa_mask如何影响sigaction函数的行为!

#include 
#include 
#include 
#include 
#include 

using namespace std;

void handler(int sig)
{
    cout<<"recv sig:"<<sig<<endl;
    sleep(5);
}

int main(void)
{   
    struct sigaction act;
    act.sa_handler = handler;
    sigemptyset(&act.sa_mask);
    sigaddset(&act.sa_mask,SIGQUIT); //将SIGQUIT加入到掩码当中,(SIGQUIT被屏蔽了)
    act.sa_flags = 0;
    sigaction(SIGINT,&act,NULL); //注册SIGINT信号函数
    while(1)
    {
        pause();
    }
    return 0;
}

测试结果:
./a.out
^Crecv sig:2
^^^^^^^^^^\退出 (核心已转储) //不能马上退出,因为SIGQUIT信号被屏蔽了
jiang@jiang-ThinkPad-X280:~/Cpp-Concurr

七)sigqueue函数的应用
sigqueue主要应用进程间通信!
//发送进程:
向一个进程发送SIGINT信号,并且将整型100也传进去!

#include
#include
#include
#include
#include

int main(int argc,char *argv[])
{
    if(argc!=2)
    {
        printf("arguments error!");
        exit(0);
    }
 
    pid_t pid=atoi(argv[1]);//将进程号转化为整数
    union sigval v;
    v.sival_int=100; //初始化sival_int的值
 
    sigqueue(pid,SIGINT,v);//向pid发送SIGINT信号,并且传递额外信息v
    return 0;
}

接收进程:
此进程等待接受信号的传入,并有SIGINT的信号处理函数!

#include
#include
#include
#include
#include

void handler(int,siginfo_t *,void *);
 
int main(void)
{
    struct sigaction act;
    act.sa_sigaction=handler;
    sigemptyset(&act.sa_mask);
    act.sa_flags=SA_SIGINFO; //必须填写SA_SIGINFO才可以进行进程间通信,传递数据
    if(sigaction(SIGINT,&act,NULL)<0)
    {
        printf("error");
        exit(0);
    }
    for(;;)
        pause();
    return 0;
}
 
void handler(int sig,siginfo_t * info,void *ctx)
{
    printf("recv a sid=%d data=%d data=%d\n",sig,info->si_value.sival_int,info->si_int);//打印接收信息
}

测试结果如下:
终端执行命令: ./a.out 10728
打开另一终端:./recv
收到的数据是:recv a sid=2 data=100 data=100

八)信号的可靠性和不可靠性
发送进程:
发送三个可靠信号和三个不可靠信号,延时3妙后,发送恢复屏蔽信号,代码如下:

#include
#include
#include
#include
#include

int main(int argc,char *argv[])
{
    if(argc!=2)
    {
        printf("arguments error!");
        exit(0);
    }
 
    pid_t pid=atoi(argv[1]);//将进程号转化为整数
    union sigval v;
    v.sival_int=100; //初始化sival_int的值
 
    sigqueue(pid,SIGINT,v);//向pid发送SIGINT信号,并且传递额外信息v
    sigqueue(pid,SIGINT,v);//向pid发送SIGINT信号,并且传递额外信息v
    sigqueue(pid,SIGINT,v);//向pid发送SIGINT信号,并且传递额外信息v
    sigqueue(pid,SIGRTMIN,v);//向pid发送SIGINT信号,并且传递额外信息v
    sigqueue(pid,SIGRTMIN,v);//向pid发送SIGINT信号,并且传递额外信息v
    sigqueue(pid,SIGRTMIN,v);//向pid发送SIGINT信号,并且传递额外信息v    
    sleep(3);
    kill(pid,SIGUSR1);
    return 0;
}

接收进程:
设置一个可靠信号(信号排队),一个不靠信号,和一个屏蔽字恢复信号,使之解除阻塞,代码如下:

#include
#include
#include
#include
#include

void handler(int);
 
int main(void)
{
    struct sigaction act;
    act.sa_handler=handler;
    sigemptyset(&act.sa_mask);
    act.sa_flags=0;

    sigset_t s;
    sigemptyset(&s);
    sigaddset(&s,SIGINT);
    sigaddset(&s,SIGRTMIN);
    sigprocmask(SIG_BLOCK,&s,NULL); //SIGINT或者SIGRTMIN信号到来的时候,就会被阻塞

    sigaction(SIGINT,&act,NULL); //不可靠信号
    sigaction(SIGRTMIN,&act,NULL);//可靠信号
    sigaction(SIGUSR1,&act,NULL);//安装一个信号,解除阻塞

    for(;;)
        pause();
    return 0;
}
 
void handler(int sig)
{
    if(sig == SIGINT || sig == SIGRTMIN)
    {
        printf("recv a sid=%d\n",sig);//打印接收信息
    }
    else if(sig == SIGUSR1)
    {
        sigset_t s;
        sigemptyset(&s);
        sigaddset(&s,SIGINT);
        sigaddset(&s,SIGRTMIN);
        sigprocmask(SIG_UNBLOCK,&s,NULL);
    }
}

测试结果如下:
接收进程运行结果:
./recv
recv a sid=34
recv a sid=34
recv a sid=34
recv a sid=2
发送进程运行结果:
./send 4739 //4739为接收进程号

你可能感兴趣的:(c语言,c++,linux)