【Linux高性能服务器编程】信号处理方法之统一事件源

简介: 【Linux高性能服务器编程】信号处理方法之统一事件源

为什么要用统一事件源

信号是一种异步事件:信号处理函数和程序的主循环是两条不同的执行路径。即当进程收到信号时,操作系统会中断进程当前的正常流程,转而进入信号处理函数去处理信号,完成后再返回中断的地方继续执行。


很显然,信号处理函数需要尽可能地快速执行完毕,以确保该信号不会被屏蔽太久。因为为了避免信号竞态条件的发生,信号处理期间系统不会再次触发它。


解决方法:

一种典型的解决方法是:把信号的主要处理逻辑放到程序的主循环中,当信号处理函数被触发时,它只是简单的通知主循环程序接收到信号,并把信号值传递给主循环,主循环再根据接收到的信号执行目标信号对应的逻辑代码。


信号处理函数通常使用管道来将信号“传递”给主循环的:信号处理函数往管道的写端写入信号值,主循环则从管道的读端读出信号值。那么主循环怎么知道管道上何时有数据可读呢?这很简单,我们只需要使用I/O复用系统调用来监听管道的读端文件描述符上的可读事件。如此一来,信号事件就能和其他I/O事件一样被处理了。

统一事件源的概念

统一事件源,即将信号和其他事件一样进行处理。


具体就是信号处理函数使用管道将信号传递给主循环,信号处理函数往管道的写端写入信号值,主循环则从管道的读端读出信号值,使用I/O复用系统调用来监听管道读端的可读事件,这样信号事件与其他文件描述符都可以通过epoll来监测,从而实现统一处理。


很多优秀的I/O框架库和后台服务器程序都统一处理信号和I/O事件,比如Libevent I/O 框架库和 xinetd 超级服务。以下是一个统一事件源的简单实现。

统一事件源的应用

信号处理函数:

//信号处理函数
void sig_handler(int sig)
{
    //保留原来的errno,在函数最后恢复,以保证函数的可重入性
    int save_errno = errno;
    int msg = sig;
    send(pipefd[1], (char* )&msg, 1, 0);
    errno = save_errno;
}

这里信号处理的方式只是通过管道向主循环发生信号,将信号交给主喜欢出现进行处理,以达到统一事件源的目的。

添加信号到信号处理函数:

void addsig(int sig)
{
  //创建sigaction结构体变量
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    //信号处理函数中仅仅发送信号值,不做对应逻辑处理
    sa.sa_handler = sig_handler;
    sa.sa_flags |= SA_RESTART;
    //将所有信号添加到信号集中
    sigfillset(&sa.sa_mask);
    //执行sigaction函数
    assert(sigaction(sig, &sa, nullptr) != -1);
}

这里使用了 sigaction 系统调用来设置信号处理函数,相比于 signal 系统调用具有更强的健壮性。


信号通知逻辑:


  1. 使用socketpair创建管道,其中写端由信号处理函数写入信号值,主循环程序通过 epoll I/O复用监视读事件就绪时从管道的读端读取信号。
  2. 这里主要设置了对SIGTERM(kill -15)和SIGTINT(ctrl + C) 信号的处理,通过struct sigaction结构体和sigaction系统调用注册对信号的捕捉和处理。
  3. 利用epoll 监测管道读端的读事件就绪。
  4. 当主循环收到这两个信号时安全的退出程序。

具体逻辑代码如下:

epoll_event events[MAX_EVENT_NUMBER];
int epollfd = epoll_create(5);
assert(epollfd != -1);
addfd(epollfd, listenfd);
// 使用 socketpair 创建管道,注册 pipefd[0] 上的可读事件
ret = socketpair(AF_UNIX, SOCK_STREAM, 0, pipefd);
assert(ret != -1);
setNonBlock(pipefd[1]);
addfd(epollfd, pipefd[0]);
//设置一些信号的处理函数
addsig(SIGHUP);
addsig(SIGCHLD);
addsig(SIGTERM);
addsig(SIGINT);
bool stop_server = false;
while (!stop_server)
{
    int number = epoll_wait(epollfd, events, MAX_EVENT_NUMBER, -1);
    if(number < 0 && errno != EINTR)
    {
        printf("epoll wait failure!\n");
        break;
    }
    for(int i = 0; i < number; ++i)
    {
        int sockfd = events[i].data.fd;
        //如果就绪的文件描述符是listenfd, 则处理新连接
        if(sockfd == listenfd)
        {
            sockaddr_in peer;
            socklen_t len = sizeof(peer);
            int connfd = accept(listenfd, (sockaddr*)&peer, &len);
            addfd(epollfd, connfd);
        }
        //如果就绪的文件描述符是pipefd[0],就处理信号
        else if(sockfd == pipefd[0] && (events[i].events & EPOLLIN))
        {
            int sig;
            char signals[1024];
            ssize_t s = recv(pipefd[0], signals, sizeof(signals), 0);
            if(s == -1)
            {
                continue;
            }
            else if(s == 0)
            {
                continue;
            }
            else
            {
                //每个信号占一个字节,所以按照字节来逐个接收信号。此处以SIGTERM为例,来安全的终止服务器程序:
                for(int i = 0; i < s; ++i)
                {
                    switch (signals[i])
                    {
                    case SIGCHLD:
                    case SIGHUP:
                    {
                        continue;
                    }
                    case SIGTERM:
                    case SIGINT:
                    {
                        stop_server = true;
                    }
                    default:
                        break;
                    }
                }
            }
        }
        else
        {
            //其他I/O就绪
        }
    }
}
printf("close fds\n");
close(listenfd);
close(pipefd[0]);
close(pipefd[1]);

