欢迎光临
我们一直在努力

webserver服务器信号处理流程

信号并不是直接被 epoll 检测到的,而是通过一个巧妙的 “信号 – 管道 – I/O” 转换机制,间接地让 epoll 感知到,从而在主循环中进行处理。

下面我们来详细拆解这个过程,并结合你项目中的具体信号(SIGALRM 和 SIGTERM)说明它们是如何被触发和处理的。

整体机制:信号的 “暗度陈仓”

这个机制的核心可以比喻为:把信号 “偷偷” 放进一条管道里,然后 epoll 一直在 “盯” 着这条管道,一旦有东西进来,就按照既定流程处理。

具体步骤如下:

  • 创建 “秘密通道” (socketpair):

    • 代码 socketpair(PF_UNIX, SOCK_STREAM, 0, m_pipefd) 创建了一对相互连接的文件描述符 m_pipefd[0] (读端) 和 m_pipefd[1] (写端)。这对 FD 就像一条内部管道。
    • 接着,utils.addfd(m_epollfd, m_pipefd[0], false, 0) 将管道的读端 m_pipefd[0] 加入到 epoll 的监听列表中,关注其读事件。
  • 信号来了!谁来接收? (sig_handler):

    • 当一个信号(比如 SIGALRM)被触发时,内核会中断程序的正常执行,转而去执行我们注册的信号处理函数 utils.sig_handler。
    • 关键点:sig_handler 函数非常简单,它不做任何复杂的业务逻辑。它唯一的工作就是:向管道的写端 m_pipefd[1] 写入一个字节的数据(通常是信号的编号)。
    • 例如,收到 SIGALRM 后,sig_handler 就往 m_pipefd[1] 里写一个代表 SIGALRM 的数字。
  • epoll “察觉” 到异常:

    • 一旦有数据写入 m_pipefd[1],管道的读端 m_pipefd[0] 就变成了 “可读” 状态。
    • 由于 m_pipefd[0] 正被 epoll 监听,epoll_wait 会立刻返回,并通知主循环 m_pipefd[0] 上有读事件发生。
  • 主循环处理 “信号包裹” (dealwithsignal):

    • 主循环在 eventLoop 中发现 m_pipefd[0] 可读,便会调用相应的处理函数(在你的项目中应该是 dealwithsignal)。
    • dealwithsignal 函数会从 m_pipefd[0] 中读取那个字节的数据,从而得知刚刚收到了哪个信号。
    • 然后,dealwithsignal 会根据信号的类型,调用真正的业务处理逻辑:
      • 如果是 SIGALRM,就调用 utils.timer_handler() 来处理定时任务(比如检查并关闭超时连接)。
      • 如果是 SIGTERM,就设置一个标志位(比如 stop_server = true),让服务器在合适的时机优雅地关闭。
  • 总结:你的理解与实际流程的对比

    你的理解实际流程
    信号触发 -> epoll 检测到信号 信号触发 -> 信号处理函数 sig_handler 被调用 -> 向管道写端写入一个字节
    -> 管道读端变为可读 -> epoll 检测到读事件
    -> 调用对应函数处理 -> 主循环调用 dealwithsignal 函数 -> 从管道读端读出信号值 -> 根据信号值调用对应函数处理

    项目中具体信号的触发方式

    现在我们来看看你项目中的两个关键信号是如何被触发的:

  • SIGALRM – 定时器信号

    • 触发方式:由 alarm() 函数触发。
    • 项目中的触发点:
      • 在 Utils::init() 函数中,可能会调用 alarm(m_TIMESLOT) 来启动第一个定时器。
      • 更重要的是,在 Utils::timer_handler() 函数的末尾,通常会再次调用 alarm(m_TIMESLOT)。
      • 这样就形成了一个循环:alarm() -> SIGALRM -> timer_handler() -> alarm() -> … 从而实现了周期性的定时任务。
  • SIGTERM – 终止信号

    • 触发方式:由外部命令触发。
    • 项目中的触发点:当你想优雅地停止服务器时,在终端中执行 kill <服务器进程ID> 命令。这个命令会向服务器进程发送一个 SIGTERM 信号。
    • 服务器收到该信号后,会在 dealwithsignal 中处理,比如设置 stop_server = true,主循环在下一次迭代时检查到这个标志,就会停止接收新连接,处理完当前所有请求后,释放资源并退出。
  • 为什么要绕这么一圈?

    这么做的根本原因是为了线程安全和代码的可维护性。

    • 信号处理函数的限制:信号处理函数 sig_handler 可以在程序执行的任何时刻被中断并调用,它的执行上下文非常特殊。在其中调用复杂的函数(如 epoll_ctl、printf、malloc 等)可能会导致死锁或数据结构损坏。
    • 统一事件循环:通过将信号转换为 I/O 事件,我们就可以将所有事件(网络 I/O、定时器、进程终止)的处理逻辑都统一到了主循环 eventLoop 中。这样代码结构更清晰,所有复杂操作都在一个安全的上下文中执行。

    SIGTERM 和 SIGALRM 这两个信号,以及它们在你的 Web 服务器项目中对应的具体事件。

    可以这样说,SIGTERM 是 “我要你优雅地退出” 的指令,SIGALRM 是 “时间到了,该做定时任务了” 的闹钟。


    1. SIGTERM – 终止信号 (Signal Terminate)

    • 对应事件:请求进程优雅地终止。
    • 触发方式:由外部命令触发。
    • 在你项目中的场景:当你希望关闭服务器时,你不会直接去 kill -9 <pid>(这是强制、暴力的终止),因为这会导致正在处理的请求中断、数据丢失。正确的做法是在终端执行 kill <服务器进程ID> 命令。这个命令默认发送的就是 SIGTERM 信号。
    • 服务器的处理流程:
    • 服务器进程收到 SIGTERM 信号。
    • 信号处理函数 sig_handler 被调用,它向 m_pipefd[1] 写入一个字节(代表 SIGTERM)。
    • epoll 检测到 m_pipefd[0] 可读,主循环调用 dealwithsignal 函数。
    • dealwithsignal 函数读取并识别出是 SIGTERM 信号。
    • 它通常会设置一个标志位,比如 stop_server = true。
    • 主循环在接下来的迭代中检查到 stop_server 为 true,便开始执行优雅关闭流程:
      • 不再接受新的客户端连接。
      • 等待线程池处理完当前所有任务。
      • 关闭监听套接字和所有客户端连接。
      • 释放 epoll 实例、数据库连接池等资源。
      • 最后,进程正常退出。

    简单来说,SIGTERM 对应着 “管理员发出关闭服务器指令” 这个事件。


    2. SIGALRM – 闹钟信号 (Signal Alarm)

    • 对应事件:定时器到期。
    • 触发方式:由 alarm() 函数设置的定时器触发。
    • 在你项目中的场景:你的服务器需要定期执行一些任务,最典型的就是清理超时的空闲连接,以防止资源耗尽。
    • 服务器的处理流程:
    • 在服务器初始化时,会调用 alarm(m_TIMESLOT),设置一个定时器,比如每隔 5 秒触发一次。
    • 当 5 秒过去,内核向进程发送 SIGALRM 信号。
    • 信号处理函数 sig_handler 被调用,它向 m_pipefd[1] 写入一个字节(代表 SIGALRM)。
    • epoll 检测到 m_pipefd[0] 可读,主循环调用 dealwithsignal 函数。
    • dealwithsignal 函数读取并识别出是 SIGALRM 信号。
    • 它会调用 utils.timer_handler() 函数。
    • timer_handler() 函数会:
      • 调用 m_timer_list.tick(),遍历定时器链表,找到所有超时的客户端连接。
      • 对于每个超时连接,执行回调函数 cb_func,该函数会关闭套接字、从 epoll 中删除事件等,释放资源。
      • 在函数末尾,再次调用 alarm(m_TIMESLOT),设置下一个定时器,形成一个周期性的循环。

    简单来说,SIGALRM 对应着 “预设的时间周期已到,需要执行定时任务(如检查超时连接)” 这个事件。


    总结

    信号中文含义触发源对应事件服务器典型处理动作
    SIGTERM 终止信号 外部命令 (kill <pid>) 管理员要求服务器关闭 启动优雅关闭流程,安全释放所有资源后退出。
    SIGALRM 闹钟信号 内部 alarm() 函数 定时任务时间到 执行 timer_handler(),检查并清理所有超时的客户端连接,然后重置定时器。

    这两个信号的处理,共同保证了服务器的稳定性(通过定期清理资源)和可管理性(通过优雅关闭)。

    赞(0)
    未经允许不得转载:171主机测评 » webserver服务器信号处理流程
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址