当前位置:首页 > 全部文章 > 正文

signal函数范例

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

void sig_usr(int signo){
	if(signo == SIGUSR1){
		printf("收到了SIGUSR1信号\n");
	}
	else if(signo == SIGUSR2){
		printf("收到了SIGUSR2信号\n");
	}
	else{
		printf("收到了为捕捉的信号%d信号\n",signo);
	}
}

// #include <signal.h>

int main()
{
	if(signal(SIGUSR1,sig_usr) == SIG_ERR){
		printf("无法捕捉SIGUSR1信号\n");
	}
	if(signal(SIGUSR2,sig_usr) == SIG_ERR){
		printf("无法捕捉SIGUSR2信号\n");
	}

	for (;;)
	{
		sleep(1);
		printf("rest 1 second!\n");
	}
	return 0;
}

你提供的这段 C 程序演示了如何使用 Unix/Linux 系统中的 信号(signal) 机制来捕捉并处理用户自定义信号 SIGUSR1和 SIGUSR2。下面是对代码的详细解释以及一些注意事项:


🧠 代码功能概述

主要功能:

  1. 注册信号处理函数:

    • 使用 signal()函数为两个用户自定义信号 SIGUSR1和 SIGUSR2注册同一个处理函数 sig_usr

    • 如果注册失败(比如返回 SIG_ERR),则打印错误信息。

  2. 主循环:

    • 程序进入一个无限循环 for (;;),每秒钟打印一次 "rest 1 second!",并调用 sleep(1)暂停一秒。

    • 在这期间,如果进程收到了 SIGUSR1或 SIGUSR2信号,就会中断 sleep并跳转到对应的信号处理函数 sig_usr,打印相应的提示信息,然后返回继续执行主循环。

  3. 信号处理函数 sig_usr

    • 根据接收到的信号编号 signo判断是哪个信号,并打印不同的提示信息。

    • 如果收到的是其他未处理的信号,也会打印出信号编号。

你提供的这段 C 程序演示了如何使用 Unix/Linux 系统中的 信号(signal) 机制来捕捉并处理用户自定义信号 SIGUSR1和 SIGUSR2。下面是对代码的详细解释以及一些注意事项:


🧠 代码功能概述

主要功能:

  1. 注册信号处理函数:

    • 使用 signal()函数为两个用户自定义信号 SIGUSR1和 SIGUSR2注册同一个处理函数 sig_usr

    • 如果注册失败(比如返回 SIG_ERR),则打印错误信息。

  2. 主循环:

    • 程序进入一个无限循环 for (;;),每秒钟打印一次 "rest 1 second!",并调用 sleep(1)暂停一秒。

    • 在这期间,如果进程收到了 SIGUSR1或 SIGUSR2信号,就会中断 sleep并跳转到对应的信号处理函数 sig_usr,打印相应的提示信息,然后返回继续执行主循环。

  3. 信号处理函数 sig_usr

    • 根据接收到的信号编号 signo判断是哪个信号,并打印不同的提示信息。

    • 如果收到的是其他未处理的信号,也会打印出信号编号。


✅ 代码逐段解析

#include <stdio.h>#include <unistd.h>#include <signal.h>
  • 引入标准输入输出、Unix 标准函数(比如 sleep)、以及信号处理相关头文件。


void sig_usr(int signo){    if(signo == SIGUSR1){        printf("收到了SIGUSR1信号\n");
    }    else if(signo == SIGUSR2){        printf("收到了SIGUSR2信号\n");
    }    else{        printf("收到了为捕捉的信号%d信号\n",signo);
    }
}
  • 这是一个自定义的信号处理函数,当进程接收到信号时会被调用。

  • 它根据传入的信号编号 signo来判断是哪个信号,并做出相应的响应。

  • 如果不是 SIGUSR1或 SIGUSR2,则认为是“未专门处理的其他信号”,但也打印其编号。

⚠️ 注意:虽然你在这里处理了“其他信号”,但在实际应用中,通常只处理你明确想处理的信号,其他信号可以忽略或按默认方式处理,避免意外行为。


int main(){    if(signal(SIGUSR1,sig_usr) == SIG_ERR){        printf("无法捕捉SIGUSR1信号\n");
    }    if(signal(SIGUSR2,sig_usr) == SIG_ERR){        printf("无法捕捉SIGUSR2信号\n");
    }
  • 使用 signal()函数为 SIGUSR1和 SIGUSR2设置处理函数为 sig_usr

  • 如果 signal()返回 SIG_ERR,说明设置失败(比如系统不支持该信号,或发生其他错误),此时打印错误信息。

  • 注意: signal()函数的行为在不同的系统/标准(如 POSIX)中可能略有差异,更现代的程序有时会使用 sigaction()来代替 signal(),因为后者更可靠、可预测。


for (;;)
    {
        sleep(1);        printf("rest 1 second!\n");
    }    return 0;
}
  • 主线程进入一个死循环,每秒休眠一次,然后打印休息信息。

  • 在这期间,如果有信号发生(比如用户从终端发送了 SIGUSR1),那么 sleep()可能会被中断,控制权转交给信号处理函数,处理完毕后继续从 sleep()被中断的位置继续执行(可能不会补足剩余的睡眠时间)。


