网络编程之linux常用信号处理

SIGPIPE信号

当往一个写端关闭的管道或socket连接中连续写入数据时会引发SIGPIPE信号。在TCP通信中,当通信的双方中的一方close一个连接时,若另一方接着发数据,根据TCP协议的规定,会收到一个RST响应报文,若再往这个已关闭的连接发送数据时,系统会发出一个SIGPIPE信号给进程,告诉进程这个连接已经断开了,不能再写入数据。
在这里插入图片描述
图片来自(https://blog.csdn.net/u010821666/article/details/81841755)

处理:

因为SIGPIPE信号的默认行为是结束进程,而我们绝对不希望因为写操作的错误而导致程序退出,尤其是作为服务器程序来说就更恶劣了。所以我们应该对这种信号加以处理,在程序中可以捕捉该信号,然后执行相应的处理程序。在这里,介绍三种处理SIGPIPE信号的方式
一、给SIGPIPE设置SIG_IGN信号处理函数,忽略该信号

// signum: 信号类型
// handler: 信号处理函数,是个函数指针
// 这个处理信号函数带有一个int型参数,并应返回void
void (*signal(int signum,void(* handler)(int)))(int);

// 例如 
signal(SIGPIPE, SIG_IGN); // SIG_IGN 忽略信号

引发SIGPIPE信号的写操作将设置errno为EPIPE。所以,第二次往关闭的socket中写入数据时, 会返回-1, 同时errno置为EPIPE. 这样,通过特殊的判断便能知道对端已经关闭,然后进行相应处理,而不会导致整个进程退出.

另一种设置信号处理函数的方法(该方法更稳健,常用)

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

signum参数指出要捕获的信号类型,act参数指定新的信号处理方式,oldact参数输出先前信号的处理方式(如果不为NULL的话)
使用该函数需要先理解struct sigaction结构体

struct sigaction {
    void (*sa_handler)(int);
    void (*sa_sigaction)(int, siginfo_t *, void *);
    sigset_t sa_mask;
    int sa_flags;
    void (*sa_restorer)(void);
}
  • sa_handler此参数和signal()的参数handler相同,代表新的信号处理函数
  • sa_mask 用来设置在处理该信号时暂时将sa_mask 指定的信号集搁置
  • sa_flags 用来设置信号处理的其他相关操作,下列的数值可用。
    • SA_RESETHAND:当调用信号处理函数时,将信号的处理函数重置为缺省值SIG_DFL
    • SA_RESTART:如果信号中断了进程的某个系统调用,则系统自动启动该系统调用
    • SA_NODEFER :一般情况下, 当信号处理函数运行时,内核将阻塞该给定信号。但是如果设置了 SA_NODEFER标记,那么在该信号处理函数运行时,内核将不会阻塞该信号sigaction

sigaction 实现忽略SIGPIPE信号

struct sigaction sa;
/* 设置信号忽略 */
memset(&sa, '\0', sizeof(sa));
sa.sa_handler = SIG_IGN; //这个地方也可以是其他处理函数(自定义也可)
// 如果信号中断了进程的某个系统调用,则系统自动启动该系统调用
sa.sa_flags |= SA_RESTART;
// sigfillset()用来将参数set信号集初始化,
// 把所有的信号加入到此信号集里
// 然后所有的信号标志位置为1,屏蔽所有的信号
sigfillset(&sa.sa_mask);
sigaction(SIGPIPE,&sa,NULL);

二、使用send函数的MSG_NOSIGNAL 标志来禁止写操作触发SIGPIPE信号。

send(sockfd , buf , size , MSG_NOSIGNAL);

我们可以根据send函数反馈的errno来判断socket的读端是否已经关闭;

三、EPOLLRDHUP
我们也可以通过IO复用函数来检测管道和socket连接的读端是否已经关闭。以EPOLL为例,当socket连接被对方关闭时,socket上的EPOLLRDHUP事件将被触发。

epoll_event event;
event.events = EPOLLIN | EPOLLRDHUP;

SIGALRM信号

他的作用是设置进程隔多久后会收到一个SIGALRM信号。通过alarm系统调用设置触发时间。(触发一次之后需要重新设置时间)

void handle_alarm() 
{ 
    exit(0); 
} 
int main(int argc, char *argv[]) 
{ 
    signal(SIGALRM, handle_alarm); 
    alarm(10);  // 10S触发SIGALRM信号,然后执行信号处理函数
    while(1) {} 
} 

SIGINT && SIGTERM && SIGKILL

SIGINT(ctrl+c):信号被当前进程树接收到,也就是说,不仅当前进程会收到信号,它的子进程也会收到

SIGTERM(kill {pid} ):只有当前进程收到信号,子进程不会收到。如果当前进程被kill了,那么它的子进程的父进程将会是init,也就是pid为1的进程

SIGKILL(kill -9 {pid}):与SIGTERM 不同的是,此信号不会被阻塞

其他信号

在这里插入图片描述

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值