【TCP/IP】多进程服务器的实现(进阶) - 信号处理及signal、sigaction函数

news/2024/5/18 11:52:27 标签: 服务器, 网络协议, 网络, tcp/ip, udp

目录

信号

signal函数

sigaction函数

尝试用信号来处理僵尸进程


        我们在之前学习了如何处理“僵尸进程”,但也会有疑问:调用wait和waitpid函数时我们关注的始终是在子进程上,那么父进程上的管控(对于子进程)该如何实现呢?这就引出了接下来讲的内容——信号处理。

信号

        信号这个概念大家应该不陌生,诸如汽车的鸣笛、闹钟的响铃等,这些都是信号,用以告诉人们某件事情的发生。而在计算机中,同样有这个概念。在编程上常用信号来对接收到的某些数据或者指令做出与之对应的响应处理。

signal函数

        让我们先来看看<signal.h>库中的signal函数,其结构如下:

#include <signal.h>

void (*signal (int signo , void (*func)(int))) (int);


// 为了在产生信号时调用,返回之前注册的函数指针。

// 函数概念:名为signal的返回类型为void型函数指针的,带有一个int参数的函数
// 参数 int signo, void (* func)(int)
// 返回类型:参数为int型,返回void型函数指针。

        signo参数用来标记触发的条件,func则为将要调用的函数的指针。与参数所相关的宏有:

  • SIGALRM: 按照已通过调用alarm函数注册的时间为信号点。
  • SIGINT: 输入CTRL+C暂停时为信号点
  • SIGCHILD: 子进程终止时为信号点。

补充:

        alarm函数的用法及意义如下:

#include <unistd.h>

unsigned int alarm (unsigned int seconds);

// 返回 以秒为单位 的,距 SIGALRM 信号发生时所剩余的时间

        其中 seconds 参数用以传入欲设定的alarm的周期(以s为单位)。若传递0至变量中,则意味着将会取消对SIGALRM信号的预约。同时,若未指定该产生信号所对应的处理函数时,则该进程将被终止。

        接下来,让我们尝试来编写 SIGALRM 和 SIGINT 两种事件下的验证demo吧。

singal.cpp

#include <stdio.h>
#include <unistd.h>
#include <signal.h>

void alarmEvent(int sig) //包括下面的keyEvent,这类函数被称为信号处理器(Handler)
{
    if (sig == SIGALRM)
    {
        printf("Time out!\n");
    }

    alarm(2);
}

void keyEvent(int sig)
{
    if (sig == SIGINT)
    {
        printf("\nCTRL+C pressed\n");
    }
}

int main(int argc, char *argv[])
{
    //注册:设置SIGGALRM为触发条件,调用alarmEvent函数
    signal(SIGALRM, alarmEvent); 
    //注册:设置SIGINT为触发条件,调用keyEvent函数
    signal(SIGINT, keyEvent);

    //设置时钟周期为2s
    alarm(2); 

    for (size_t i = 0; i < 5; i++)
    {
        printf("Wait for an event to occur...\n");
        sleep(50);  //实际操作上无法休眠50s
    }

    return 0;
}

        运行结果:

        验证完毕。

        Q:为什么程序在实际运行上,没有感受到在等待事件发生后的50s延迟呢?

        A:信号在响应时,尽管存在有阻塞态的进程,但操作系统为了调用信号处理器,会强制唤醒进行阻塞态(sleep中的)进程,并且该进程一旦被唤醒,将不会在进入睡眠状态,因此在操作上,我们并不会感受到会有长达50s的延迟。

sigaction函数

        sigaction算是对signal函数的升级,能够更好地支持不同的操作系统(signal函数在不同的操作系统中用法和效果可能会不同)。

        接下来让我们来看下这个函数长什么样:

#include <signal.h>

int sigaction(int signo , const struct sigaction * act , struct sigaction *
oldact) ;

// 成功时退回0,失败时退回-1。

/* 参数含义 */

// signo:与signal函数相同,传递信号信息
// act:对应第一个参数的信号处理函数
// oldact:通过此参数获取之前注册的信号处理函数指针,不需要时则传递0。

        对于sigaction,它的结构体定义如下:

 //结构体做了简化,实际上其中一些变量用宏和typedef做了进一步封装

 struct sigaction
    {
        void (*sa_handler)(int);
        sigset_t sa_mask;
        int sa_flags;
        void (*sa_restorer)(void);
    };

        参数意义可以参考以下: 

  • sa_handler:用以保存信号处理函数的指针值
  • sa_mask:需要滤除的附加信号集,省略
  • sa_flags:特殊标记符,省略
  • sa_restorer:欲重置的(信号)处理器,省略

        OK,让我们编写一个实例来验证这个函数的功能

