0


织梦云端:网络信号原理的艺术解码

hello !大家好呀! 欢迎大家来到我的Linux高性能服务器编程系列之《织梦云端:网络信号原理的艺术解码》,在这篇文章中,你将会学习到网络信号原理以及应用,并且我会给出源码进行剖析,以及手绘UML图来帮助大家来理解,希望能让大家更能了解网络编程技术!!!

希望这篇文章能对你有所帮助9fe07955741149f3aabeb4f503cab15a.png,大家要是觉得我写的不错的话,那就点点免费的小爱心吧!1a2b6b564fe64bee9090c1ca15a449e3.png(注:这章对于高性能服务器的架构非常重要哟!!!)

03d6d5d7168e4ccb946ff0532d6eb8b9.gif

一. 什么是信号?

在Linux网络编程中,“信号”(Signal)是操作系统用来通知进程某个事件已经发生的一种机制。信号是一种软件中断,它可以在任何时候发送给一个进程,使得该进程可以中断当前的执行,处理该信号,然后再回到原来的执行状态。

Linux系统中定义了许多信号,每个信号都有其特定的用途。例如:

  • SIGINT:通常在用户按下Ctrl+C时发出,用于中断程序的执行。
  • SIGCHLD:当一个子进程终止时,父进程会收到这个信号。
  • SIGALRM:定时器超时时发送。
  • SIGPIPE:在尝试写入一个没有读取器的管道或socket时发送。

在网络编程中,信号经常用于处理各种异步事件,比如:

  1. 处理子进程状态变化:当一个子进程改变其状态(比如终止或暂停)时,父进程会接收到SIGCHLD信号。
  2. 处理网络IO:在一些网络编程模型中,如使用select或poll,当IO准备好时,可以通过信号来通知应用程序。
  3. 定时器:使用setitimeralarm函数设置的定时器超时时,可以通过SIGALRM信号通知进程。

处理信号的方法主要有两种:

  • 信号处理函数:可以为特定的信号设置一个处理函数(handler),当该信号发生时,系统将调用这个函数。
  • 信号掩码:可以阻塞某些信号,使得它们在处理期间不被传递给进程。

信号是Linux系统编程中的一个重要部分,特别是在网络编程领域,因为它们提供了一种机制来响应异步事件,这对于构建高效和响应迅速的网络应用程序至关重要。

二. 信号的发送以及处理方式

在Linux中,信号的发送和处理可以通过以下函数来完成:

  1. signal():用于设置一个信号的处理函数。
  2. raise():用于向当前进程发送一个信号。
  3. kill():用于向指定进程发送一个信号。
  4. sigaction():提供了一种更为复杂的方式来设置信号的处理函数,包括信号掩码。
  5. sigprocmask():用于设置信号掩码,从而阻塞或解阻塞特定的信号。
  6. sigpending():用于检查被阻塞的信号。
  7. sigsuspend():用于在阻塞信号集的基础上临时替换信号掩码,并暂停进程直到捕获到一个信号。
  1. signal()- 功能signal函数用于设置一个信号的处理函数。当指定信号被传递给进程时,系统将调用这个处理函数。- 原型void (*signal(int sig, void (*func)(int)))(int);- 参数: - sig:要处理的信号编号。- func:信号处理函数,可以是SIG_IGN(忽略信号)、SIG_DFL(默认处理)或者一个自定义的函数指针。- 返回值:返回先前的信号处理函数的指针。
  2. raise()- 功能raise函数用于向当前进程发送一个信号。- 原型int raise(int sig);- 参数sig是要发送的信号的编号。- 返回值:成功时返回0,失败时返回非0值。
  3. kill()- 功能kill函数用于向指定进程发送一个信号。- 原型int kill(pid_t pid, int sig);- 参数: - pid:目标进程的进程ID。如果pid大于0,信号将发送给进程ID为pid的进程;如果pid等于0,信号将发送给与当前进程同组的所有进程;如果pid小于-1,信号将发送给进程组ID为-pid的所有进程。- sig:要发送的信号的编号。- 返回值:成功时返回0,失败时返回-1。
  4. sigaction()- 功能sigaction函数提供了一种更为复杂的方式来设置信号的处理函数,包括信号掩码和信号处理时的行为。- 原型int sigaction(int sig, const struct sigaction *act, struct sigaction *oldact);- 参数: - sig:要处理的信号编号。- act:指向sigaction结构的指针,用于指定新的信号处理动作。- oldact:指向sigaction结构的指针,用于保存先前的信号处理动作。- 返回值:成功时返回0,失败时返回-1。
  5. sigprocmask()- 功能sigprocmask函数用于设置信号掩码,从而阻塞或解阻塞特定的信号。- 原型int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);- 参数: - how:如何修改信号掩码。可以是SIG_BLOCK(添加到当前掩码)、SIG_UNBLOCK(从当前掩码中移除)或SIG_SETMASK(替换当前掩码)。- set:指向要添加或移除的信号集的指针。- oldset:指向保存当前信号掩码的指针。- 返回值:成功时返回0,失败时返回-1。
  6. sigpending()- 功能sigpending函数用于检查被阻塞的信号。- 原型int sigpending(sigset_t *set);- 参数set是一个指向sigset_t的指针,用于保存当前进程阻塞且待处理的信号集。- 返回值:成功时返回0,失败时返回-1。
  7. sigsuspend()- 功能sigsuspend函数用于在阻塞信号集的基础上临时替换信号掩码,并暂停进程直到捕获到一个信号。- 原型int sigsuspend(const sigset_t *mask);- 参数mask是一个指向sigset_t的指针,指定了临时的信号掩码。- 返回值:总是返回-1,并将errno设置为EINTR,表示函数被信号中断。

