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

打印 上一主题 下一主题

主题 664|帖子 664|积分 1992

hello !大家好呀! 接待大家来到我的Linux高性能服务器编程系列之《织梦云端:网络信号原理的艺术解码》,在这篇文章中,你将会学习到网络信号原理以及应用,并且我会给出源码进行剖析,以及手绘UML图来帮助大家来理解,希望能让大家更能了解网络编程技能!!!
  希望这篇文章能对你有所帮助
,大家要是觉得我写的不错的话,那就点点免费的小爱心吧!
(注:这章对于高性能服务器的架构非常重要哟!!!)
  
         
   

目次
一. 什么是信号?
二. 信号的发送以及处置惩罚方式 
三.网络编程相关信号
四.统一信号变乱源


一. 什么是信号?


在Linux网络编程中,“信号”(Signal)是操作系统用来关照进程某个变乱已经发生的一种机制。信号是一种软件中断,它可以在任何时候发送给一个进程,使得该进程可以中断当前的执行,处置惩罚该信号,然后再回到原来的执行状态。
Linux系统中界说了许多信号,每个信号都有其特定的用途。比方:


  • SIGINT:通常在用户按下Ctrl+C时发出,用于中断步伐的执行。
  • SIGCHLD:当一个子进程终止时,父进程会收到这个信号。
  • SIGALRM:定时器超时时发送。
  • SIGPIPE:在尝试写入一个没有读取器的管道或socket时发送。
在网络编程中,信号常常用于处置惩罚各种异步变乱,好比:

  • 处置惩罚子进程状态变革:当一个子进程改变其状态(好比终止或暂停)时,父进程会吸收到SIGCHLD信号。
  • 处置惩罚网络IO:在一些网络编程模子中,如使用select或poll,当IO预备好时,可以通过信号来关照应用步伐。
  • 定时器:使用setitimer或alarm函数设置的定时器超时时,可以通过SIGALRM信号关照进程。
处置惩罚信号的方法主要有两种:


  • 信号处置惩罚函数:可以为特定的信号设置一个处置惩罚函数(handler),当该信号发生时,系统将调用这个函数。
  • 信号掩码:可以阻塞某些信号,使得它们在处置惩罚期间不被传递给进程。
信号是Linux系统编程中的一个重要部分,特别是在网络编程领域,因为它们提供了一种机制来相应异步变乱,这对于构建高效和相应敏捷的网络应用步伐至关重要。

二. 信号的发送以及处置惩罚方式 

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

  • signal():用于设置一个信号的处置惩罚函数。
  • raise():用于向当进步程发送一个信号。
  • kill():用于向指定进程发送一个信号。
  • sigaction():提供了一种更为复杂的方式来设置信号的处置惩罚函数,包括信号掩码。
  • sigprocmask():用于设置信号掩码,从而阻塞或解阻塞特定的信号。
  • sigpending():用于检查被阻塞的信号。
  • sigsuspend():用于在阻塞信号集的基础上暂时更换信号掩码,并暂停进程直到捕获到一个信号。
  

  • signal()

    • 功能:signal函数用于设置一个信号的处置惩罚函数。当指定信号被传递给进程时,系统将调用这个处置惩罚函数。
    • 原型:void (*signal(int sig, void (*func)(int)))(int);
    • 参数

      • sig:要处置惩罚的信号编号。
      • func:信号处置惩罚函数,可以是SIG_IGN(忽略信号)、SIG_DFL(默认处置惩罚)或者一个自界说的函数指针。

    • 返回值:返回先前的信号处置惩罚函数的指针。

  • raise()

    • 功能:raise函数用于向当进步程发送一个信号。
    • 原型:int raise(int sig);
    • 参数:sig是要发送的信号的编号。
    • 返回值:乐成时返回0,失败时返回非0值。

  • kill()

    • 功能:kill函数用于向指定进程发送一个信号。
    • 原型:int kill(pid_t pid, int sig);
    • 参数

      • pid:目标进程的进程ID。如果pid大于0,信号将发送给进程ID为pid的进程;如果pid等于0,信号将发送给与当进步程同组的全部进程;如果pid小于-1,信号将发送给进程组ID为-pid的全部进程。
      • sig:要发送的信号的编号。

    • 返回值:乐成时返回0,失败时返回-1。

  • sigaction()

    • 功能:sigaction函数提供了一种更为复杂的方式来设置信号的处置惩罚函数,包括信号掩码和信号处置惩罚时的行为。
    • 原型:int sigaction(int sig, const struct sigaction *act, struct sigaction *oldact);
    • 参数

      • sig:要处置惩罚的信号编号。
      • act:指向sigaction结构的指针,用于指定新的信号处置惩罚动作。
      • oldact:指向sigaction结构的指针,用于生存先前的信号处置惩罚动作。

    • 返回值:乐成时返回0,失败时返回-1。

  • sigprocmask()

    • 功能:sigprocmask函数用于设置信号掩码,从而阻塞或解阻塞特定的信号。
    • 原型:int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
    • 参数

      • how:如何修改信号掩码。可以是SIG_BLOCK(添加到当前掩码)、SIG_UNBLOCK(从当前掩码中移除)或SIG_SETMASK(更换当前掩码)。
      • set:指向要添加或移除的信号集的指针。
      • oldset:指向生存当前信号掩码的指针。

    • 返回值:乐成时返回0,失败时返回-1。

  • sigpending()

    • 功能:sigpending函数用于检查被阻塞的信号。
    • 原型:int sigpending(sigset_t *set);
    • 参数:set是一个指向sigset_t的指针,用于生存当进步程阻塞且待处置惩罚的信号集。
    • 返回值:乐成时返回0,失败时返回-1。

  • sigsuspend()

    • 功能:sigsuspend函数用于在阻塞信号集的基础上暂时更换信号掩码,并暂停进程直到捕获到一个信号。
    • 原型:int sigsuspend(const sigset_t *mask);
    • 参数:mask是一个指向sigset_t的指针,指定了暂时的信号掩码。
    • 返回值:总是返回-1,并将errno设置为EINTR,表示函数被信号中断。

