#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
。下面是对代码的详细解释以及一些注意事项:
🧠 代码功能概述
主要功能:
注册信号处理函数:
使用
signal()
函数为两个用户自定义信号SIGUSR1
和SIGUSR2
注册同一个处理函数sig_usr
。如果注册失败(比如返回
SIG_ERR
),则打印错误信息。主循环:
程序进入一个无限循环
for (;;)
,每秒钟打印一次"rest 1 second!"
,并调用sleep(1)
暂停一秒。在这期间,如果进程收到了
SIGUSR1
或SIGUSR2
信号,就会中断sleep
并跳转到对应的信号处理函数sig_usr
,打印相应的提示信息,然后返回继续执行主循环。信号处理函数
sig_usr
:根据接收到的信号编号
signo
判断是哪个信号,并打印不同的提示信息。如果收到的是其他未处理的信号,也会打印出信号编号。
你提供的这段 C 程序演示了如何使用 Unix/Linux 系统中的 信号(signal) 机制来捕捉并处理用户自定义信号 SIGUSR1
和 SIGUSR2
。下面是对代码的详细解释以及一些注意事项:
🧠 代码功能概述
主要功能:
注册信号处理函数:
使用
signal()
函数为两个用户自定义信号SIGUSR1
和SIGUSR2
注册同一个处理函数sig_usr
。如果注册失败(比如返回
SIG_ERR
),则打印错误信息。主循环:
程序进入一个无限循环
for (;;)
,每秒钟打印一次"rest 1 second!"
,并调用sleep(1)
暂停一秒。在这期间,如果进程收到了
SIGUSR1
或SIGUSR2
信号,就会中断sleep
并跳转到对应的信号处理函数sig_usr
,打印相应的提示信息,然后返回继续执行主循环。信号处理函数
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
是用户自定义信号,你不能直接通过键盘输入触发它们,但可以通过以下方法从另一个终端窗口或者命令行向该进程发送信号:
步骤:
编译程序:
gcc your_program.c -o signal_test
运行程序:
./signal_test
你会看到程序每隔一秒打印:
rest 1 second!
查找进程 ID(PID):
在运行程序的终端,你可以看到进程的 PID(通常是终端显示的一串数字,比如
12345
),或者你可以通过以下命令查找:ps aux | grep signal_test
从另一个终端发送信号:
假设你的程序进程 PID 是
12345
,那么:📌 注意:
SIGUSR1
和SIGUSR2
的具体信号编号可能因操作系统不同而稍有差异,常见的如:你可以运行
kill -l
查看所有信号及其编号。SIGUSR1
→ 10SIGUSR2
→ 12发送
SIGUSR1
信号:kill -USR1 12345
或者简写为:
kill -10 12345 # SIGUSR1 的信号编号通常是 10(可能因系统而异)
发送
SIGUSR2
信号:kill -USR2 12345
或者:
kill -12 12345 # SIGUSR2 的信号编号通常是 12
观察输出:
当你发送信号后,原本正在循环打印
"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_DFL
、SIG_IGN
或你之前设置的某个函数指针)。如果失败,它会返回
SIG_ERR
,这是一个特殊的宏,代表出现了错误(比如不合法的信号编号)。所以这里的判断是在检测:“我能不能成功为
SIGUSR1
设置sig_usr
这个处理函数”。