【Linux网络编程】信号和定时器

总结《Linux高性能服务器编程》10-11章

第10章 信号

  • 信号是由用户、系统或者进程发送给目标进程的信息,以通知目标进程某个状态的改变或系统异常
  • Linux信号可由如下条件产生
    • 对于前台进程,可以通过输入特殊的终端字符来给它发送信号;
    • 系统异常、系统状态变化;
    • 运行kill命令或调用kill函数;
  • Linux定义的信号值都大于0

Linux信号概述

  • 发送及接收信号
    • Linux下,一个进程给其他进程发送信号的API是kill函数

      #include<sys/types.h>
      #include<signal.h>
      int kill(pid_t pid,int sig);//该函数把信号sig发送给目标进程pid
      
      • 成功时返回0,失败则返回-1并设置errno;
    • Linux下,目标进程收到信号时使用的信号处理函数原型

      #include<signal.h>
      typedef void(*__sighandler_t)(int);
      
      #include<bits/signum.h>
      #define SIG_DFL((__sighandler_t)0) //SIG_DFL表示使用信号的默认处理方式
      #define SIG_IGN((__sighandler_t)1) //SIG_IGN表示忽略目标信号
      
  • Linux信号
    • Linux的可用信号都定义在bits/signum.h头文件中,其中包括标准信号和POSIX实时信号;
    • 重点关注与网络编程关系紧密的几个信号:SIGHUP、SIGPIPE和SIGURG
  • 中断系统调用
    • 如果程序在执行处于阻塞状态的系统调用时接收到信号,并且设置了信号处理函数,则默认情况下系统调用将被中断,并且errno被设置为EINTR,可以为信号设置SA_RESTART标志自动重启被该信号中断的系统调用
    • 对于默认行为是暂停进程的信号(比如SIGSTOP、SIGTTIN),如果我们没有为它们设置信号处理函数,则它们也可以中断某些系统调用(比如connect、epoll_wait);

信号函数

  • 要为一个信号设置处理函数,可以使用signal系统调用

    #include<signal.h>
    _sighandler_t signal(int sig,_sighandler_t_handler)
    
    • sig参数指出要捕获的信号类型;
    • _handler参数指定信号sig的处理函数;
  • 设置信号处理函数的更健壮的接口sigaction系统调用

    #include<signal.h>
    int sigaction(int sig, const struct sigaction* act, struct sigaction* oact);
    
    • sig参数指出要捕获的信号类型,act参数指定新的信号处理方式,oact参数则=输出信号先前的处理方式(如果不为NULL的话);
    • sigaction结构体描述了信号处理的细节,如信号处理函数,进程掩码,信号集,收到信号后的行为等;