这些函数是Linux信号处置惩罚的基石,它们允许步伐以可控的方式处置惩罚信号,确保步伐的稳固性和安全性。在现实使用中,sigaction通常比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. }
复制代码
在这个示例中,我们首先设置了一个信号处置惩罚函数handle_sigint来处置惩罚SIGINT信号。然后,我们使用sigaction函数来设置这个处置惩罚函数。接着,我们使用raise函数向当进步程发送了一个SIGINT信号。末了,步伐进入一个循环,每隔一秒打印一条消息。 

三.网络编程相关信号


在网络编程中,信号通常用于处置惩罚异步变乱,好比网络IO的读写停当、连接请求、超时等。以下是一些在网络编程中常用的信号及相关函数和代码示例:

  • 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. }
复制代码
在这个示例中,我们为SIGIO信号设置了一个处置惩罚函数handle_sigio。然后,我们将标准输入设置为异步关照模式,并指定当进步程作为信号吸收者。当标准输入有数据可读时,进程将收到SIGIO信号。

  • 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. }
复制代码
在这个示例中,我们为SIGPIPE信号设置了一个处置惩罚函数handle_sigpipe。当尝试向一个已经关闭的连接写入数据时,进程将收到SIGPIPE信号。

  • 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. }
复制代码
在这个示例中,我们为SIGALRM信号设置了一个处置惩罚函数handle_sigalrm。然后,我们使用alarm函数设置了5秒的定时器。当定时器超时时,进程将收到SIGALRM信号。

四.统一信号变乱源

 


   在服务器步伐中,统一处置惩罚信号和I/O变乱是挑拨用单一的变乱循环来处置惩罚全部的异步变乱,包括信号和I/O变乱(如网络数据到达、连接请求等)。这种方法可以进步步伐的相应性和效率,因为它避免了在多个地方处置惩罚不同范例的变乱,从而简化了步伐的结构。
  为了实现这一点,通常需要使用特殊的系统调用或库,如select、poll、epoll(在Linux中)或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. }
复制代码
  在这个示例中,我们使用select来监控标准输入的I/O变乱,并设置了一个全局变量got_sigio来标记SIGIO信号是否发生。在信号处置惩罚函数handle_sigio中,我们只是设置了这个标志。在主变乱循环中,我们首先使用select来等待I/O变乱,然后在select返回后检查是否收到了SIGIO信号。如果收到了信号,我们就处置惩罚它,然后继承变乱循环。
  请留意,这个示例仅用于演示目的,现实应用中大概需要更复杂的逻辑来处置惩罚信号和I/O变乱,尤其是在多线程或多进程的环境中。此外,select并不是处置惩罚大量文件形貌符的最有用方法,对于高性能服务器,通常会选择使用epoll或kqueue。
  
         好啦!到这里这篇文章就结束啦,关于实例代码中我写了很多注释,如果大家尚有不懂得,可以评论区或者私信我都可以哦
!! 感谢大家的阅读,我还会连续创造网络编程相关内容的,记得点点小爱心和关注哟!
  

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

王海鱼

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表