美文网首页
Linux进程间通信——信号

Linux进程间通信——信号

作者: superLiS | 来源:发表于2019-07-25 13:44 被阅读0次

一、认识信号

信号(Signals)是Unix、类Unix以及其他POSIX兼容的操作系统中进程间通讯的一种有限制的方式。它是一种异步的通知机制,用来提醒进程一个事件已经发生。当一个信号发送给一个进程,操作系统中断了进程正常的控制流程,此时,任何非原子操作都将被中断。如果进程定义了信号的处理函数,那么它将被执行,否则就执行默认的处理函数。

信号是进程间通信机制中唯一的异步通信机制,可以看作是异步通知,通知接收信号的进程有哪些事情发生了。也可以简单理解为信号是某种形式上的软中断。

二、信号来源

一般情况下,信号的来源可分为以下三种:

  • 硬件方式:除数为零、无效的存储访问等硬件异常产生信号。这些事件通常由硬件(如:CPU)检测到,并将其通知给Linux操作系统内核,然后内核生成相应的信号,并把信号发送给该事件发生时正在进行的程序。
  • 软件方式:用户在终端下调用kill命令向进程发送任务信号、进程调用killsigqueue函数发送信号、当检测到某种软件条件已经具备时发出信号,如由alarmsettimer设置的定时器超时时将生成SIGALRM信号等多种情景均可产生信号。
  • 键盘输入:当用户在终端上按下某键时,将产生信号。如按下组合键Ctrl+C将产生一个SIGINT信号,Ctrl+\产生一个SIGQUIT信号等。

三、信号种类

可运行kill -l查看Linux支持的信号列表:

sl@Li:~/Works$ kill -l
 1) SIGHUP   2) SIGINT   3) SIGQUIT  4) SIGILL   5) SIGTRAP
 6) SIGABRT  7) SIGBUS   8) SIGFPE   9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT   17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG  24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM   27) SIGPROF 28) SIGWINCH    29) SIGIO   30) SIGPWR
31) SIGSYS  34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3
38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
63) SIGRTMAX-1  64) SIGRTMAX

可以看到Linux中系统一共支持64种信号,其中1到31号信号为普通信号(也程为不可靠信号),34到64为实时信号(可靠信号)。

可靠信号与不可靠信号的区别:

  • 这里的不可靠主要是不支持信号队列,就是当多个信号发生在进程中的时候(收到信号的速度超过进程处理的速度的时候),这些没来的及处理的信号就会被丢掉,仅仅留下一个信号。
  • 可靠信号是多个信号发送到进程的时候(收到信号的速度超过进程处理信号的速度的时候),这些没来的及处理的信号就会排入进程的队列。等进程有机会来处理的时候,依次再处理,信号不丢失。

以下列出几个常用的信号:

信号 描述
SIGHUP 当用户退出终端时,由该终端开启的所有进程都退接收到这个信号,默认动作为终止进程。
SIGINT 程序终止(interrupt)信号, 在用户键入INTR字符(通常是Ctrl+C)时发出,用于通知前台进程组终止进程。
SIGQUIT SIGINT类似, 但由QUIT字符(通常是Ctrl+\)来控制. 进程在因收到SIGQUIT退出时会产生core文件, 在这个意义上类似于一个程序错误信号。
SIGKILL 用来立即结束程序的运行. 本信号不能被阻塞、处理和忽略
SIGTERM 程序结束(terminate)信号, 与SIGKILL不同的是该信号可以被阻塞和处理。通常用来要求程序自己正常退出。
SIGSTOP 停止(stopped)进程的执行. 注意它和terminate以及interrupt的区别:该进程还未结束, 只是暂停执行. 本信号不能被阻塞, 处理或忽略.

四、可重入函数

要注意信号处理函数的可重入问题,因为信号处理函数执行过程中有可能被其他信号再次中断,这时程序会跳到另一个信号的处理函数中,处理完成后再次返回当前处理函数,编写自己定义的信号处理函数的时候一定要注意这一点。信号可以理解为“软中断”,这样可重入函数就很好理解了。

五、信号的捕获

内核处理一个进程收到的信号的时机是在一个进程从内核态返回用户态时。所以,当一个进程在内核态下运行时,软中断信号并不立即起作用,要等到将返回用户态时才处理。进程只有处理完信号才会返回用户态,进程在用户态下不会有未处理完的信号。

内核处理一个进程收到的软中断信号是在该进程的上下文中,因此,进程必须处于运行状态。当进程接收到一个它忽略的信号时,进程丢弃该信号,就像没有收到该信号似的继续运行。

如果进程收到一个要捕捉的信号,那么进程从内核态返回用户态时执行用户定义的函数。而且执行用户定义的函数的方法很巧妙,内核在用户栈上创建一个新的层,该层中将返回地址的值设置成用户定义的处理函数的地址,这样进程从内核返回弹出栈顶时就返回到用户定义的函数处,从函数返回再弹出栈顶时,才返回原先进入内核的地方。 这样做的原因是用户定义的处理函数不能且不允许在内核态下执行(如果用户定义的函数在内核态下运行的话,用户就可以获得任何权限)。

