Linuxc/c++之信号基础

简介: 这篇文章详细介绍了Linux下C/C++信号的基本概念、产生原因、处理过程、分类、注册与发送方法,以及信号屏蔽的机制。

1. 信号的本质

信号的本质就是整数,是用户模式下用来模拟硬件中断的一种方式。

硬件中断(真正中断):物理层面

软件中断(模拟中断):模拟

2. 信号的产生

  1. 硬件产生
  2. 内核产生
  3. 进程产生

3. 信号的处理过程

如果当前进程A正在运行,然后内核,硬件或者其它进程发送信号给进程A

进程A接收到信号之后:

1. 直接做信号本身规定的对应处理(例如SIGINT就是关闭进程(ctrl + C))

2. 做事先注册好的信号处理(信号注册函数signal(信号,处理函数))

3. 信号被屏蔽,等待信号屏蔽解除然后做出相应处理

4. 信号的分类

4.1 Linux提供的64个信号

kill -l

4.2 按照可靠性分类

不可靠信号:非实时性的信号 由UNIX提供的 1 --- 31

可靠信号:实时性的信号 后来扩充的 32 --- 64

4.3 按照类型分

标准信号:操作系统提供的信号

自定义信号:用户自定义的信号 SIG_USR

5. 信号注册与信号发送

5.1 信号注册

5.1.1 signal函数

不含signal 函数的一个进程, 使用Ctrl + C 或者 Ctrl + \ 可以结束当前进程

标准信号处理函数

typedef void (*sighandler_t)(int);

sighandler_t signal(int signum,sighandler_t handler);

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

int main(){

    printf("pid: %d\n",getpid());

    int n = 0;
    while(1){
        printf(">> %d\n",n++);
        sleep(1);
    }

    return 0;
}

使用signal函数注册SIGINT信号后, 当注册SIGINT信号之后, 使用Ctrl + C 发送SIGINT信号就会调用自定义的信号处理函数

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

//SIGINT信号处理函数
void hand(int val){
    printf("val---%d\t想干掉我,没门!\n",val);   //val 为 signum  SIGINT的值为2
}

int main(){

    signal(SIGINT,hand); //注册信号处理(信号自己处理,不交给操作系统处理)
    printf("pid: %d\n",getpid());

    int n = 0;
    while(1){
        printf(">> %d\n",n++);
        sleep(1);
    }

    return 0;
}

5.1.2 sigaction函数(高级信号注册函数)

sigaction()高级信号注册函数对应sigqueue()高级信号发送函数

int sigaction(int signum, const struct sigaction* act, struct sigaction* oldact)

参数一: 信号的id(例如SIGINT信号的signum 为 2)

参数二: 新的信号处理方式

参数三: 旧的信号处理方式

struct sigaction{

void (*sa_handler)(int); //原来的信号处理函数

void (*sa_sigaction)(int, siginfo_t *,void *); //高级的信号处理函数

sigset_t sa_mask; //信号屏蔽

int sa_flags; //使用原来的还是高级的信号处理函数

void (*sa_restorer)(void); //目前未使用

};

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>

void hand(int n){
    printf("基本的信号处理函数!\n");
}
//参数1 信号   参数2 信号的一些信息(信号发送者给传入的数据)    参数3 注册信号传入的参数
void handler(int n,siginfo_t* siginfo,void* arg){
    printf("高级的信号处理函数!\n");
    printf("n:%d,msg:%d\n",n,siginfo->si_int);
}
int main(){
    printf("pid--->%d\n",getpid());
    struct sigaction act = {0};
    struct sigaction oldAct = {0};

    act.sa_handler = hand;      //信号处理函数
    act.sa_sigaction = handler;  //高级信号处理函数
    //sa_mask                    //信号屏蔽
    //*sa_restorer               //目前未使用
    act.sa_flags = SA_SIGINFO;  //sigaction替换handler

    //SIGINT
    sigaction(2,&act,&oldAct);  //注册高级信号处理
    //参数三作为返回值存在的

    int n = 0;
    while(1){
        printf(">>> %d\n",n++);
        sleep(1);
    }

    return 0;
}

5.2 信号发送

5.2.1 kill函数

kill(pid,sid); 参数1: 进程id 参数二: 信号

#include <stdio.h>
#include <unistd.h>
//信号的发送之kill函数
int aton(char* str){
    int num = 0;
    while(*str){
        num = num * 10 + (*str-'0');
        str++;
    }
    return num;
}

int main(int argc,char* argv[]){
    int pid = aton(argv[1]);   //进程id
    int sid = aton(argv[2]);   //信号id  2(SIGINT信号) 
    printf("%d %d",pid,sid);

//       进程id  信号id
    kill(pid,sid);
    return 0;
}

5.2.2 sigqueue函数(高级信号发送函数)

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>