🛠️ 如何测试这个程序?

由于 SIGUSR1和 SIGUSR2是用户自定义信号,你不能直接通过键盘输入触发它们,但可以通过以下方法从另一个终端窗口或者命令行向该进程发送信号:

步骤:

  1. 编译程序:

    gcc your_program.c -o signal_test

  2. 运行程序:

    ./signal_test

    你会看到程序每隔一秒打印:

    rest 1 second!

  3. 查找进程 ID(PID):

    在运行程序的终端,你可以看到进程的 PID(通常是终端显示的一串数字,比如 12345),或者你可以通过以下命令查找:

    ps aux | grep signal_test

  4. 从另一个终端发送信号:

    假设你的程序进程 PID 是 12345,那么:

    📌 注意:SIGUSR1和 SIGUSR2具体信号编号可能因操作系统不同而稍有差异,常见的如:

    你可以运行 kill -l查看所有信号及其编号。

    • SIGUSR1→ 10

    • SIGUSR2→ 12

    • 发送 SIGUSR1信号:

      kill -USR1 12345

      或者简写为:

      kill -10 12345    # SIGUSR1 的信号编号通常是 10(可能因系统而异)

    • 发送 SIGUSR2信号:

      kill -USR2 12345

      或者:

      kill -12 12345    # SIGUSR2 的信号编号通常是 12

  5. 观察输出:

    当你发送信号后,原本正在循环打印 "rest 1 second!"的程序,会先打印出信号处理函数中的信息,例如:

    收到了SIGUSR1信号
    rest 1 second!

    信号处理完毕后,程序会从 sleep()被唤醒并继续执行主循环。


⚠️ 注意事项与改进建议

1. 关于 signal()函数

  • signal()是一个较老的、不可靠的信号处理接口,在某些系统上它的行为可能不一致。

  • 更推荐使用 sigaction() 函数来注册信号处理,因为它更可靠、功能更强大,且行为是标准化的。

  • 如果你想编写更健壮的程序,建议学习并改用 sigaction

2. 关于 sleep()被信号中断

  • 当信号发生时,sleep()可能会被中断,并提前返回。你可以检查其返回值以确定实际睡眠了多久(不过在这个简单示例中没有处理)。

  • 如果你希望被信号打断后继续睡眠剩余时间,需要额外处理,比如使用循环和 sleep()剩余时间。

3. 信号处理函数应尽量简单

  • 在信号处理函数中,只建议做非常有限且安全的事情,比如设置标志位。不要调用非异步信号安全的函数(如 printf实际上并不是完全安全的!但在简单测试中通常不会有大问题)。

  • 生产环境中如果要做复杂操作,建议在信号处理函数里修改一个 volatile sig_atomic_t类型的全局变量,然后在主循环中检查该变量并处理。


下面详细解释一下

当然!我们来详细解释这一行代码:

signal(SIGUSR1, sig_usr);

这是 C 语言中用于注册信号处理函数的系统调用,它来自 <signal.h>头文件。

这里传递了两个参数:


🧩 参数一:SIGUSR1

含义:

  • 这是第一个参数,表示你要捕捉的信号类型(Signal Number / Signal Name)

  • SIGUSR1是一个用户自定义信号,它是由 POSIX 标准定义 的,专门留给用户程序使用的信号。

  • 它的作用是:让程序员可以自定义用途,比如用于进程间通信(IPC)、控制程序行为等。

🧩 参数二:sig_usr

含义:

  • 这是第二个参数,表示当 SIGUSR1信号发生时,操作系统应该调用哪个函数(即信号处理函数 / Signal Handler)去处理这个信号。

  • 在你的代码中,sig_usr是你自己定义的一个函数:

它的函数签名必须是:

void 函数名(int signo);

即:接收一个 int 类型的参数(信号编号),并且没有返回值(void)

🤝 两个参数合起来的含义:

signal(SIGUSR1, sig_usr);

🔍 翻译成人话就是:

“当我的程序收到 SIGUSR1信号时,请操作系统调用我的函数 sig_usr来处理这个信号。”

🔄 信号处理函数的调用时机

  • 信号是异步事件,可能发生在程序执行的任意时刻(比如你在 sleep()或者打印时)。

  • 当你的进程接收到 SIGUSR1信号时(比如通过另一个终端用 kill -SIGUSR1 <pid>命令发送),操作系统会中断程序当前正在做的事情,转而调用你注册的 sig_usr函数。

  • 在 sig_usr函数执行完毕后,程序会从中断的地方继续执行(除非你做了特殊处理)。

📌 补充:signal()函数的返回值

你原来的代码中还做了返回值判断:

if (signal(SIGUSR1, sig_usr) == SIG_ERR) {    printf("无法捕捉SIGUSR1信号\n");
}
  • signal()如果成功,会返回之前注册的信号处理函数(可能是 SIG_DFLSIG_IGN或你之前设置的某个函数指针)。

  • 如果失败,它会返回 SIG_ERR,这是一个特殊的宏,代表出现了错误(比如不合法的信号编号)。

  • 所以这里的判断是在检测:“我能不能成功为 SIGUSR1设置 sig_usr这个处理函数”。

更新时间 2025-09-21