运行结果:

运行程序,然后启动另一个终端使用pidof查看该程序的PID,然后向该程序发送15号信号,即SIGTERM,可以发现程序安全的

目录
相关文章
|
16天前
|
安全 大数据 Linux
云上体验最佳的服务器操作系统 - Alibaba Cloud Linux | 飞天技术沙龙-CentOS 迁移替换专场
本次方案的主题是云上体验最佳的服务器操作系统 - Alibaba Cloud Linux ,从 Alibaba Cloud Linux 的产生背景、产品优势以及云上用户使用它享受的技术红利等方面详细进行了介绍。同时,通过国内某社交平台、某快递企业、某手机客户大数据业务 3 大案例,成功助力客户实现弹性扩容能力提升、性能提升、降本增效。 1. 背景介绍 2. 产品介绍 3. 案例分享
|
2月前
|
运维 监控 Linux
推荐几个不错的 Linux 服务器管理工具
推荐几个不错的 Linux 服务器管理工具
136 6
|
2月前
|
存储 监控 Linux
嵌入式Linux系统编程 — 5.3 times、clock函数获取进程时间
在嵌入式Linux系统编程中,`times`和 `clock`函数是获取进程时间的两个重要工具。`times`函数提供了更详细的进程和子进程时间信息,而 `clock`函数则提供了更简单的处理器时间获取方法。根据具体需求选择合适的函数,可以更有效地进行性能分析和资源管理。通过本文的介绍,希望能帮助您更好地理解和使用这两个函数,提高嵌入式系统编程的效率和效果。
110 13
W9
|
3月前
|
运维 关系型数据库 MySQL
轻松管理Linux服务器的5个优秀管理面板
Websoft9 应用管理平台,github 2k star 开源软件,既有200+的优秀开源软件商店,一键安装。又有可视化的Linux管理面板,文件、数据库、ssl证书方便快捷管理。
W9
242 1
|
3月前
|
缓存 Ubuntu Linux
Linux环境下测试服务器的DDR5内存性能
通过使用 `memtester`和 `sysbench`等工具,可以有效地测试Linux环境下服务器的DDR5内存性能。这些工具不仅可以评估内存的读写速度,还可以检测内存中的潜在问题,帮助确保系统的稳定性和性能。通过合理配置和使用这些工具,系统管理员可以深入了解服务器内存的性能状况,为系统优化提供数据支持。
82 4
|
3月前
|
运维 监控 Linux
服务器管理面板大盘点: 8款开源面板助你轻松管理Linux服务器
在数字化时代,服务器作为数据存储和计算的核心设备,其管理效率与安全性直接关系到业务的稳定性和可持续发展。随着技术的不断进步,开源社区涌现出众多服务器管理面板,这些工具以其强大的功能、灵活的配置和友好的用户界面,极大地简化了Linux服务器的管理工作。本文将详细介绍8款开源的服务器管理面板,包括Websoft9、宝塔、cPanel、1Panel等,旨在帮助运维人员更好地选择和使用这些工具,提升服务器管理效率。
|
2月前
|
存储 Oracle 安全
服务器数据恢复—LINUX系统删除/格式化的数据恢复流程
Linux操作系统是世界上流行的操作系统之一,被广泛用于服务器、个人电脑、移动设备和嵌入式系统。Linux系统下数据被误删除或者误格式化的问题非常普遍。下面北亚企安数据恢复工程师简单聊一下基于linux的文件系统(EXT2/EXT3/EXT4/Reiserfs/Xfs) 下删除或者格式化的数据恢复流程和可行性。
|
3月前
|
安全 Linux API
Linux服务器安全
人们常误认为服务器因存于数据中心且数据持续使用而无需加密。然而,当驱动器需维修或处理时,加密显得尤为重要,以防止数据泄露。Linux虽有dm-crypt和LUKS等内置加密技术,但在集中管理、根卷加密及合规性等方面仍存不足。企业应选择具备强大验证、简单加密擦除及集中管理等功能的解决方案,以弥补这些缺口。
48 0
|
21天前
|
弹性计算 数据挖掘 应用服务中间件
阿里云轻量应用服务器68元与云服务器99元和199元区别及选择参考
目前阿里云有三款特惠云服务器,第一款轻量云服务器2核2G68元一年,第二款经济型云服务器2核2G3M带宽99元1年,第三款通用算力型2核4G5M带宽199元一年。有的新手用户并不是很清楚他们之间的区别,因此不知道如何选择。本文来介绍一下它们之间的区别以及选择参考。
329 87
|
14天前
|
存储 弹性计算 应用服务中间件
阿里云轻量应用服务器出新品通用型实例了,全球26个地域可选
近日,阿里云再度发力,推出了首款全新升级的轻量应用服务器——通用型实例。这款服务器实例不仅标配了200Mbps峰值公网带宽,更在计算、存储、网络等基础资源上进行了全面优化,旨在为中小企业和开发者提供更加轻量、易用、普惠的云计算服务,满足其对于通用计算小算力的迫切需求。目前,这款新品已在全球26个地域正式上线,为全球用户提供了更加便捷、高效的上云选择。
115 27