Linux-信号处理


信号是在软件层次上对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。信号是异步的,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达。

信号是进程间通信机制中唯一的异步通信机制,可以看作是异步通知,通知接收信号的进程有哪些事情发生了。

每个信号都有一个名字,以SIG打头。常见的SIGINT( 终端终止符)、SIGABRT(异常终止,abort()产生)、SIGUSR1(用户定义的信号)等。

当信号产生,有三种方式处理该信号

  1.  忽略信号,两种信号(SIGKILL,SIGSTOP不能忽略)
  2. 捕捉信号,用户可以自己定义函数用于处理该信号。
  3. 执行系统默认动作,一般默认动作是终止进程
    在某些情况下,我们需要捕捉信号。比如说,之前编写的echo服务器,服务端按ctrl+c产生SIGINT信号,终止服务端程序。但是某些时候,我需要在终止程序前,做一些善后工作,如释放申请的内存空间,以免造成内存泄露。所以信号捕捉是有用的。

    可以使用signal为信号指定信号处理函数,也可以使用sigaction。现在推荐使用sigaction。

  1. #include <signal.h> //信号头文件   
  2. typedef void (*__sighandler_t) (int);  
  3. __sighandler_t signal (int __sig, __sighandler_t __handler);  
signal有两个参数,一个是信号量,另一个是函数指针。

    下面以echo服务器,来说明如何自定义信号处理函数。

  1. #include <stdio.h>   
  2. #include <stdlib.h>   
  3. #include <sys/socket.h>   
  4. #include <string.h>   
  5. #include <errno.h>   
  6. #include <netinet/in.h>   
  7. #include <arpa/inet.h>   
  8. #include <signal.h> //信号头文件   
  9.   
  10. #define SERVERIP "192.168.0.23"   
  11. #define SERVERPORT 12345   
  12. #define MAXBUFFER 256   
  13.   
  14. int serverFd=-1, connfd=-1;  
  15. char *readBuf=NULL;  
  16. /* 
  17. *功能:SIGINT自定义处理函数,用于释放申请的内存空间 
  18. */  
  19. void handInt(int signo)  
  20. {  
  21.     printf("捕捉到SIGINT信号,服务端被终止,善后处理中...\n");  
  22.     if (readBuf != NULL)  
  23.     {  
  24.         free(readBuf);  
  25.         readBuf = NULL;  
  26.     }  
  27.     close(serverFd);  
  28.     close(connfd);  
  29.     exit(-1);  
  30. }  
  31.   
  32. int main(int argc, char** argv)  
  33. {  
  34.     int ret;  
  35.     readBuf=(char *)malloc(MAXBUFFER);  
  36.     socklen_t len;  
  37.     struct sockaddr_in serveraddr,clientaddr;      
  38.     char ip[40]={0};  
  39.     serverFd=socket(AF_INET,SOCK_STREAM,0);//创建socket   
  40.     if(serverFd < 0)  
  41.     {  
  42.         printf("socket error:%s\n",strerror(errno));  
  43.         exit(-1);  
  44.     }  
  45.     bzero(&serveraddr,sizeof(serveraddr));  
  46.     serveraddr.sin_family=AF_INET;  
  47.     serveraddr.sin_port=htons(SERVERPORT);  
  48.     inet_pton(AF_INET,SERVERIP,&serveraddr.sin_addr);//将c语言字节序转换为网络字节序   
  49.   
  50.     if (signal(SIGINT, handInt) == SIG_ERR)//注册信号处理函数   
  51.     {  
  52.         printf("signal error: %s", strerror(errno));  
  53.         free(readBuf);  
  54.         exit(-1);  
  55.     }   
  56.     ret=bind(serverFd,(struct sockaddr *)&serveraddr,sizeof(serveraddr));//绑定IP和端口   
  57.     if(ret!=0)  
  58.     {  
  59.         close(serverFd);  
  60.         printf("bind error:%s\n",strerror(errno));  
  61.         exit(-1);  
  62.     }  
  63.     ret=listen(serverFd,5);//监听   
  64.     if(ret!=0)  
  65.     {  
  66.        close(serverFd);  
  67.        printf("listen error:%s\n",strerror(errno));  
  68.        exit(-1);  
  69.     }  
  70.     len=sizeof(clientaddr);  
  71.     bzero(&clientaddr,sizeof(clientaddr));  
  72.     while (1)  
  73.     {  
  74.         connfd = accept(serverFd, (struct sockaddr *) &clientaddr, &len);//接受客户端的连接   
  75.         printf("%s 连接到服务器 \n",inet_ntop(AF_INET,&clientaddr.sin_addr,ip,sizeof(ip)));  
  76.         if (serverFd < 0)  
  77.         {  
  78.             printf("accept error : %s\n", strerror(errno));  
  79.             continue;  
  80.         }  
  81.         while((ret=read(connfd,readBuf,MAXBUFFER)))//读客户端发送的数据            
  82.         {  
  83.             write(connfd,readBuf,MAXBUFFER);//写回客户端   
  84.             bzero(readBuf,MAXBUFFER);  
  85.         }  
  86.         if(ret==0)  
  87.         {  
  88.             printf("客户端关闭连接\n");           
  89.         }else  
  90.         {  
  91.             printf("read error:%s\n",strerror(errno));  
  92.         }  
  93.         close(connfd);  
  94.     }  
  95.     free(readBuf);  
  96.     close(serverFd);  
  97.     return 0;  
  98. }  
当没有捕捉SIGINT信号时,采用默认处理方式,终止程序。但是程序中用alloc分配了内存,程序终止前并没有释放。用valgrind 检测会出现内存泄露


如图所示,分配了一个内存空间,释放了0个内存空间。

但为SIGINT加上信号处理函数,在终止前,释放掉内存空间,就不会出现内存泄露了。效果如下所示:

相关内容