信号、服务器崩溃

信号(signal)就是告知某个进程发生了某个事件的通知,有时也成为软件中断。信号通常是异步发生的,也就是说进程预先不知道信号的准确发生时刻。
信号可以:
由一个进程发送给另一个进程
由内核发送给某个进程。
上一节结尾提到的SIGCHLD信号就是由内核在任何一个进程终止时发送给它的父进程的一个信号。
每个信号都有一个与之关联的处置,也称为行为。我们通过调用sigaction函数来设定一个信号的处置,并有三种选择。
(1)我们可以提供一个函数,只要有特定信号发生它就被调用。这样的函数称为信号处理函数,这种行为称为 捕抓信号。有两种信号不能被捕抓,它们是SIGKILL和SIGSTOP。信号处理函数由信号值这个单一的整数参数来调用,且没有返回值,其函数原型因此如下:
void handler(int signo);
对于大多数信号来说,调用sigaction函数并指定信号发生时调用的函数就是捕获信号所需做的全部工作。不过我们稍后将看到,SIGIO,SIGPOLL和SIGURG这些个别信号还要求捕获它们的进程做些额外操作。
(2)我们可以把某个信号的处置设定为SIG_IGN来忽略它,SIGKILL和SIGSTOP这两个信号不能忽略。
(3)我们可以把某个信号的处置设定为SIG_DFL来启动默认的处置。默认处置通常是在收到信号后终止进程,其中某些信号还在当前工作目录产生一个进程的核心映像。另有个别信号的默认处置是忽略的,SIGCHID和SIGURG(带外来数据到达时发送)。

signal函数
建立信号处置的POSIX方法就是调用sigaction函数。不过这有点复杂,因为该函数的参数之一是我们必须分配并填写的结构。简单些的方法就是调用signal函数,其第一个参数是信号名,第二个参数或为指向函数的指针,或为常量SIG_IGN或SIG_DFL。然而signal是早于POSIX出现的历史悠久的函数。调用它时,不同实现提供不同的信号语义以达成后向兼容,而POSIX则明确规定了调用sigaction函数,这就以所期望的POSIX语义提供一个简单的接口,我们把该函数以及早先讲过的e

POSIX系统上的信号处理总结:
1、一旦安装了信号处理函数,它便一直安装着
2、在一个信号处理函数运行期间,正被递交的信号是阻塞的。而且,安装处理函数时在传递给sigaction函数的sa_mask信号集中指定的任何信号也被阻塞。我们设置sa_mask置为空集,意味着除了被捕获的信号外,没有额外信号被阻塞。
3、如果一个信号在被阻塞期间产生一次或多次,那么该信号被解除阻塞之后通常只递交一次,也就是说UNIX信号默认不排队。
4、利用sigprocmask函数选择性阻塞或解阻塞一组信号是可能的。这使得我们可以做到一段临界区代码执行期间,防止捕获某些信号,以此保护这段代码。

处理SIGCHLD信号
设置僵尸状态的目的是维护子进程的信息,以便父进程在以后某个时候获取。这些信息包括子进程的进程ID、终止状态以及资源利用信息(CPU时间、内存使用量等等)。如果一个进程终止,而该进程有子进程处于僵尸状态,那么它的所有僵尸子进程的父进程的ID将被重置为1(init进程)。继承这些子进程的init进程将清理它们(也就是说init进程将wait它们,从而去除它们的僵尸状态)。

处理僵尸进程
我们显然不愿意留存僵尸进程。它们占用的内核中的空间,最终可能导致我们耗尽进程资源,无论何时我们fork子进程都得wait他们,以防它们变成僵死进程。为此我们建立一个俘获SIGCHLD信号的信号处理函数,在函数体中我们调用wait。在listen之后增加如下调用
signal(SIGCHLD,sig_chld);
我们就建立了该信号处理函数。这必须在fork第一个子进程之前完成,且只做一次。我们接着定义名为sig_chld的这个信号处理函数

