首页 文章 精选 留言 我的

精选列表

搜索[嵌入式],共4462篇文章
优秀的个人博客,低调大师

嵌入式Linux:信号分类

在Linux系统中,信号可以从两个不同的角度进行分类:一是从可靠性方面,将信号分为**可靠信号**与**不可靠信号**;二是从实时性方面,将信号分为**实时信号**与**非实时信号**。 在 Linux 系统下使用"kill -l"命令可查看到所有信号,如下所示: ![](https://developer.qcloudimg.com/http-save/yehe-8201782/f909c4ef372d9c4231b3821162d34d61.png) 图中,信号编号在**1到31之间**的信号被定义为**不可靠信号(非实时信号)**。这些信号主要继承自早期的UNIX系统,使用了最初设计的简单信号机制。在这种机制中,如果一个信号在进程处理另一个相同信号时到达,该信号可能会被丢失,从而导致进程错过某些事件。信号编号在**34到64之间**的信号被定义为**可靠信号(实时信号)**。与不可靠信号不同,可靠信号支持排队处理,即使多个相同信号在处理过程中到达,它们也不会被丢失,而是按照到达顺序依次处理。 值得注意的是,**可靠信号(实时信号)**并没有像不可靠信号那样的具体名称,而是采用了相对编号的方式来表示。这些信号使用`SIGRTMIN+N`或`SIGRTMAX-N`的形式进行表示,其中: - `SIGRTMIN`是**可靠信号(实时信号)**的最小编号,通常为34。 - `SIGRTMAX`是**可靠信号(实时信号)**的最大编号,通常为64。 因此,`SIGRTMIN+1`代表编号为35的**可靠信号(实时信号)**,`SIGRTMAX-1`代表编号为63的**可靠信号(实时信号)**。 ## 1、不可靠信号与可靠信号 ### 1.1、不可靠信号 早期UNIX系统中的信号机制被称为不可靠信号,Linux的信号机制基本上继承自早期UNIX系统。所以信号机制设计相对简单,虽然实现了基本的进程间通信功能,但在实际应用中暴露出一些显著问题。 其主要问题包括: - **信号处理后恢复默认行为**: 在早期的UNIX中,进程每次处理完信号后,系统会自动将该信号的处理方式恢复为默认操作。这意味着,如果用户希望持续使用自定义的信号处理函数,就需要在信号处理函数的末尾再次调用`signal()`函数重新绑定处理函数。这种机制增加了编程的复杂性,并且容易导致程序员在忽略这个步骤时发生错误。 - **信号可能丢失**: 另一个更严重的问题是不可靠信号可能会丢失。当进程正在处理一个信号时,如果相同类型的另一个信号到达,第二个信号可能会被直接丢弃,导致进程错过了重要的事件。这在关键任务应用中尤其危险。 虽然Linux仍然支持不可靠信号机制,但对其进行了改进。Linux修复了在信号处理函数执行后必须手动重新绑定处理函数的问题。在现代Linux系统中,一旦信号处理函数被绑定,除非显式更改,否则它将一直保持有效。 然而,信号丢失问题仍然存在。这意味着,在处理不可靠信号时,如果在信号处理期间有相同信号再次到达,该信号可能无法被捕获。 ### 1.2、可靠信号 为了克服不可靠信号的缺陷,Linux引入了**可靠信号**机制。可靠信号支持排队,即使进程在处理某个信号时有新的信号到达,这些信号也不会丢失,而是被加入队列,待当前信号处理完成后再依次处理。 Linux还引入了新的信号发送函数`sigqueue()`和信号绑定函数`sigaction()`,进一步增强了信号处理的灵活性和可靠性。`sigqueue()`不仅可以发送信号,还可以附带一个整数值或指针,传递额外的信息。`sigaction()`则允许更精细地控制信号的行为,替代了传统的`signal()`函数。 ## 2、实时信号和非实时信号 Linux信号的另一种分类方式是根据实时性来区分为**实时信号**与**非实时信号**。 ### **2.1、非实时信号** 非实时信号指的是传统的、不支持排队的信号。这类信号在早期的UNIX系统中得以引入,信号编号一般在1到31之间。这些信号在处理时没有严格的顺序保证,并且如果在处理某个信号时有相同类型的新信号到达,后者可能会被忽略或丢失。因此,这类信号被称为**不可靠信号**。 ### **2.2、实时信号** 实时信号是为了解决非实时信号在处理可靠性方面的不足而引入的。实时信号的一个显著特点是它们**支持排队**,即使在处理某个信号期间有新的相同类型的信号到达,这些信号也不会被丢弃,而是按照到达的顺序依次处理。这样,实时信号**保证了多个信号都能被正确接收和处理**,因此它们被称为**可靠信号**。 在实际编程中,开发者应当根据应用需求选择合适的信号类型。对于简单的进程间通信或用户交互,标准的非实时信号可能已经足够。而对于需要保证信号处理顺序且不能丢失的重要任务,使用可靠信号或实时信号是更好的选择。 作者:不脱发的程序猿。来源:CSDN 原文(CC BY-SA 4.0)。

优秀的个人博客,低调大师

嵌入式Linux:信号集

在 Linux 系统中,处理多个信号时常用到一种数据结构:信号集(`sigset_t`)。信号集允许我们将多个信号组织在一起,以便在系统调用中传递和操作。例如,`sigaction()`、`sigprocmask()` 和 `sigpending()` 等系统调用都使用信号集作为参数。 信号集的数据结构定义如下: ``` #define _SIGSET_NWORDS (1024 / (8 * sizeof(unsigned long int))) typedef struct { unsigned long int __val[_SIGSET_NWORDS]; } sigset_t; ``` 这个结构体可以表示一组信号。`_SIGSET_NWORDS` 计算了信号集数组所需的长整型数目,以便能够容纳所有可能的信号。 ## 1、信号集初始化 `sigemptyset()` 用于初始化信号集,使其不包含任何信号。函数原型如下: ``` #include int sigemptyset(sigset_t *set); ``` **参数**: - `set`: 指向需要初始化的信号集变量的指针。 **返回值**: - 成功时返回 0。 - 失败时返回 -1,并设置 `errno`。 使用示例如下: ``` #include int main() { sigset_t sig_set; if (sigemptyset(&sig_set) == -1) { perror("sigemptyset"); return 1; } // 现在 sig_set 不包含任何信号 return 0; } ``` `sigfillset()` 用于初始化信号集,使其包含所有信号,包括所有实时信号。函数原型如下: ``` #include int sigfillset(sigset_t *set); ``` **参数**: - `set`: 指向需要初始化的信号集变量的指针。 **返回值**: - 成功时返回 0。 - 失败时返回 -1,并设置 `errno`。 使用示例如下: ``` #include int main() { sigset_t sig_set; if (sigfillset(&sig_set) == -1) { perror("sigfillset"); return 1; } // 现在 sig_set 包含所有信号 return 0; } ``` ## 2、向信号集中添加或删除信号 `sigaddset()` 用于将一个信号添加到信号集中。函数原型如下: ``` #include int sigaddset(sigset_t *set, int signum); ``` **参数**: - `set`: 指向信号集的指针。 - `signum`: 要添加的信号。 **返回值**: - 成功时返回 0。 - 失败时返回 -1,并设置 `errno`。 使用示例如下: ``` #include int main() { sigset_t sig_set; if (sigemptyset(&sig_set) == -1) { perror("sigemptyset"); return 1; } if (sigaddset(&sig_set, SIGINT) == -1) { perror("sigaddset"); return 1; } // 现在 sig_set 包含 SIGINT 信号 return 0; } ``` `sigdelset()` 用于将一个信号从信号集中删除。函数原型如下: ``` #include int sigdelset(sigset_t *set, int signum); ``` **参数**: - `set`: 指向信号集的指针。 - `signum`: 要删除的信号。 **返回值**: - 成功时返回 0。 - 失败时返回 -1,并设置 `errno`。 使用示例如下: ``` #include int main() { sigset_t sig_set; if (sigfillset(&sig_set) == -1) { perror("sigfillset"); return 1; } if (sigdelset(&sig_set, SIGINT) == -1) { perror("sigdelset"); return 1; } // 现在 sig_set 不包含 SIGINT 信号 return 0; } ``` ## 3、测试信号是否在信号集中 `sigismember()` 用于测试某个信号是否在指定的信号集中。函数原型如下: ``` #include int sigismember(const sigset_t *set, int signum); ``` **参数**: - `set`: 指向信号集的指针。 - `signum`: 要测试的信号。 **返回值**: - 如果信号 `signum` 在信号集 `set` 中,则返回 1。 - 如果信号 `signum` 不在信号集 `set` 中,则返回 0。 - 失败时返回 -1,并设置 `errno`。 以下示例中,我们首先初始化了一个空的信号集,然后添加了 `SIGINT` 信号,并使用 `sigismember()` 函数检查信号集是否包含 `SIGINT` 信号。 ``` #include #include int main() { sigset_t sig_set; if (sigemptyset(&sig_set) == -1) { perror("sigemptyset"); return 1; } if (sigaddset(&sig_set, SIGINT) == -1) { perror("sigaddset"); return 1; } if (sigismember(&sig_set, SIGINT)) { puts("信号集中包含 SIGINT 信号"); } else { puts("信号集中不包含 SIGINT 信号"); } return 0; } ``` 作者:不脱发的程序猿。来源:[CSDN 原文](https://blog.csdn.net/m0_38106923/article/details/141305104)(CC BY-SA 4.0)。

优秀的个人博客,低调大师

嵌入式Linux:信号掩码

在 Linux 系统中,内核为每一个进程维护了一个信号掩码(实际上是一个信号集),即一组信号的集合。当进程接收到一个在其信号掩码中定义的信号时,该信号将被阻塞,而不会立即传递给进程进行处理。这个阻塞行为意味着信号不会被丢弃,而是会被保留,直到该信号从信号掩码中移除,内核才会将其传递给进程进行处理。 向信号掩码中添加信号的三种方式: - 通过 signal() 或 sigaction() 函数: 当应用程序调用 signal() 或 sigaction() 函数为某一信号设置处理方式时,该信号通常会自动添加到进程的信号掩码中。这样做的目的是确保在处理某个信号时,如果该信号再次发生,它将被阻塞,以避免信号处理函数的重入问题。 - 对于 sigaction() 函数来说,是否自动将信号添加到信号掩码中,还取决于是否设置了 SA_NODEFER 标志。如果设置了 SA_NODEFER,信号将不会被自动阻塞。 - 当信号处理函数执行完毕并返回后,该信号将自动从信号掩码中移除,允许其再次传递。 - 通过 sigaction() 的 sa_mask 参数: 使用 sigaction() 为信号设置处理方式时,还可以通过 sa_mask 参数指定一组额外的信号。这些信号将在调用信号处理函数时被自动添加到信号掩码中,并在处理函数结束后移除。这种方式允许在处理某一信号时,临时阻塞其他相关的信号,以避免干扰。 - 通过 sigprocmask() 系统调用: 除了上述两种方法,Linux 系统还提供了 sigprocmask() 系统调用,允许程序员在任何时候显式地向信号掩码中添加或移除信号。这种方法非常灵活,适用于需要精细控制信号屏蔽行为的场景。 本篇文章主要介绍sigprocmask() 函数向信号掩码中添加信号的方式。 sigprocmask() 的函数原型如下: ```c #include int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); ``` - how: 这个参数指定了对信号掩码的操作行为,有以下三种可能的值: - SIG_BLOCK:将 set 所指向的信号集中的所有信号添加到当前的信号掩码中(即将信号掩码设置为当前掩码与 set 的并集)。 - SIG_UNBLOCK:从当前信号掩码中移除 set 所指向的信号集中的所有信号。 - SIG_SETMASK:将当前的信号掩码直接设置为 set 所指向的信号集。 - set: 指向一个信号集,表示需要添加到(或移除自)信号掩码中的信号。如果 set 为 NULL,则不改变当前的信号掩码。 - oldset: 如果 oldset 不为 NULL,则在修改信号掩码之前,会将当前的信号掩码保存到 oldset 指向的信号集中。这对于需要临时修改信号掩码并在之后恢复原来状态的操作非常有用。 - 返回值: 如果调用成功,函数返回 0;如果失败,则返回 -1 并设置 errno 来指示错误原因。 以下代码展示了如何使用 sigprocmask() 将信号 SIGINT 添加到进程的信号掩码中,并在之后将其移除。 ```c #include #include #include int main() { int ret; sigset_t sig_set; // 初始化信号集为空 sigemptyset(&sig_set); // 向信号集中添加 SIGINT 信号 sigaddset(&sig_set, SIGINT); // 将 SIGINT 添加到进程的信号掩码中(阻塞 SIGINT) ret = sigprocmask(SIG_BLOCK, &sig_set, NULL); if (ret == -1) { perror("sigprocmask error"); exit(EXIT_FAILURE); } printf("SIGINT blocked\n"); // 从信号掩码中移除 SIGINT 信号(解除阻塞) ret = sigprocmask(SIG_UNBLOCK, &sig_set, NULL); if (ret == -1) { perror("sigprocmask error"); exit(EXIT_FAILURE); } printf("SIGINT unblocked\n"); return 0; } ``` 信号掩码是 Linux 进程信号处理机制中的一个关键概念。通过 signal()、sigaction() 和 sigprocmask() 等函数,程序可以精确控制哪些信号应该被阻塞、哪些信号应该被传递。理解和灵活运用这些函数,可以帮助开发人员编写更加健壮的信号处理代码,避免信号干扰导致的潜在问题。 作者:不脱发的程序猿。本文转载自作者CSDN博客。原文:https://handsome-man.blog.csdn.net/article/details/141755555

优秀的个人博客,低调大师

嵌入式Linux:获取进程时间

进程时间是指进程从创建到当前时刻所使用的CPU资源的总时间。为了记录和分析,内核将CPU时间(进程时间)分为以下两个部分: - **用户CPU时间**:进程在用户空间(用户态)运行时所花费的CPU时间。有时也称为虚拟时间(virtual time)。 - **系统CPU时间**:进程在内核空间(内核态)运行时所花费的CPU时间。这是内核执行系统调用或代表进程执行其他任务(例如,处理页错误)所花费的时间。 通常,进程时间是指用户CPU时间和系统CPU时间的总和,即进程使用的总CPU时间。 **提示**:进程时间不等于程序的整个生命周期所消耗的时间。如果进程处于休眠状态(进程被挂起,不会得到系统调度),它不会使用CPU资源,因此休眠时间不计入进程时间。 在Linux系统中,获取进程时间的两种常用方法是使用`times`函数和`clock`函数。这些函数允许程序获取进程的CPU时间,以进行性能分析和优化。 **`times**`** 函数**: - 用于获取进程及其子进程的用户和系统CPU时间。 - 返回从系统启动到调用时的时钟滴答数。 - `struct tms` 结构用于存储具体时间信息。 **`clock**`** 函数**: - 用于获取程序的用户和系统CPU时间。 - 返回从程序启动到调用时的时钟滴答数。 - 通过将返回值除以`CLOCKS_PER_SEC`可以得到秒数。 ## 1、`times`函数 `times`函数用于获取当前进程及其子进程的CPU时间。 函数原型如下: ``` #include clock_t times(struct tms *buf); ``` **参数**: - `buf`:指向`struct tms`结构的指针,用于存储进程时间信息。 **返回值**: - 返回从系统启动到调用`times`函数时的时钟滴答数(clock ticks)。 - 失败时返回`(clock_t)-1`,并设置`errno`来指示错误。 **`struct tms**`** 结构**: ``` struct tms { clock_t tms_utime; /* 用户CPU时间 */ clock_t tms_stime; /* 系统CPU时间 */ clock_t tms_cutime; /* 已终止的子进程的用户CPU时间 */ clock_t tms_cstime; /* 已终止的子进程的系统CPU时间 */ }; ``` 示例如下: ``` #include #include #include int main() { struct tms t; clock_t start, end; start = times(&t); if (start == (clock_t)-1) { perror("times"); return 1; } // 模拟一些工作负载 for (volatile int i = 0; i clock_t clock(void); ``` **返回值**: - 返回程序使用的用户和系统CPU时间的时钟滴答数(clock ticks)。 - 失败时返回`(clock_t)-1`,并设置`errno`来指示错误。 示例如下: ``` #include #include int main() { clock_t start, end; double cpu_time_used; start = clock(); if (start == (clock_t)-1) { perror("clock"); return 1; } // 模拟一些工作负载 for (volatile int i = 0; i < 100000000; i++); end = clock(); if (end == (clock_t)-1) { perror("clock"); return 1; } cpu_time_used = ((double) (end - start)) / CLOCKS_PER_SEC; printf("CPU time used: %f seconds\n", cpu_time_used); return 0; } ``` 这两个函数在进行程序性能分析和调试时非常有用,可以帮助开发者了解程序的CPU时间消耗情况。 作者:不脱发的程序猿。来源:[CSDN 原文](https://handsome-man.blog.csdn.net/article/details/140364545)(CC BY-SA 4.0)。

优秀的个人博客,低调大师

嵌入式Linux:设置进程休眠

在Linux中,进程休眠(sleep)是指进程暂停执行一段时间。常用的休眠函数包括`sleep`、`usleep`和`nanosleep`。它们的主要区别在于指定休眠时间的精度。 ## 1、sleep函数 `sleep`函数使进程休眠指定的秒数。其原型为: ``` unsigned int sleep(unsigned int seconds); ``` - **参数**:`seconds`是进程休眠的时间,以秒为单位。 - **返回值**:实际休眠的秒数(如果信号中断了休眠,则返回剩余的秒数)。 示例如下: ``` #include #include int main() { printf("Sleeping for 5 seconds...\n"); sleep(5); printf("Awake!\n"); return 0; } ``` ## 2、usleep函数 `usleep`函数使进程休眠指定的微秒数。其原型为: ``` int usleep(useconds_t usec); ``` - **参数**:`usec`是进程休眠的时间,以微秒为单位(1秒 = 1,000,000微秒)。 - **返回值**:0表示成功,-1表示失败。 示例如下: ``` #include #include int main() { printf("Sleeping for 500,000 microseconds (0.5 seconds)...\n"); usleep(500000); printf("Awake!\n"); return 0; } ``` ## 3、nanosleep函数 `nanosleep`函数使进程休眠指定的纳秒数。其原型为: ``` int nanosleep(const struct timespec *req, struct timespec *rem); ``` **参数**: - `req`:指向一个`timespec`结构体,指定休眠时间。 - `rem`:指向一个`timespec`结构体,如果休眠被信号中断,则此结构体保存剩余的时间。 **返回值**:0表示成功,-1表示失败,并设置errno。 `timespec`结构体定义如下: ``` struct timespec { time_t tv_sec; /* seconds */ long tv_nsec; /* nanoseconds */ }; ``` 示例如下: ``` #include #include int main() { struct timespec req, rem; req.tv_sec = 0; req.tv_nsec = 500000000L; // 500 milliseconds printf("Sleeping for 500,000,000 nanoseconds (0.5 seconds)...\n"); if (nanosleep(&req, &rem) == -1) { printf("Nanosleep was interrupted\n"); } printf("Awake!\n"); return 0; } ``` ## 4、注意事项 - **精度**:`sleep`的精度最低,以秒为单位;`usleep`以微秒为单位,精度稍高;`nanosleep`以纳秒为单位,精度最高。 - **中断**:如果休眠期间进程接收到信号,休眠可能会被中断,可以通过返回值判断是否被中断,并在需要时重新调用休眠函数。 这三个函数提供了不同精度和功能的休眠方式,根据实际需求选择合适的函数。 作者:不脱发的程序猿。来源:[CSDN 原文](https://handsome-man.blog.csdn.net/article/details/140388676)(CC BY-SA 4.0)。

优秀的个人博客,低调大师

嵌入式Linux:信号是什么?

信号是Linux系统中用于通知进程事件发生的一种机制,可以将其视为一种软件中断。信号类似于硬件中断,能够打断进程当前的执行流程,从而实现对中断机制的一种软件层面的模拟。信号的主要作用是处理异步事件,因为大多数情况下,信号的到达时间是不可预测的。 信号的一个主要目的是用于进程间的通信。具有合适权限的进程可以向另一个进程发送信号,这种用法不仅可以用作一种同步技术,还可以视为进程间通信(IPC)的最基础形式。 ## 1、信号的来源 信号可以由多种情况触发,以下是常见的几种信号来源: **硬件异常** 硬件检测到错误条件并通知内核,内核随即发送相应的信号给相关进程。例如,当执行除数为零、访问越界的内存等异常操作时,硬件会捕捉到这些错误并通知内核,内核则向相关进程发送信号,如`SIGFPE`(浮点异常)或`SIGSEGV`(非法内存访问)。 **终端输入特殊字符** 用户通过终端输入特定的控制字符可以产生信号。例如,按下`CTRL + C`组合键会产生`SIGINT`(中断信号),可以终止前台运行的进程;按下`CTRL + Z`会产生`SIGTSTP`(暂停信号),可暂停当前前台运行的进程。 **进程调用系统调用** 进程可以通过`kill()`系统调用向另一个进程或进程组发送信号。为了确保系统安全,发送信号的进程和接收信号的进程通常需要具有相同的所有者,或者发送信号的进程的所有者是`root`超级用户。 **用户命令** 用户可以通过`kill`命令向其它进程发送信号。虽然`kill`命令的名称听起来像是用来“杀死”进程,但实际上它可以发送任意信号。例如,`kill -9 PID`会向进程发送`SIGKILL`信号,强制终止指定进程。 **软件事件** 软件检测到特定条件发生时也会产生信号。这些条件可能包括:进程设置的定时器到期、进程使用的CPU时间超限、子进程退出等。这些信号通常由内核触发并发送给相关进程。 ## 2、信号的处理方式 当信号到达进程时,进程需要对该信号进行处理。通常,进程对信号的处理方式有以下几种: **忽略信号** 进程可以选择忽略某些信号,使其不对进程的执行产生影响。然而,有两种信号`SIGKILL`和`SIGSTOP`是无法被忽略的,因为它们提供了终止或停止进程的可靠方法。如果进程忽略某些由硬件异常产生的信号,其行为可能是未定义的。 **捕获信号** 进程可以捕获并处理信号,通过预先定义的信号处理函数来响应特定的信号。为了实现这一点,进程需要通过`signal()`或`sigaction()`系统调用来注册信号处理函数,当信号发生时,该函数将被执行以处理相应的事件。 **执行系统默认操作** 如果进程没有捕获信号,系统会对信号进行默认处理。对于大多数信号,系统默认的处理方式是终止进程。然而,也有些信号的默认处理方式是忽略。 ## 3、信号的异步性 信号是异步事件的经典实例。信号的产生对进程而言是随机的,进程无法预测信号到达的具体时间。这种异步性与硬件中断非常相似。进程无法通过简单的变量测试或系统调用判断信号是否产生,只有当信号实际发生时,系统才会通知进程,打断当前执行流程,跳转到信号处理函数去执行相应操作。 ## 4、信号编号 在Linux系统中,信号本质上是`int`类型的数字编号,类似于硬件中断所对应的中断号。内核为每一个信号定义了一个唯一的整数编号,这些编号从数字1开始依次展开。每个信号都有一个对应的名字,这个名字实际上是一个宏,通常以`SIGxxx`的形式出现,例如`SIGINT`、`SIGKILL`等。 信号的整数编号与其符号名之间是一一对应的关系,但由于不同操作系统的实现可能存在差异,某些信号的实际编号在不同系统中可能会有所不同。为了提高程序的可移植性,在编写代码时,开发者通常使用信号的符号名而不是直接使用编号。例如,在程序中使用`SIGINT`来表示中断信号,而不是直接使用数字2(在大多数系统中,`SIGINT`的编号为2)。 信号的定义可以在``或``头文件中找到,这些文件中定义了所有标准信号的编号和名称。 需要注意,信号编号从1开始,而编号为0的信号在标准定义中并不存在。 ``` #define SIGHUP 1 /* 挂断 (POSIX). */ #define SIGINT 2 /* 中断 (ANSI). */ #define SIGQUIT 3 /* 退出 (POSIX). */ #define SIGILL 4 /* 非法指令 (ANSI). */ #define SIGTRAP 5 /* 跟踪陷阱 (POSIX). */ #define SIGABRT 6 /* 异常终止 (ANSI). */ #define SIGIOT 6 /* IOT 陷阱 (4.2 BSD). */ #define SIGBUS 7 /* 总线错误 (4.2 BSD). */ #define SIGFPE 8 /* 浮点异常 (ANSI). */ #define SIGKILL 9 /* 终止,无法阻塞 (POSIX). */ #define SIGUSR1 10 /* 用户自定义信号 1 (POSIX). */ #define SIGSEGV 11 /* 段错误 (ANSI). */ #define SIGUSR2 12 /* 用户自定义信号 2 (POSIX). */ #define SIGPIPE 13 /* 管道破裂 (POSIX). */ #define SIGALRM 14 /* 闹钟信号 (POSIX). */ #define SIGTERM 15 /* 终止 (ANSI). */ #define SIGSTKFLT 16 /* 栈错误. */ #define SIGCHLD 17 /* 子进程状态改变 (POSIX). */ #define SIGCLD SIGCHLD /* 与 SIGCHLD 相同 (System V). */ #define SIGCONT 18 /* 继续执行 (POSIX). */ #define SIGSTOP 19 /* 停止,无法阻塞 (POSIX). */ #define SIGTSTP 20 /* 终端停止信号 (POSIX). */ #define SIGTTIN 21 /* 后台从终端读取 (POSIX). */ #define SIGTTOU 22 /* 后台向终端写入 (POSIX). */ #define SIGURG 23 /* 套接字紧急情况 (4.2 BSD). */ #define SIGXCPU 24 /* 超过 CPU 时间限制 (4.2 BSD). */ #define SIGXFSZ 25 /* 超过文件大小限制 (4.2 BSD). */ #define SIGVTALRM 26 /* 虚拟时钟信号 (4.2 BSD). */ #define SIGPROF 27 /* 程序执行时钟信号 (4.2 BSD). */ #define SIGWINCH 28 /* 窗口大小改变 (4.3 BSD, Sun). */ #define SIGPOLL SIGIO /* 可轮询事件发生 (System V). */ #define SIGIO 29 /* I/O 操作完成 (4.2 BSD). */ #define SIGPWR 30 /* 电源故障重启 (System V). */ #define SIGSYS 31 /* 错误的系统调用. */ #define SIGUNUSED 31 /* 未使用的信号. */ ``` 在 Linux 系统下使用"kill -l"命令可查看到所有信号,如下所示: ![](https://developer.qcloudimg.com/http-save/yehe-8201782/c8aff4ae3a21bf0858bd49e3dacdbd99.png) 在实际开发中,合理使用信号处理机制可以提高程序的健壮性和响应速度。开发者需要根据应用场景选择合适的信号处理方式,比如在关键任务中确保某些信号能够及时处理,或者在某些情况下忽略不重要的信号以避免不必要的中断。 作者:不脱发的程序猿。来源:[CSDN 原文](https://blog.csdn.net/m0_38106923/article/details/141096751)(CC BY-SA 4.0)。

优秀的个人博客,低调大师

嵌入式Linux:发送实时信号

非实时信号有一个明显的局限性:当同一个信号多次发生时,它只会被记录为一次,且不会记录发生的次数。因此,当该信号被解除阻塞后,它仅会被处理一次。这种行为使得标准信号在某些应用场景下不够灵活。 相比之下,实时信号 提供了几个关键的优势: - 信号范围扩大:标准信号仅提供 SIGUSR1 和 SIGUSR2 供用户自定义使用,而实时信号的编号范围更大(SIGRTMIN 到 SIGRTMAX,对应编号范围 34~64),可以应用于更多自定义目的。 - 队列化管理:实时信号采取队列化管理,这意味着如果同一个实时信号多次发生,内核将记录每次事件并按顺序传递,而标准信号只会传递一次。 - 附带数据:实时信号允许携带附带数据(可以是整型数据或指针),供接收方在信号处理函数中使用,这为信号传递带来了更大的灵活性。 - 传递顺序保证:当多个不同的实时信号处于等待状态时,信号编号越小的信号会优先传递。如果同一个信号多次发生,传递顺序会与发送顺序保持一致。 为了使用实时信号,通常需要满足以下要求: - 发送实时信号:发送进程需要使用 sigqueue() 系统调用发送实时信号及其伴随数据。 - 接收实时信号:接收进程需要为该信号设置信号处理函数,并在 sigaction 函数中启用 SA_SIGINFO 标志,以确保可以接收伴随数据。 sigqueue() 的函数原型如下: ```c #include int sigqueue(pid_t pid, int sig, const union sigval value); ``` - 参数: - pid:接收信号的进程 ID。 - sig:需要发送的信号编号。与 kill() 类似,也可以将 sig 设置为 0 来检查进程是否存在。 - value:一个 union sigval 类型的值,表示伴随信号传递的数据,可以是整型或指针。 - 返回值: - 成功返回 0; - 失败返回 -1,并设置 errno。 union sigval 是一个共用体,定义如下: ```c typedef union sigval { int sival_int; // 整型数据 void *sival_ptr; // 指针数据 } sigval_t; ``` ## 1、发送进程 使用 sigqueue() 向另一个进程发送实时信号及其伴随数据。 ```c #include #include #include int main(int argc, char *argv[]) { union sigval sig_val; int pid, sig; // 检查参数个数 if (argc #include #include #include static void sig_handler(int sig, siginfo_t *info, void *context) { sigval_t sig_val = info->si_value; printf("Received real-time signal: %d\n", sig); printf("Attached data: %d\n", sig_val.sival_int); } int main(int argc, char *argv[]) { struct sigaction sa = {0}; int sig; // 检查参数个数 if (argc < 2) { exit(-1); } // 获取命令行传递的信号编号 sig = atoi(argv[1]); // 绑定信号处理函数 sa.sa_sigaction = sig_handler; sa.sa_flags = SA_SIGINFO; // 启用 SA_SIGINFO 标志,以接收附带数据 if (sigaction(sig, &sa, NULL) == -1) { perror("sigaction error"); exit(-1); } // 无限循环,等待信号 while (1) { sleep(1); } return 0; } ``` 标准信号和实时信号在 Linux 信号处理机制中各有优劣。标准信号适用于大多数常见场景,但其无法记录信号的多次发生,且缺少附带数据传递的能力。而实时信号则提供了更灵活的功能,包括多次传递信号、附带数据和保证传递顺序。这些特性使得实时信号在高性能和复杂信号处理需求下尤为有用。 作者:不脱发的程序猿。本文转载自作者CSDN博客。原文:https://handsome-man.blog.csdn.net/article/details/141756910

优秀的个人博客,低调大师

嵌入式流程解决方案

一.需求分析由于企业业务的独特性或者企业高层独特的管理思想,很多客户选择了自行开发业务系统的方式来实现独有的竞争力。这类信息系统通常经过了多年的开发,伴随着企业的发展一直在不断优化,与企业的业务非常匹配。然而,近几年流程管理思想和技术的不断兴起,这类系统由于规划时间早,对流程的支持非常弱,因此,很多客户期望通过集成第三方的流程管理产品,在业务系统尽量少调整的前提下,嵌入工作流,实现业务系统的工作流驱动。二.方案实现 以H3 BPM流程引擎为基础,企业自主开发的业务系统通过H3 BPM开放的Webservice接口、 API接口、功能控件等,将流程引擎集成到业务系统,实现业务系统的工作流驱动。 H3 BPM采用微软.net技术架构,如果客户业务系统同样采用.net,可在项目中直接引用H3 BPM的 程序集和控件集,把H3 BPM流 程引擎作为基础构建来使用,如下图: 这样,可以充分的运用H3 BPM进百个控件和所有的API函数,H3 BPM有近 600页的API库,可以完成几乎所有的流程操作,如下图: 旧系统如果不是.net系统,可以采用WebService的方式,H3 BPM给常用 接口封装了WebService接口,包括流程发起、任务提交、任务打回、任务转交、撤销流程等等。如下图: 三.方案价值 本文转自 lwl_BPM 51CTO博客,原文链接:http://blog.51cto.com/12438115/1913512,如需转载请自行联系原作者

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册