UNP读书笔记第5章--POSIX信号处理

POSIX信号处理

信号就是告知某个进程发生了某个事件的通知,有时也称为软件中断.信号通常是异步发生的(指进程不知道信号发生的准确时刻)

信号可以:

  • 由一个进程发给另一个进程
  • 由内核发给某个进程

每个信号都有一个与之关联的处置,也称为行为。通过sigaction函数来设定一个信号的处置,并有三种选择:

  • 提供一个函数(称之为信号处理函数),只要有特定信号发生就调用。这种行为称之为捕获信号。SIGKILL和SIGSTOP不能被捕获。信号处理函数原型
void handler(int signo); // signo: 信号对应的信号值
  • 把某个信号的处置设定为SIGIGN来忽略它。同样,SIGKILL和SIGSTOP不能被忽略。
  • 把某个信号的处置设定为SIGDFL来启用它的默认处置。

符合POSIX的系统的信号处理为以下几点:

SIGCHLD信号

SIGCHLD信号的介绍

SIGCHLD信号是由内核在任何一个进程终止时发给它的父进程的一个信号。

子进程终止后,会进入僵死状态(zombie),目的是维护子进程的信息(包括子进程的pid,终止状态,资源利用信息等等),直至父进程获取这些信息后,才清理子进程占有的资源。如果一直不读取,子进程就一直处于僵死状态,占有内核空间。

(当一个进程终止时,而且其子进程中有僵死的,那么所有僵死的子进程的父进程ID将会设置为1,init进程,清理这些僵死进程的工作交由init进程完成,即init进程wait这些僵死进程)

SIGCHLD信号的处理

通过signal函数: 将SIGCHLD信号的处置设为sig_chld(调用wait函数获取已终止的子进程的信息)

signal(SIGCHLD, sig_chld);

在这里插入图片描述

这样,每当有子进程终止,该信号处理函数都会被调用,从而获取子进程的信息,避免僵死进程

处理被中断的系统调用

  • 慢调用:指那些可能永远阻塞的系统调用。(比如:read、write、accept等等)

  • 当阻塞于某个慢调用时,一个进程捕获某个信号并且相应处理函数返回时,该系统调用可能返回EINTR错误。

  • 关于于EINTR错误:ENITR错误时慢调用由于软中断(信号)所产生的错误。发生EINTR错误一般情况下是应该要重启该调用。(但是,这里举一个例外,connect函数,前面第4章提到,当connect函数失败时,不能对原来的sockfd重新调用connect,需要重新调用socket,对新的sockfd调用connect。)

如果进程在一个慢系统调用(slow system call)中阻塞时,当捕获到某个信号且相应信号处理函数返回时,这个系统调用不再阻塞而是被中断,就会调用返回错误(一般为-1)并且设置errno为EINTR(相应的错误描述为“Interrupted system call”)。

wait和waitpid

在这里插入图片描述

wait和waitpid均返回两个值:已终止的进程ID和statloc指向的状态信息。statloc通过以下几个宏来读取:

在这里插入图片描述
在这里插入图片描述

关于waitpid:当第一个参数pid设置为-1,其将会等待第一个终止的子进程。当第三个参数options为WNOHANG,不会阻塞调用,而是立即返回。

下面是改进SIGCHLD信号处理函数:

在这里插入图片描述

在这里插入图片描述

如上图有5个客户接服务器,从而产生5个服务器子进程。当五个客户同时向服务器(子进程)发送FIN分节,导致服务器子进程终止,五个服务器子进程同时向服务器进程发送SIGCHLD信号,而Unix信号是不排队的,从而导致之前设置的SIGCHLD信号处理函数调用次数小于5,然后产生僵死进程。

下面对信号处理函数进行改进:循环调用waitpid(指定WNOHANG)直至返回-1,这样可以保证当这个信号处理函数被调用时,会清理掉当前所有的僵死进程。

在这里插入图片描述

SIGPIPE信号

SIGPIPE产生的原因:如果一个 socket 在接收到了 RST packet 之后,程序仍然向这个 socket 写入数据,那么就会产生SIGPIPE信号

对于SIGPIPE信号的处理,一般就是忽略它。

在muduo中就是这样,假设:服务器和客户端真正进行友好交流,而客户端不辞而别,服务器后面在对这个套接字write时,会让服务端收到一个RST分节。如果

不理会,继续write,就会产生SIGPIPE信号。而SIGPIPE信号的默认行为是终止进程。这样就有点小题大作,只需忽略即可。

UNP书上的处理就不放这了。

数据格式

考虑一个场景,客户端往服务端发送一行文本(两个整数,之间用空格相隔,最后是换行符),服务端读取这行文本,计算两数之和,并发回客户端。

有两种传输方案可以选择:

  • 传递字符串
  • 传递二进制结构
struct arg
{
	int a;
	int b;
};

struct res
{
	long long result;
};

第一种方案可以,第二种由于客户端和服务端运行在不同的机器,而且对齐方式或者数据类型的位数不一样等等问题,经常失效.

两种解决方案

  1. 将所有数据转换为字符串来传输
  2. 显示定义所支持数据类型的二进制格式(位数,大小端).RPC就是这样的.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值