服务器主机崩溃
我们接着查看当服务器主机崩溃时就发生什么,为了模拟这种情形,我们必须在不同的主机上运行客户和服务器。我们先启动服务器,再启动客户,接着在客户上键入一行文本以确认连接工作正常,然后从网络上断开服务器主机,并在客户上键入另一行文本。这样同时也模拟了当前客户发送数据时服务器主机不可达的情形(即建立连接后某些中间路由器不工作)
步骤如下所述
(1)当服务器主机崩溃时,已有的网络连接上不发出任何东西。这里我们假设主机崩溃,而不是由操作员操作不当执行命令关机。
(2)我们在客户上键入一行文本,它由writen写入内核,再由客户TCP作为一个数据分节送出。客户随后阻塞于readline调用,等待回射的应答。
(3)如果我们用tcpdump观察网络就会发现,客户TCP持续重传数据分节,试图从服务器上接收一个ACK。TCP重传一个典型模式:重传该数据12次,共等待9分钟才放弃重传。当客户TCP最后终于放弃时(假设在这段时间內,服务器主机没有重新启动,或者如果是服务器主机未崩溃但是网络上不可达,那么假设主机仍然不可达),给客户进程返回一个错误。既然客户阻塞在readline调用上,该调用将返回一个错误。假设服务器主机已崩溃,从而对客户的数据分节根本没有响应,那么所返回的错误是ETIMEDOUT。然而如果某个中间路由器判定服务器主机已不可达,从而响应一个destination unreadable“(目的地不可达)ICMP消息,那么所返回的错误时EHOSTUREACH或ENETUNREACH。
尽管我们的客户最终还是会发现对端主机已崩溃或不可达,不过有时候我们需要比不得不等待9分钟更快地检测出这种情况。所用的方法就是对readline调用设置一个超时。
我们刚讨论的轻型纸由在我们向服务器主机发生数据时才能检测出它自己已经崩溃。如果我们不主动向它发送数据也想检查出服务器主机的崩溃,那么需要采用另外一个技术,也就是SO_KEEPALIVE套接字选项。

服务器主机崩溃后重启
在这种情形中,我们先在客户与服务器之间建立连接,然后假设服务器主机崩溃并重启。前一节中,当我们发送数据时,服务器主机仍然处于崩溃状态,本节中,我们将在发送数据前重启启动已经崩溃的服务器主机。模拟这种情形最简单就是,先建立连接,再从网络上断开服务器主机,将它关机后再重新启动,最后把它重新连接到网络中。我们不想客户知道服务器主机的关闭。
正如前一节所述,如果在服务器主机崩溃时客户不主动给服务器发送数据,那么客户将不会知道服务器主机已经崩溃。(这里假设我们没有使用SO_KEPPALIVE套接字选项)。所发生的步骤如下:
1)我们启动服务器和客户,并在客户键入一行文本以确认连接已经建立
2)服务器主机崩溃和重启
3)在客户上键入一行文本,它将作为一个TCP数据分节发送到服务器主机
4)当服务器主机崩溃后重启时,它的TCP丢失了崩溃前的所有连接信息,因此服务器TCP对于所收到的来自客户的数据分节响应一个RST。
5)当客户TCP收到该RST时,客户正阻塞与readline调用,导致该调用返回ECONNREST错误。
如果对客户而言检查服务器主机是否崩溃很重要,即使客户不主动发送数据也要能检测出来,就需要采用其他某种技术

服务器主机关机
前面两节讨论了服务器主机崩溃或无法通过网络到达的情形。本节我们考虑服务器进程正在运行时,服务器主机被操作员关机将会发送什么。
Unix系统关机时,init进程通常先给所有进程发送SIGTERM信号(该信号可被捕获),等待一段固定的时间(往往在5到20秒之间),然后给所有仍在运行的进程发送一个SIGKILL信号(该信号不能捕获)。这么做留给所有运行进程的一小段时间来清除和终止。如果我们不捕获SIGTREM并终止,我们的服务器将由SIGKILL信号终止,当服务器子进程终止时,它的所有打开着的描述符都被关闭,随后我们必须在客户中使用select、poll函数,是的服务器进程的终止一经发生,客户就能检测到。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值