linux-信號處理

    信號是在軟件層次上對中斷機制的一種模擬,在原理上,一個進程收到一個信號與處理器收到一箇中斷請求可以說是一樣的。信號是異步的,一個進程不必通過任何操作來等待信號的到達,事實上,進程也不知道信號到底什麼時候到達。

    信號是進程間通信機制中唯一的異步通信機制,可以看作是異步通知,通知接收信號的進程有哪些事情發生了。

    每個信號都有一個名字,以SIG打頭。常見的SIGINT( 終端終止符)、SIGABRT(異常終止,abort()產生)、SIGUSR1(用戶定義的信號)等。

   當信號產生,有三種方式處理該信號

  1.  忽略信號,兩種信號(SIGKILL,SIGSTOP不能忽略)
  2. 捕捉信號,用戶可以自己定義函數用於處理該信號。
  3. 執行系統默認動作,一般默認動作是終止進程
    在某些情況下,我們需要捕捉信號。比如說,之前編寫的echo服務器,服務端按ctrl+c產生SIGINT信號,終止服務端程序。但是某些時候,我需要在終止程序前,做一些善後工作,如釋放申請的內存空間,以免造成內存泄露。所以信號捕捉是有用的。

    可以使用signal爲信號指定信號處理函數,也可以使用sigaction。現在推薦使用sigaction。

#include <signal.h> //信號頭文件
typedef void (*__sighandler_t) (int);
__sighandler_t signal (int __sig, __sighandler_t __handler);
signal有兩個參數,一個是信號量,另一個是函數指針。

    下面以echo服務器,來說明如何自定義信號處理函數。

 

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <string.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h> //信號頭文件

#define SERVERIP "192.168.0.23"
#define SERVERPORT 12345
#define MAXBUFFER 256

int serverFd=-1, connfd=-1;
char *readBuf=NULL;
/*
*功能:SIGINT自定義處理函數,用於釋放申請的內存空間
*/
void handInt(int signo)
{
    printf("捕捉到SIGINT信號,服務端被終止,善後處理中...\n");
    if (readBuf != NULL)
    {
        free(readBuf);
        readBuf = NULL;
    }
    close(serverFd);
    close(connfd);
    exit(-1);
}

int main(int argc, char** argv)
{
    int ret;
    readBuf=(char *)malloc(MAXBUFFER);
    socklen_t len;
    struct sockaddr_in serveraddr,clientaddr;    
    char ip[40]={0};
    serverFd=socket(AF_INET,SOCK_STREAM,0);//創建socket
    if(serverFd < 0)
    {
        printf("socket error:%s\n",strerror(errno));
        exit(-1);
    }
    bzero(&serveraddr,sizeof(serveraddr));
    serveraddr.sin_family=AF_INET;
    serveraddr.sin_port=htons(SERVERPORT);
    inet_pton(AF_INET,SERVERIP,&serveraddr.sin_addr);//將c語言字節序轉換爲網絡字節序

    if (signal(SIGINT, handInt) == SIG_ERR)//註冊信號處理函數
    {
        printf("signal error: %s", strerror(errno));
        free(readBuf);
        exit(-1);
    } 
    ret=bind(serverFd,(struct sockaddr *)&serveraddr,sizeof(serveraddr));//綁定IP和端口
    if(ret!=0)
    {
        close(serverFd);
        printf("bind error:%s\n",strerror(errno));
        exit(-1);
    }
    ret=listen(serverFd,5);//監聽
    if(ret!=0)
    {
       close(serverFd);
       printf("listen error:%s\n",strerror(errno));
       exit(-1);
    }
    len=sizeof(clientaddr);
    bzero(&clientaddr,sizeof(clientaddr));
    while (1)
    {
        connfd = accept(serverFd, (struct sockaddr *) &clientaddr, &len);//接受客戶端的連接
        printf("%s 連接到服務器 \n",inet_ntop(AF_INET,&clientaddr.sin_addr,ip,sizeof(ip)));
        if (serverFd < 0)
        {
            printf("accept error : %s\n", strerror(errno));
            continue;
        }
        while((ret=read(connfd,readBuf,MAXBUFFER)))//讀客戶端發送的數據         
        {
            write(connfd,readBuf,MAXBUFFER);//寫回客戶端
            bzero(readBuf,MAXBUFFER);
        }
        if(ret==0)
        {
            printf("客戶端關閉連接\n");         
        }else
        {
            printf("read error:%s\n",strerror(errno));
        }
        close(connfd);
    }
    free(readBuf);
    close(serverFd);
    return 0;
}
當沒有捕捉SIGINT信號時,採用默認處理方式,終止程序。但是程序中用alloc分配了內存,程序終止前並沒有釋放。用valgrind 檢測會出現內存泄露


如圖所示,分配了一個內存空間,釋放了0個內存空間。

但爲SIGINT加上信號處理函數,在終止前,釋放掉內存空間,就不會出現內存泄露了。效果如下所示:



其他內容,可以參考http://www.ibm.com/developerworks/cn/linux/l-ipc/part2/index1.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章