
信号(Signal)是Linux系统中用于进程间异步通知的一种机制。当一个进程接收到信号时,它可以执行相应的信号处理函数,从而实现进程间的简单通信或事件通知。信号机制是Unix系统中最为古老的进程间通信机制之一,由操作系统内核提供。
在嵌入式 Linux 系统中,信号(Signal)是一种异步的进程间通信机制,用于通知进程发生了某种特定的事件。信号可以由系统内核产生,也可以由一个进程发送给另一个进程。信号的本质是一种软件中断,它可以打断进程当前的执行流程,转而执行信号处理函数,处理完信号后再返回到原来的执行点继续执行。
信号在嵌入式系统中具有重要的作用,例如用于处理程序的异常情况(如除零错误、段错误等)、实现进程间的异步通知(如父进程通知子进程终止)等。
轻量级、不可靠(可能丢失)、无优先级,适用于简单事件通知。
Linux 系统定义了一系列的信号,每个信号都有一个唯一的编号和名称。以下是一些常见的信号及其含义。
Ctrl+C 组合键时,会向当前前台进程发送该信号。在嵌入式 Linux 中,可以使用以下几种方式发送信号。
kill 命令在终端中,可以使用 kill 命令向指定的进程发送信号。例如,要向进程 ID 为 1234 的进程发送 SIGTERM 信号,可以使用以下命令:
kill -15 1234其中,-15 表示信号编号,也可以使用信号名称,如 kill -TERM 1234。
kill 函数在 C 语言程序中,可以使用 kill 函数向指定的进程发送信号。kill 函数的原型如下:
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);pid:目标进程的 ID。如果 pid > 0,则表示向指定 ID 的进程发送信号;如果 pid == 0,则表示向调用进程所在的进程组中的所有进程发送信号;如果 pid == -1,则表示向所有有权限发送信号的进程发送信号;如果 pid < -1,则表示向 ID 为 -pid 的进程组中的所有进程发送信号。sig:要发送的信号编号。errno 变量。
raise 函数raise 函数用于向调用进程自身发送信号。其原型如下:
#include <signal.h>
int raise(int sig);sig 为要发送的信号编号。alarm函数设置定时器(发送SIGALRM)。
unsigned int alarm(unsigned int seconds);进程可以对信号采取不同的处理动作,主要有以下三种。
每个信号都有一个默认的处理动作,如上述常见信号中提到的终止进程、暂停进程等。当进程接收到信号后,如果没有对该信号进行特殊处理,就会执行默认处理动作。
进程可以选择忽略某些信号,即不做任何处理。可以使用 signal 或 sigaction 函数将信号的处理动作设置为 SIG_IGN。例如,忽略 SIGINT 信号的代码如下:
#include <signal.h>
void ignore_signal() {
signal(SIGINT, SIG_IGN);
}进程可以捕获信号,并执行自定义的信号处理函数。可以使用 signal 或 sigaction 函数来设置信号处理函数。
①使用 signal 函数
signal():简单注册信号处理函数(不推荐,兼容性差)。
signal 函数的原型如下:
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);signum:要处理的信号编号。handler:信号处理函数的指针。可以是自定义的信号处理函数,也可以是 SIG_IGN(忽略信号)或 SIG_DFL(执行默认处理动作)。SIG_ERR。
以下是一个使用 signal 函数捕获 SIGINT 信号的示例:
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void sigint_handler(int signum) {
printf("Received SIGINT signal. Exiting...\n");
// 可以在这里进行一些清理工作
_exit(0);
}
int main() {
// 设置信号处理函数
signal(SIGINT, sigint_handler);
while (1) {
printf("Running...\n");
sleep(1);
}
return 0;
} ②使用 sigaction 函数
sigaction 函数比 signal 函数更强大和灵活,它可以更精确地控制信号的处理。其原型如下:
#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);signum:要处理的信号编号。act:指向 struct sigaction 结构体的指针,用于设置新的信号处理动作。oldact:指向 struct sigaction 结构体的指针,用于保存旧的信号处理动作。struct sigaction 结构体的定义如下:
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
}; 以下是一个使用 sigaction 函数捕获 SIGINT 信号的示例:
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void sigint_handler(int signum) {
printf("Received SIGINT signal. Exiting...\n");
// 可以在这里进行一些清理工作
_exit(0);
}
int main() {
struct sigaction act;
act.sa_handler = sigint_handler;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
// 设置信号处理动作
if (sigaction(SIGINT, &act, NULL) == -1) {
perror("sigaction");
return 1;
}
while (1) {
printf("Running...\n");
sleep(1);
}
return 0;
}进程可以选择阻塞某些信号,即暂时不处理这些信号。被阻塞的信号不会立即被处理,而是处于未决状态,直到进程解除对该信号的阻塞。可以使用 sigprocmask 函数来设置信号掩码,从而实现信号的阻塞和解除阻塞。
sigprocmask 函数的原型如下:
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);how:指定如何修改信号掩码。可以取值为 SIG_BLOCK(将 set 中的信号添加到当前信号掩码中)、SIG_UNBLOCK(从当前信号掩码中移除 set 中的信号)或 SIG_SETMASK(将当前信号掩码设置为 set)。set:指向 sigset_t 类型的信号集的指针,用于指定要操作的信号。oldset:指向 sigset_t 类型的信号集的指针,用于保存旧的信号掩码。未决信号是指已经发送给进程,但由于进程对该信号进行了阻塞,而暂时未被处理的信号。可以使用 sigpending 函数来获取当前进程的未决信号集。
#include <signal.h>
int sigpending(sigset_t *set);set 指向 sigset_t 类型的信号集的指针,用于保存当前进程的未决信号集。errno 变量。malloc、printf等非异步安全函数)。
volatile变量:确保信号处理函数与主程序共享变量时可见性。
sa_mask防止处理函数被嵌套调用。
printf、malloc等。
SIGRTMIN到SIGRTMAX的信号支持排队(通过sigqueue发送)。
SIGCHLD)。
SIGALRM实现任务超时机制。
信号是嵌入式 Linux 系统中一种重要的进程间通信机制,它提供了一种异步的事件通知方式。通过合理地使用信号的发送、处理、阻塞和未决等机制,可以实现进程间的高效通信和异常处理。在实际开发中,需要注意信号处理函数的编写规范,避免在信号处理函数中调用不可重入函数,以确保系统的稳定性和可靠性。同时,要根据具体的应用场景选择合适的信号和处理方式,以满足系统的需求。
man 命令查看信号相关的手册页,如 man 7 signal 可以获取 Linux 系统中信号的详细信息,包括信号编号、名称、默认动作等。这些手册页是最权威和准确的参考资料,反映了当前系统所支持的信号机制。signal、sigaction、kill 等)进行了详细的说明,包括函数的原型、参数解释、返回值以及使用示例等。文档地址为:Top (The GNU C Library)。include/linux/signal.h、kernel/signal.c 等,深入研究信号在内核中的具体实现细节。