信号集

  • 信号集函数
    #include<bits/sigset.h>
    #define_SIGSET_NWORDS(1024/(8*sizeof(unsigned long int)))
    typedef struct
    {
    unsigned long int__val[_SIGSET_NWORDS];
    }__sigset_t;
    
    • sigset_t实际上是一个长整型数组,数组的每个元素的每个位表示一个信号,这种定义方式和文件描述符集fd_set类似;
    • Linux提供了如下一组函数来设置、修改、删除和查询信号集:
    #include<signal.h>
    int sigemptyset(sigset_t*_set)/*清空信号集*/
    int sigfillset(sigset_t*_set)/*在信号集中设置所有信号*/
    int sigaddset(sigset_t*_set, int_signo)/*将信号_signo添加至信号集中*/
    int sigdelset(sigset_t*_set, int_signo)/*将信号_signo从信号集中删除*/
    int sigismember(_const sigset_t*_set, int_signo)/*测试_signo是否在信号集中*/
    
  • 进程信号掩码
    • 以指定哪些信号不能发送给本进程;

    • 可以利用sigaction结构体的sa_mask成员设置进程的信号掩码

    • 可以用sigprocmask函数设置或查看进程的信号掩码;

      #include<signal.h>
      int sigprocmask(int how, const sigset_t * set, sigset_t * oset;
      
      • set参数指定新的信号掩码,oset参数则输出原来的信号掩码,how参数指定设置进程信号掩码的方式(如交集、并集等);
  • 被挂起的信号
    • 如果给进程发送一个被屏蔽的信号,则操作系统将该信号设置为进程的一个被挂起的信号

    • 取消对被挂起信号的屏蔽,则它能立即被进程接收到;

    • 如下函数可以获得进程当前被挂起的信号集

      #include<signal.h>
      int sigpending(sigset_t*set);
      

统一事件源

  • 信号是一种异步事件:信号处理函数和程序的主循环是两条不同的执行路线;
  • 信号处理函数通常使用管道来将信号“传递”给主循环:信号处理函数往管道的写端写入信号值,主循环则从管道的读端读出该信号值;
  • 统一事件源:使用I/O复用系统调用来监听管道的读端文件描述符上的可读事件,信号事件就能和其他I/O事件一样被处理;

代码清单10-1 统一事件源

网络编程相关信号

  • SIGHUP
    • 挂起进程的控制终端时,SIGHUP信号将被触发;
    • 对于没有控制终端的网络后台程序而言,通常利用SIGHUP信号来强制服务器重读配置文件
  • SIGPIPE
    • 默认情况下,往一个读端关闭的管道或socket连接中写数据将引发SIGPIPE信号,程序接收到SIGPIPE信号的默认行为是结束进程;
    • 需要在代码中捕获并处理该信号,或者至少忽略它,否则将因错误的写操作而导致程序退出;
      • 引起SIGPIPE信号的写操作将设置errno为EPIPE
      • 使用send函数的MSG_NOSIGNAL标志来禁止写操作触发SIGPIPE信号;
      • 利用I/O复用系统调用来检测管道和socket连接的读端是否已经关闭;
  • SIGURG
    • 在Linux环境下,内核通知应用程序带外数据到达主要有两种方法:
      • I/O复用技术:select等系统调用在接收到带外数据时将返回并报告socket上的异常事件;
      • 使用SIGURG信号

第11章 定时器

  • 定时是指在一段时间之后触发某段代码的机制,可以在这段代码中依次处理所有到期的定时器;
  • 要将每个定时事件分别封装成定时器,然后放在定时器容器中并统一管理;

socket选项SO_RCVTIMEO和SO_SNDTIMEO

  • 用来设置socket接收数据超时时间发送数据超时时间,仅对与数据接收和发送相关的socket专用系统调用有效;

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nLVWcGQn-1668427955689)(img/Linux高性能服务器编程/image-20221028192620558.png)]

代码清单11-1 设置connect超时时间

SIGALRM信号

  • 由alarm和setitimer函数设置的实时闹钟一旦超时,将触发SIGALRM信号,可以利用该信号的信号处理函数来处理定时任务;
  • 一般SIGALRM信号按照固定的频率生成,即由alarm或setitimer函数设置的定时周期T保持不变,因此T反映了定时的精度;
  • 服务器程序通常要定期处理非活动连接:给客户端发一个重连请求,或者关闭该连接,或者其他;
    • 可以通过socket选项KEEPALIVE来激活;
    • 或利用定时器在应用层实现类似的机制,以管理所有长时间处于非活动状态的连接;

I/O复用系统调用的超时参数

  • Linux下的3组I/O复用系统调用都带有超时参数,因此它们不仅能统一处理信号和I/O事件,也能统一处理定时事件;

代码清单11-4 利用I/O复用系统调用定时

高性能定时器

  • 时间轮

    • (实线)指针指向轮子上的一个槽(slot),它以恒定的速度顺时针转动,每转动一步就指向下一个槽;
    • 每次转动称为一个滴答(tick)。一个滴答的时间称为时间轮的槽间隔si(slot interval),即心搏时间;
    • 每个槽指向一条定时器链表,每条链表上的定时器具有相同的特征:它们的定时时间相差N*si的整数倍,时间轮正是利用这个关系将定时器散列到不同的链表中,插入操作的效率更高;
  • 时间堆
    • 将所有定时器中超时时间最小的一个定时器的超时值作为心搏间隔,一旦心搏函数tick被调用,超时时间最小的定时器必然到期,就可以在tick函数中处理该定时器;
    • 最小堆很适合处理这种定时方案;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值