sigaction.cpp

#include <stdio.h>
#include <unistd.h>
#include <signal.h>

void alarmEvent(int sig)
{
    if (sig == SIGALRM)
    {
        printf("Time out!\n");
    }
    alarm(2);
}

int main(int argc, char *argv[])
{
    //声明sigaction结构体变量act
    struct sigaction act;
    //将信号处理函数存入结构体中
    act.sa_handler = alarmEvent;
    //将sa_mask成员的所有位初始化为0
    sigemptyset(&act.sa_mask);
    //将sa_flags成员同样初始化为0
    act.sa_flags = 0;
    //注册:触发条件为SIGALARM,调用act信号处理器中的信息
    sigaction(SIGALRM, &act, 0);

    alarm(2);

    for (size_t i = 0; i < 5; i++)
    {
        printf("Wait for an event to occur...\n");
        sleep(50);
    }

    return 0;
}

        运行结果:

        结果验证了之前的想法。

尝试用信号来处理僵尸进程

        当我们掌握signal、sigaction两个函数后,便可更好地去管控父、子进程了。在之前我们采用的是wait、waitpid函数来销毁僵尸进程。接下来,请大家尝试一下用信号来消灭“僵尸进程”吧!

        欢迎大家在评论区贴出你们的代码~


http://www.niftyadmin.cn/n/420419.html

相关文章

chatgpt赋能python:Python如何去除列表中重复的元素?

Python如何去除列表中重复的元素&#xff1f; 在Python编程中&#xff0c;经常会遇到需要操作列表的情况。而有时候列表中会出现重复的元素&#xff0c;这会给后续的数据处理带来麻烦。那么如何去除列表中重复的元素呢&#xff1f;下面将为大家介绍几种方法。 1. 使用set()函…

chatgpt赋能python:Python如何取三位小数

Python 如何取三位小数 Python 是一种很强大的编程语言&#xff0c;可以应用于各个领域。其中&#xff0c;处理数字也是 Python 的一项强大功能。当我们需要对数字进行精细的操作时&#xff0c;常常需要使用到取小数的功能。本文将介绍如何使用 Python 取三位小数&#xff0c;…

萤石摄像头RTSP流获取(黑屏解决)

前言 在获取萤石摄像头RTSP视频流时&#xff0c;视频流获取不成功&#xff0c;黑屏并且一直显示缓冲中。下面对获取过程中查阅的资料和解决方案做一下汇总。 打开RTSP 在萤石云视频APP中打开RTSP&#xff0c;【我的】-【工具】-【局域网设备预览】-【开始扫描】-【选择摄像头…

​LeetCode解法汇总1093. 大样本统计

目录链接&#xff1a; 力扣编程题-解法汇总_分享记录-CSDN博客 GitHub同步刷题项目&#xff1a; https://github.com/September26/java-algorithms 原题链接&#xff1a;力扣 描述&#xff1a; 我们对 0 到 255 之间的整数进行采样&#xff0c;并将结果存储在数组 count 中&…

DDD--基本概念

最近项目组一直在推DDD领域驱动设计&#xff0c;现就一些个人理解分享如下。 DDD&#xff08;领域驱动设计&#xff09;是一种软件开发方法论&#xff0c;旨在解决复杂业务场景下的软件设计与开发问题。以下是DDD的基本概念&#xff1a; 领域&#xff08;Domain&#xff09;&…

NLP——ELMO;BERT;Transformers

文章目录 ELMOELMO 简介ELMO 优点利用了多层的 hidden 表示ELMO 缺点 BERTBERT V.S. ELMO两种预训练任务Object1: Masked Language ModelObject2: Next sentence prediction 训练细节如何使用 BERTBERT 应用——垃圾邮件分类 Transformerself-attentionMulti-head AttentionTra…

浅析设计模式 -- 责任链模式

目录 前言 概述 基本概念 ▐ 结构 ▐ 使用 使用示例 ▐ 代码实现​​​​​​​ ▐ 结果输出 ▐ UML图 扩展 源码赏析 优缺点及适用场景 ▐ 优点 ▐ 缺点 ▐ 适用场景 前言 我们在进行软件开发时要想实现可维护、可扩展&#xff0c;就需要尽量复用代码&…

chatgpt赋能python:Python中如何取出字符串中的数字并赋予新的变量

Python 中如何取出字符串中的数字并赋予新的变量 在 Python 中&#xff0c;我们经常需要处理字符串&#xff0c;其中可能包含多种类型的数据。当我们需要获取字符串中的数字时&#xff0c;该怎样做呢&#xff1f;本文将介绍取出字符串中的数字的方法&#xff0c;并赋予新的变量…