int aton(char* str){
    int num = 0;
    while(*str){
        num = num * 10 + (*str-'0');
        str++;
    }

    return num;
}
int main(int argc,char* argv[]){
    int pid = aton(argv[1]);
    int sid = aton(argv[2]);

    union sigval u;
    u.sival_int = 6666666;

    sigqueue(pid,sid,u);

    return 0;
}

5.2.3 kill命令

kill sid -s pid

sid: 信号id pid: 进程id

6. 信号屏蔽

    **int sigprocmask(int how, const sigset\_t\* set, sigset\_t\* oldset);**

    **参数一: 是否屏蔽(或者其它的操作)  SIG\_BLOCK(屏蔽)  SIG\_UNBLOCK(不屏蔽)**

    **参数二: 指向信号集的指针,新设的信号集**

    **参数三: 指向信号集的指针,原来的信号集**

    **返回值: 成功返回 0**

    **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); //判断某个信号是否在信号集中**
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>

void hand(int n){
    printf("基本的信号处理函数!\n");
}
int main(){
    printf("pid: %d\n",getpid());
    sigset_t set,oldSet;
    sigemptyset(&set);     //清空信号集

    sigaddset(&set,2);
    //5秒钟不设置信号屏蔽
    signal(2,hand);
    sleep(5);
    //设置信号屏蔽20秒
    //sigismember  判断信号是否在信号集中
    int ret;
    if(1 == sigismember(&set,2)){
        printf("设置信号屏蔽!\n");
        ret = sigprocmask(SIG_BLOCK,&set,&oldSet);
        //参数1 屏蔽还是解除屏蔽
        //参数3为返回值  返回old信号集
        if(0 == ret) 
            printf("设置信号屏蔽成功!\n");
        else
            printf("设置信号屏蔽失败!\n");
    }
    sleep(20);
    //解除信号屏蔽
    if(1 == sigismember(&set,2)){
        printf("解除信号屏蔽!\n");
        ret = sigprocmask(SIG_UNBLOCK,&set,&oldSet);
        if(0 == ret) 
            printf("解除信号屏蔽成功!\n");
        else
            printf("解除信号屏蔽失败!\n");
    }
    while(1);

    return 0;
}

相关文章
|
11月前
|
算法 调度 C++
C++项目实战-信号(二)
C++项目实战-信号(二)
50 0
|
5月前
|
算法 数据处理 C++
Franca IDL与CommonAPI C++ D-Bus实战教程:方法、信号和属性传递详解
Franca IDL与CommonAPI C++ D-Bus实战教程:方法、信号和属性传递详解
139 0
|
5月前
|
算法 编译器
[C++&Qt] 通过信号与槽传递数据
[C++&Qt] 通过信号与槽传递数据
140 0
|
5月前
|
设计模式 开发框架 安全
C++ Qt开发:如何使用信号与槽
在Qt中,信号与槽(Signal and Slot)是一种用于对象之间通信的机制。是Qt框架引以为傲的一项机制,它带来了许多优势,使得Qt成为一个强大且灵活的开发框架之一。信号与槽的关联通过`QObject::connect`函数完成。这样的机制使得对象能够以一种灵活而松散耦合的方式进行通信,使得组件之间的交互更加灵活和可维护。
|
11月前
|
存储 NoSQL Linux
C++信号的使用
C++信号的使用
95 0
|
11月前
|
NoSQL 算法 Linux
C++项目实战-信号(一)
C++项目实战-信号(一)
73 0
|
Unix Linux C++
​​软件开发入门教程网之​​C++ 信号处理
信号是由操作系统传给进程的中断,会提早终止一个程序。在 UNIX、LINUX、Mac OS X 或 Windows 系统上,可以通过按 Ctrl+C 产生中断。有些信号不能被程序捕获
|
Unix Linux C++
​​软件开发入门教程网之​​C++ 信号处理信号
是由操作系统传给进程的中断,会提早终止一个程序。在 UNIX、LINUX、Mac OS X 或 Windows 系统上,可以通过按 Ctrl+C 产生中断
|
编译器 Linux Android开发
Android C++系列:Linux信号(三)
例如:strtok就是一个不可重入函数,因为strtok内部维护了一个内部静态指针,保存上一 次切割到的位置,如果信号的捕捉函数中也去调用strtok函数,则会造成切割字符串混乱, 应用strtok_r版本,r表示可重入。
188 0
|
存储 Linux Android开发
Android C++系列:Linux信号(二)
如果oset是非空指针,则读取进程的当前信号屏蔽字通过oset参数传出。如果set是非 空指针,则更改进程的信号屏蔽字,参数how指示如何更改。如果oset和set都是非空指针, 则先将原来的信号屏蔽字备份到oset里,然后根据set和how参数更改信号屏蔽字。假设当前 的信号屏蔽字为mask,下表说明了how参数的可选值。
94 0