linux进程间通信-信号_linux进程间通信之信号

信号(Signal)

信号的基本概念

信号是一种异步的、非阻塞的通信机制,用于通知接收进程某个事件已经发生。例如,SIGINT信号通常由 Ctrl+C触发,用于终止进程。

信号(Signal)也叫“用户态中断”,用于异步通知进程某个事件的发生,每个信号都有一个唯一的编号和一个对应的处理动作。当某个信号发生时,内核会向相应的进程发送该信号,并触发预设的信号处理函数或执行默认操作。

信号通常用于简单的事件通知,如终止进程、暂停执行等,而不适合用于复杂的数据传输任务。

信号的一些特点

  1. 信号的异步性: 信号在任何时候都可能被触发,这意味着它们是异步的。无法预测信号的确切发生时间,因此也无法保证信号处理的顺序。
  2. 信号阻塞: 进程可以选择阻塞某些信号,这意味着在阻塞期间,这些信号不会被传递或处理,这破坏了信号的及时性。
  3. 信号丢失: 如果一个进程在很短的时间内连续收到相同的信号,可能会导致信号丢失,因为Linux内核默认只会将该信号传递一次。
  4. 信号与进程状态: 信号的传递和处理还依赖于进程的状态。如果进程处于不可中断的睡眠状态,信号可能无法立即传递。
  5. 缺乏流量控制: 信号机制没有内置的流量控制,无法控制信号的发送速率,这可能导致信号的丢失或延迟。
  6. 信号的有限行为: 信号处理函数中不应该执行可能导致进程状态不一致的操作,如打开文件、分配内存等。信号处理函数应该尽可能快地执行完。

信号的种类

Linux系统中定义了许多种类的信号,每个信号都有其特定的含义和用途。常见的信号包括:

  1. SIGINT(中断信号,通常由Ctrl+C产生):用于终止前台进程。
  2. SIGQUIT(退出信号,通常由Ctrl+\产生):用于终止进程并产生core文件。
  3. SIGTERM(终止信号):用于正常终止进程。
  4. SIGKILL(强制终止信号):强制终止进程,该信号不能被捕获或忽略。
  5. SIGSTOP(暂停信号):暂停进程的执行。
  6. SIGCONT(继续信号):恢复被暂停的进程执行。

此外,还有许多其他类型的信号,如SIGUSR1SIGUSR2等用户自定义信号,以及SIGHUPSIGCHLDSIGPIPESIGALRM等系统定义的信号。

信号的处理方式

Linux内核提供了三种处理信号的方式:

  1. 忽略信号:进程可以选择忽略某个信号,当该信号到达时,系统会自动忽略它。但是有些信号(如SIGKILLSIGSTOP)不能被忽略。
  2. 捕捉信号:进程可以注册信号处理函数,当信号到达时,内核会调用相应的处理函数来处理该信号。这使得进程能够在接收到信号时执行特定的操作,如清理资源、保存数据等。
  3. 默认处理方式:对于大多数信号,系统都会提供一个默认的处理方式。例如,SIGTERM的默认处理方式是终止进程。如果进程没有为某个信号指定处理函数,那么当该信号到达时,系统会执行默认的操作。

需要注意的是,如果一个用户进程捕获了SIGINT信号(通常是通过按下Ctrl+C产生的),并且为这个信号注册了一个自定义的处理函数,那么当SIGINT信号发生时,用户进程将执行这个自定义的处理函数,而不是Linux内核默认的终止进程行为。

此外,还有一些信号是不能被用户态程序捕获或忽略的,例如SIGKILLSIGSTOP。这些信号用于强制终止或暂停进程,并且只能由操作系统内核处理。

在C语言中,可以使用 signal()函数或 sigaction()函数来设置信号处理函数。sigaction()函数提供了更多的控制选项,并且是可移植的,推荐在编程中使用。

信号的发送与接收

信号的发送可以通过多种方式实现,如通过终端按键产生信号(如Ctrl+C)、调用系统函数向进程发送信号(如kill()函数、raise()函数)、由软件条件产生信号(如alarm()函数和SIGALRM信号)以及硬件异常产生信号等。当进程接收到信号时,会根据预设的信号处理函数或默认操作来响应。

实验:处理 SIGINT信号

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

void handle_sigint(int sig) {
    printf("Caught signal %d\n", sig);
}

int main() {
    // 设置SIGINT信号的处理函数
    signal(SIGINT, handle_sigint);

    while (1) {
        printf("Waiting for SIGINT...\n");
        sleep(1);
    }

    return 0;
}

实验结果 

linux进程间通信-信号_linux进程间通信之信号_02

实验解释:

当用户运行这个程序时,它会进入一个无限循环,并定期打印消息表明它正在等待 SIGINT 信号。当用户按下 Ctrl+C 时,会向前台进程组发送 SIGINT 信号。由于程序已经通过 signal 函数注册了 SIGINT 的自定义处理函数 handle_sigint,因此程序不会终止,而是调用 handle_sigint 函数。
在 handle_sigint 函数中,程序简单地打印出接收到的信号编号,然后返回。由于程序没有执行任何退出操作,它会在 sleep 调用结束后继续执行,再次打印消息,并等待下一个 SIGINT 信号。