一、SIGHUP信号处理
- 信号产生的情景:
- 1.如果终端接口检测到一个连接断开,则将此信号送给与该终端相关的控制进程(会话首进程)
- 此信号被送给session结构中s_leader字段所指向的进程。仅当终端的CLOCAL标志没有设置时,在上述条件下才产生此信号(如果所连接的终端是本地的,则设置该终端的CLOCAL标志。它告诉终端驱动程序忽略所有调制解调器的状态行)
- 注意:接到此信号的会话首进程可能在后台。这区别于由终端正常产生的几个信号(中断、退出和挂起),这些信号总是传递给前台进程组
- 2.如果会话首进程终止,也产生此信号,在此情况下,此信号送给前台进程组的每一个进程
- 程序对此信号的默认动作为终止程序
- 通常用此信号通知守护进程再次读取它们的配置文件。选用SIGHUP的理由是,守护进程不会有控制终端,通常绝不会接收到这种信号。一个典型的例子就是xinetd超级服务程序(见下面的演示案例)
演示案例(xinetd超级服务程序)
- xinetd程序在接收到SIGHUP信号之后将调用hard_reconfig函数,它循环读取/etc/xinetd.g/目录下的每个子配置文件,并检测其变化
- 如果某个正在运行的子服务的配置文件被修改以停止服务,则xinetd主程序将给该子服务进程发送SIGTERM信号以结束它。如果某个子服务的配置文件被修改以开启服务,则xinetd将创建新的socket并将其绑定到该服务对应的端口上
- 现在来分析xinetd处理SIGHUP信号的流程。下面是检测机器的环境:
- 从ps的输出可以看出,xinetd创建子进程7442,它运行echo-stream内部服务
- 从lsof输出来看,xinetd打开了一个管道。该管道的读端文件描述符的值是3,写端文件描述符的值是4(这个就是我们前面介绍的统一事件源)
![](https://i-blog.csdnimg.cn/blog_migrate/e90b13217b73276b73075a280f80c4bc.png)
- 现在我们修改/etc/xinetd.d/目录下的部分配置文件,并给xinetd发送一个SIGHUP信号,操作如下:
![](https://i-blog.csdnimg.cn/blog_migrate/9ac1b7a4328ab62724a38b761553894b.png)
- 我们使用streace命令跟踪程序执行时调用的系统调用和接收到的信号。此处我们根据进程7438(即xinetd服务器程序),输出内容如下,每个部分用空行隔开,共4个部分:
- 第一部分:描述程序接收到SIGHUP信号时,信号处理函数使用管道通知主程序该信号的到来。信号处理函数往文件描述符4(管道的写端)写入信号值1(SIGHUP信号),而主程序使用poll检测到文件描述符3(管道的读端)上有可读事件,就将管道上的数据读入
- 第二部分:描述符了xinetd重新读取一个子配置文件的过程
- 第三部分:描述了xinetd给子进程echo-stream(PID为7442)发送SIGTERM信号来终止该子进程,调用waitpid来等待子进程结束
- 第四部分:描述了xinetd启动telnet服务的过程:创建一个流服务socket并将其绑定到端口23上,然后监听该端口
![](https://i-blog.csdnimg.cn/blog_migrate/a54e1600aa7e82353e2f55e521844146.png)
二、SIGPIPE信号处理
- 信号产生的情景:
- 如果在管道的读进程已终止时写管道,则产生此信号
- 当类型为SOCK_STREAM的套接字已不再连接时,进程写该套接字也产生此信号
- 程序对此信号的默认动作为终止程序
- 引起SIGPIPE信号的写操作将设置errno为EPIPE
- 信号处理:
- 我们可以使用send函数的MSG_NOSIGNAL标志来禁止写操作触发SIGPIPE信号
- 我们也可以根据errno值来判断管道或者socket连接的读端是否已经关闭
- 我们也可以根据I/O复用系统来检测管道和socket连接的读端是否已经关闭。以poll为例,当管道的读端关闭时,写端文件描述符上的POLLHUP事件将被触发;当socket连接对对方关闭时,socket上的POLLRDHUP事件将被触发
三、SIGURG信号处理
演示案例(处理带外数据)
//sigurg.cpp
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <libgen.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#define LISTEN_NUM 5
#define BUFFER_SIZE 1024
static int client_fd; //客户端的fd
void add_signal(int signal_no,void (*sig_handler)(int signal_no));
void sig_handler(int signal_no);
int main(int argc,char* argv[])
{
if(argc!=3){
printf("usage:./%s [server ip] [server port]\n",basename(argv[1]));
exit(EXIT_FAILURE);
}
const char* ip;
int server_fd,port;
ip=argv[1];
port=atoi(argv[2]);
//创建套接字
if((server_fd=socket(AF_INET,SOCK_STREAM,0))==-1){
perror("socket");
exit(EXIT_FAILURE);
}
//初始化服务器地址
struct sockaddr_in server_address;
bzero(&server_address,sizeof(server_address));
server_address.sin_family=AF_INET;
server_address.sin_port=htons(port);
if(inet_pton(AF_INET,ip,&server_address.sin_addr)==-1){
perror("inet_pton");
exit(EXIT_FAILURE);
}
//绑定服务端地址
if(bind(server_fd,(struct sockaddr*)&server_address,sizeof(server_address))==-1){
perror("bind");
exit(EXIT_FAILURE);
}
//开启监听
if(listen(server_fd,LISTEN_NUM)==-1){
perror("listen");
exit(EXIT_FAILURE);
}
//接受客户端连接
struct sockaddr_in client_address;
socklen_t client_address_len=sizeof(client_address);
bzero(&client_address,sizeof(client_address));
if((client_fd=accept(server_fd,(struct sockaddr*)&client_address,&client_address_len))==-1){
perror("accept");
exit(EXIT_FAILURE);
}
else{
//添加SIGURG信号处理函数
add_signal(SIGURG,sig_handler);
//使用SIGURG之前,必须设置socket的宿主进程或进程组
fcntl(client_fd,F_SETOWN,getpid());
int recv_ret_value;
char recv_buffer[BUFFER_SIZE];
//接收普通数据
while(1)
{
bzero(recv_buffer,sizeof(recv_buffer));
//接收数据
recv_ret_value=recv(client_fd,recv_buffer,sizeof(recv_buffer-1),0);
if(recv_ret_value<0){
perror("recv");
close(client_fd);
close(server_fd);
exit(EXIT_FAILURE);
}
else if(recv_ret_value==0){
break;
}
else{
printf("recv normal data:%s ,%d bytes\n",recv_buffer,recv_ret_value);
}
}
close(client_fd);
}
close(server_fd);
exit(EXIT_SUCCESS);
}
void add_signal(int signal_no,void (*sig_handler)(int signal_no))
{
struct sigaction act;
act.sa_handler=sig_handler;
sigemptyset(&act.sa_mask);
act.sa_flags=0;
//对信号进行处理
sigaction(signal_no,&act,NULL);
}
void sig_handler(int signal_no)
{
//在这个信号处理函数中,我们接收带外数据
int save_errno=errno;
int recv_ret_value;
char recv_buffer[BUFFER_SIZE];
bzero(recv_buffer,sizeof(recv_buffer));
//接收带外数据
recv_ret_value=recv(client_fd,recv_buffer,sizeof(recv_buffer-1),0);
printf("recv oob data:%s ,%d bytes\n",recv_buffer,recv_ret_value);
errno=save_errno;
}
![](https://i-blog.csdnimg.cn/blog_migrate/5141b23a931bef78bd6c0480082ccd25.png)