这些函数是Linux信号处理的基石,它们允许程序以可控的方式处理信号,确保程序的稳定性和安全性。在实际使用中,

  1. sigaction

通常比

  1. signal

更受欢迎,因为它提供了更多的灵活性和控制。

接下来给一个简单例子演示:

  1. #include <signal.h>
  2. #include <stdio.h>
  3. #include <unistd.h>
  4. // 信号处理函数
  5. void handle_sigint(int sig) {
  6. printf("Caught signal %d\n", sig);
  7. }
  8. int main() {
  9. struct sigaction sa;
  10. // 设置信号处理函数
  11. sa.sa_handler = &handle_sigint;
  12. sigemptyset(&sa.sa_mask);
  13. sa.sa_flags = 0;
  14. sigaction(SIGINT, &sa, NULL);
  15. // 发送信号
  16. raise(SIGINT);
  17. // 主循环
  18. while (1) {
  19. printf("Hello, World!\n");
  20. sleep(1);
  21. }
  22. return 0;
  23. }

在这个示例中,我们首先设置了一个信号处理函数

  1. handle_sigint

来处理

  1. SIGINT

信号。然后,我们使用

  1. sigaction

函数来设置这个处理函数。接着,我们使用

  1. raise

函数向当前进程发送了一个

  1. SIGINT

信号。最后,程序进入一个循环,每隔一秒打印一条消息。

三.网络编程相关信号

在网络编程中,信号通常用于处理异步事件,比如网络IO的读写就绪、连接请求、超时等。以下是一些在网络编程中常用的信号及相关函数和代码示例:

  1. SIGIO: - 功能:当网络IO操作可以进行时,系统会发送SIGIO信号给进程。- 使用场景:用于异步通知网络IO事件,如数据到达或套接字可写。- 代码示例
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <signal.h>
  4. #include <unistd.h>
  5. #include <fcntl.h>
  6. void handle_sigio(int sig) {
  7. printf("Received SIGIO signal.\n");
  8. }
  9. int main() {
  10. struct sigaction sa;
  11. sa.sa_handler = &handle_sigio;
  12. sigemptyset(&sa.sa_mask);
  13. sa.sa_flags = 0;
  14. sigaction(SIGIO, &sa, NULL);
  15. // 将标准输入设置为非阻塞
  16. fcntl(STDIN_FILENO, F_SETFL, O_ASYNC | O_NONBLOCK);
  17. fcntl(STDIN_FILENO, F_SETOWN, getpid());
  18. while (1) {
  19. sleep(1); // 模拟程序的其他工作
  20. }
  21. return 0;
  22. }

在这个示例中,我们为

  1. SIGIO

信号设置了一个处理函数

  1. handle_sigio

。然后,我们将标准输入设置为异步通知模式,并指定当前进程作为信号接收者。当标准输入有数据可读时,进程将收到

  1. SIGIO

信号。

  1. SIGPIPE: - 功能:当尝试向一个已经关闭的管道或网络连接写入数据时,系统会发送SIGPIPE信号给进程。- 使用场景:用于处理对端已经断开连接的情况。- 代码示例
  1. #include <stdio.h>
  2. #include <signal.h>
  3. void handle_sigpipe(int sig) {
  4. printf("Received SIGPIPE signal.\n");
  5. }
  6. int main() {
  7. struct sigaction sa;
  8. sa.sa_handler = &handle_sigpipe;
  9. sigemptyset(&sa.sa_mask);
  10. sa.sa_flags = 0;
  11. sigaction(SIGPIPE, &sa, NULL);
  12. // 模拟网络连接的写入操作
  13. // ...
  14. return 0;
  15. }

在这个示例中,我们为

  1. SIGPIPE

信号设置了一个处理函数

  1. handle_sigpipe

。当尝试向一个已经关闭的连接写入数据时,进程将收到

  1. SIGPIPE

信号。

  1. SIGALRM: - 功能:当设定的定时器超时时,系统会发送SIGALRM信号给进程。- 使用场景:用于实现超时机制,比如在网络请求中没有及时收到响应。- 代码示例
  1. #include <stdio.h>
  2. #include <signal.h>
  3. #include <unistd.h>
  4. void handle_sigalrm(int sig) {
  5. printf("Received SIGALRM signal.\n");
  6. }
  7. int main() {
  8. struct sigaction sa;
  9. sa.sa_handler = &handle_sigalrm;
  10. sigemptyset(&sa.sa_mask);
  11. sa.sa_flags = 0;
  12. sigaction(SIGALRM, &sa, NULL);
  13. alarm(5); // 设置5秒的定时器
  14. while (1) {
  15. sleep(1); // 模拟程序的其他工作
  16. }
  17. return 0;
  18. }