下面引用一个例子说明这个过程:

  1. 用户程序注册了SIGQUIT信号的处理函数sighandler
  2. 当前正在执行main函数,这时发生中断或异常切换到内核态。
  3. 在中断处理完毕后要返回用户态的main函数之前检查到有信号SIGQUIT递达。
  4. 内核决定返回用户态后不是恢复main函数的上下文继续执行,而是执行sighandler函数,sighandlermain函数使用不同的堆栈空间,它们之间不存在调用和被调用的关系,是两个独立的控制流程。
  5. sighandler函数返回后自动执行特殊的系统调用sigreturn再次进入内核态。
  6. 如果没有新的信号要递达,这次再返回用户态就是恢复main函数的上下文继续执行了。

六、代码示例1

下面的代码收到程序退出信号后会执行用户定义的信号处理函数来替代系统默认的处理程序。

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

void sig_handle(int sig) {
    printf("received signal: %d, quit.\n", sig);
    exit(0);
}

int main () {
    signal(SIGINT, sig_handle);
    signal(SIGKILL, sig_handle);
    signal(SIGSEGV, sig_handle);
    signal(SIGTERM, sig_handle);

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

    printf("main quit.");

    return 0;
}

运行结果:

1
2
received signal: 15, quit.

七、代码示例2

这段代码功能与上面的例子差不多,信号也可以传参,但是更多的是通知功能,可传递的信息非常有限,如果需要传递大量的信息,可以考虑其他进程间通信方式。

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

void new_op(int, siginfo_t*, void*);

int main() {
    if (NULL == freopen("sigproc.log", "w", stdout)) {
        fprintf(stderr, "error redirecting stdout\n");
    }

    struct sigaction act;

    sigemptyset(&act.sa_mask);  //sa_mask指定在信号处理程序执行过程中,哪些信号应当被阻塞。缺省情况下当前信号本身被阻塞,防止信号的嵌套发送
    sigaddset(&act.sa_mask, SIGTERM);
    sigaddset(&act.sa_mask, SIGINT);
    act.sa_flags = SA_SIGINFO;  //SA_SIGINFO,当设定了该标志位时,表示信号附带的参数可以被传递到信号处理函数中
    act.sa_sigaction = new_op;

    if (sigaction(SIGINT, &act, NULL) < 0) {
        printf("install sigal error\n");
    }

    if (sigaction(SIGTERM, &act, NULL) < 0) {
        printf("install sigal error\n");
    }

    if (sigaction(SIGHUP, &act, NULL) < 0) {
        printf("install sigal error\n");
    }

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

    printf("end.");

    return 0;
}

void new_op(int signum, siginfo_t *info, void *myact) {
    printf("receive signal %d\n", signum);
    for (int i = 0; i < 5; ++i) {
        printf("signal processing: %d\n", i);
        sleep(1);
    }
    printf("process quit.");

    exit(0);
}

运行结果:

1
2
3
receive signal 15
signal processing: 0
signal processing: 1
signal processing: 2
signal processing: 3
signal processing: 4
process quit.

最后,欢迎关注微信公众号,Let's go! 让我思考一下.jpg

相关文章

  • Linux系统编程:Inter-Process Communic

    一、IPC——进程间通信 Linux系统提供的进程间通信的手段: 消息传递类:信号 匿名管道 命名管道 socke...

  • Linux进程间通信 -- signal初步(一)

    Linux进程间通信 -- signal初步 1. signal信号的初步理解 为了方便理解Linux信号量, 我...

  • Linux进程间通信- signal信号的捕捉与sigactio

    Linux进程间通信- signal信号的捕捉与sigaction函数 前面有提到Linux中信号的处理动作是用户...

  • Linux进程间通信——信号

    一、认识信号 信号(Signals)是Unix、类Unix以及其他POSIX兼容的操作系统中进程间通讯的一种有限制...

  • 8.ipc

    进程间通信 Linux中的进程间通信主要有:管道、FIFO、消息队列、信号量、共享存储以及网络IPC中的套接字。 ...

  • linux进程通信(IPC)

    本文摘抄自linux进程间通信信号可以看作一种粗糙的进程间通信的方式,用以向进程封闭的内存空间传递信息。为了让进程...

  • 五、进程间通信

    信号信号是linux操作系统进程间通信的一种方式,一个应用进程可以接受、发送信号给另一个进程,当进程捕获到某个信号...

  • Android Framework学习笔记之Binder

    Linux进程间通信的方式:管道(Pipe)、信号(Signal)、消息队列(Message)、共享内存(Shar...

  • linux信号与trap命令

    信号是什么信号作为一种进程间通信的方式,是linux进程管理使用最为广泛的方式,通过给进程发送信号,来传递信息,比...

  • Linux进程间通信--signal的阻塞与未决(三)

    Linux进程间通信--signal的阻塞与未决 实际执行信号的处理动作(handler)称为信号递达(deliv...

网友评论

      本文标题:Linux进程间通信——信号

      本文链接:https://www.haomeiwen.com/subject/izqhrctx.html