在这个示例中,我们为

  1. SIGALRM

信号设置了一个处理函数

  1. handle_sigalrm

。然后,我们使用

  1. alarm

函数设置了5秒的定时器。当定时器超时时,进程将收到

  1. SIGALRM

信号。

四.统一信号事件源

在服务器程序中,统一处理信号和I/O事件是指使用单一的事件循环来处理所有的异步事件,包括信号和I/O事件(如网络数据到达、连接请求等)。这种方法可以提高程序的响应性和效率,因为它避免了在多个地方处理不同类型的事件,从而简化了程序的结构。

为了实现这一点,通常需要使用特殊的系统调用或库,如

  1. select

  1. poll

  1. epoll

(在Linux中)或

  1. kqueue

(在BSD系统中),这些系统调用允许程序同时监控多个文件描述符的I/O事件。然而,信号通常不能直接通过这些系统调用来监控。因此,需要将信号处理与I/O事件处理结合起来,通常的做法是在信号处理函数中设置一个全局标志,然后在主事件循环中检查这个标志。

以下是一个简单例子:

  1. // 全局变量,用于标记信号是否发生
  2. volatile sig_atomic_t got_sigio = 0;
  3. void handle_sigio(int sig) {
  4. got_sigio = 1; // 设置信号发生标志
  5. }
  6. int main() {
  7. struct sigaction sa;
  8. fd_set readfds;
  9. int max_fd = 0;
  10. // 设置SIGIO信号的处理函数
  11. sa.sa_handler = &handle_sigio;
  12. sigemptyset(&sa.sa_mask);
  13. sa.sa_flags = 0;
  14. sigaction(SIGIO, &sa, NULL);
  15. // 将标准输入设置为非阻塞,并指定当前进程作为信号接收者
  16. fcntl(STDIN_FILENO, F_SETFL, O_ASYNC | O_NONBLOCK);
  17. fcntl(STDIN_FILENO, F_SETOWN, getpid());
  18. // 主事件循环
  19. while (1) {
  20. FD_ZERO(&readfds);
  21. // 监控标准输入
  22. FD_SET(STDIN_FILENO, &readfds);
  23. max_fd = STDIN_FILENO;
  24. // 使用select监控I/O事件
  25. struct timeval timeout = {5, 0}; // 超时设置为5秒
  26. int ready = select(max_fd + 1, &readfds, NULL, NULL, &timeout);
  27. if (ready == -1) {
  28. perror("select");
  29. break;
  30. } else if (ready == 0) {
  31. printf("Timeout occurred.\n");
  32. } else {
  33. if (FD_ISSET(STDIN_FILENO, &readfds)) {
  34. // 处理标准输入的I/O事件
  35. char buffer[1024];
  36. ssize_t count = read(STDIN_FILENO, buffer, sizeof(buffer));
  37. if (count <= 0) {
  38. break;
  39. }
  40. printf("Read from stdin: %.*s", (int)count, buffer);
  41. }
  42. }
  43. // 检查信号是否发生
  44. if (got_sigio) {
  45. printf("SIGIO received.\n");
  46. got_sigio = 0; // 重置信号发生标志
  47. }
  48. }
  49. return 0;
  50. }

在这个示例中,我们使用

  1. select

来监控标准输入的I/O事件,并设置了一个全局变量

  1. got_sigio

来标记

  1. SIGIO

信号是否发生。在信号处理函数

  1. handle_sigio

中,我们只是设置了这个标志。在主事件循环中,我们首先使用

  1. select

来等待I/O事件,然后在

  1. select

返回后检查是否收到了

  1. SIGIO

信号。如果收到了信号,我们就处理它,然后继续事件循环。

请注意,这个示例仅用于演示目的,实际应用中可能需要更复杂的逻辑来处理信号和I/O事件,尤其是在多线程或多进程的环境中。此外,

  1. select

并不是处理大量文件描述符的最有效方法,对于高性能服务器,通常会选择使用

  1. epoll

  1. kqueue

  1. 好啦!到这里这篇文章就结束啦,关于实例代码中我写了很多注释,如果大家还有不懂得,可以评论区或者私信我都可以哦![4d7d9707063b4d9c90ac2bca034b5705.png](https://img-blog.csdnimg.cn/direct/4d7d9707063b4d9c90ac2bca034b5705.png)!! 感谢大家的阅读,我还会持续创造网络编程相关内容的,记得点点小爱心和关注哟!![2cd0d6ee4ef84605933ed7c04d71cfef.jpeg](https://img-blog.csdnimg.cn/direct/2cd0d6ee4ef84605933ed7c04d71cfef.jpeg)
标签: AIGC 服务器 运维

本文转载自: https://blog.csdn.net/weixin_73951752/article/details/138303007
版权归原作者 离芩 所有, 如有侵权,请联系我们删除。

“织梦云端:网络信号原理的艺术解码”的评论:

还没有评论