linux下處理信號範例

最先處理信號是用signal函數簡單處理的,但是我們需要做的是商業代碼,下面的2個範例,基本涵蓋了商業代碼中信號的使用.

信號中信號集的概念.
目前的信號集一共有32個

0000000000 0000000000 0000000000 00
0000000000 0000000000 0000000000 00

規則:
1.如果信號來了,就將對應的二進制位設置爲1,當執行完信號函數後,對應的二進制位歸爲0。

2.正在執行對應的信號函數,再來的信號,無論多少個都歸爲一個,在上一個信號處理完後再進行處理.

3.不同的信號來了後,如果後面的信號來的時候,前面的還沒有執行完,那麼會打斷前面的。

/**
 * 信號編程範例
 * 最新版本的信號編程範例. 
 * 
 * 
 * 同一個信號多次進來,必須前面的信號都處理完了,後續的信號纔會處理。
 * 同一個信號多次進來,前面的信號還在處理,後面的多個信號都會歸類爲一個信號.
 */

#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>



int main()
{

    //一.信號集.
    //一個進程,必須能夠記住 當前進程阻塞了哪些進程.
    //000000000000000000000000000
    //我們需要信號集的這麼一種數據類型,能夠把這60多個信號都裝下.

    // linux 是用sigset_t類型來表示信號集的.

    // 信號機函數相關
    // sigemptyset(); ---清空信號集,把所有二進制位全部都清爲0.
    // sigfillset(); ---全部設置爲1.

    // sigaddset() sigdelset() 往信號集增加信號/刪除信號.

    sigset_t newmask,oldmask,pendmask;

    sigemptyset(&newmask);  //將信號集清0.

    // 這個函數是要屏蔽嗎? 表示以後我不想收到這個消息??
    sigaddset(&newmask,SIGQUIT);        //將newmask信號集的的SIGQUIT信號位設置爲1,再來SIGQUIT信號時,進程不再處理.

    // 怎麼把我的信號集給當前的進程呢?

    // sigprocmask() --設置當前進程所對應的信號集.
    if(sigprocmask(SIG_BLOCK,&newmask,&oldmask) < 0)        // 第一個參數用了SIG_BLOCK表明設置進程 新的信號採用屏蔽阻塞的方式
    {                                                       // 將進程綁定當前的信號機
        exit(1);
    }

    if (sigismember(&newmask,SIGQUIT))  //檢測某個信號是否在信號集中被屏蔽了.
    {
        printf("SIGQUIT 信號被屏蔽了! \n");
    }
    else
    {
        printf("SIGQUIT 信號沒有被當前的信號集屏蔽! \n");
    }
    


    return 0;
}

上面的操作是屏蔽和允許信號的操作,下面的範例是綁定信號操作對應的函數.

/**
 * 信號處理--用最新的辦法來進行處理
 * 處理子進程退出後的信號方法
 */

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>


//和設置標題有關的全局量
char **g_os_argv;            //原始命令行參數數組,在main中會被賦值
char *gp_envmem = NULL;      //指向自己分配的env環境變量的內存
int  g_environlen = 0;       //環境變量所佔內存大小

//設置可執行程序標題相關函數:分配內存,並且把環境變量拷貝到新內存中來
void ngx_init_setproctitle()
{    
    int i;
    //統計環境變量所佔的內存。注意判斷方法是environ[i]是否爲空作爲環境變量結束標記
    for (i = 0; environ[i]; i++) 
    {
        g_environlen += strlen(environ[i]) + 1; //+1是因爲末尾有\0,是佔實際內存位置的,要算進來
    } //end for

    //這裏無需判斷penvmen == NULL,有些編譯器new會返回NULL,有些會報異常,但不管怎樣,如果在重要的地方new失敗了,你無法收場,讓程序失控崩潰,助你發現問題爲好; 
    gp_envmem = new char[g_environlen]; 
    memset(gp_envmem,0,g_environlen);  //內存要清空防止出現問題

    char *ptmp = gp_envmem;

    //把原來的內存內容搬到新地方來
    for (i = 0; environ[i]; i++) 
    {
        size_t size = strlen(environ[i])+1 ; //不要拉下+1,否則內存全亂套了,因爲strlen是不包括字符串末尾的\0的
        strcpy(ptmp,environ[i]);      //把原環境變量內容拷貝到新地方【新內存】
        environ[i] = ptmp;            //然後還要讓新環境變量指向這段新內存
        ptmp += size;
    }
    return;
}

//設置可執行程序標題
void ngx_setproctitle(const char *title)
{
    //我們假設,所有的命令 行參數我們都不需要用到了,可以被隨意覆蓋了;
    //注意:我們的標題長度,不會長到原始標題和原始環境變量都裝不下,否則怕出問題,不處理
    
    //(1)計算新標題長度
    size_t ititlelen = strlen(title); 

    //(2)計算總的原始的argv那塊內存的總長度【包括各種參數】
    size_t e_environlen = 0;     //e表示局部變量吧
    for (int i = 0; g_os_argv[i]; i++)  
    {
        e_environlen += strlen(g_os_argv[i]) + 1;
    }

    size_t esy = e_environlen + g_environlen; //argv和environ內存總和
    if( esy <= ititlelen)
    {
        //你標題多長啊,我argv和environ總和都存不下?注意字符串末尾多了個 \0,所以這塊判斷是 <=【也就是=都算存不下】
        return;
    }

    //空間夠保存標題的,夠長,存得下,繼續走下來    

    //(3)設置後續的命令行參數爲空,表示只有argv[]中只有一個元素了,這是好習慣;防止後續argv被濫用,因爲很多判斷是用argv[] == NULL來做結束標記判斷的;
    g_os_argv[1] = NULL;  

    //(4)把標題弄進來,注意原來的命令行參數都會被覆蓋掉,不要再使用這些命令行參數,而且g_os_argv[1]已經被設置爲NULL了
    char *ptmp = g_os_argv[0]; //讓ptmp指向g_os_argv所指向的內存
    strcpy(ptmp,title);
    ptmp += ititlelen; //跳過標題

    //(5)把剩餘的原argv以及environ所佔的內存全部清0,否則會出現在ps的cmd列可能還會殘餘一些沒有被覆蓋的內容;
    size_t cha = esy - ititlelen;  //內存總和減去標題字符串長度(不含字符串末尾的\0),剩餘的大小,就是要memset的;
    memset(ptmp,0,cha);  
    return;
}

//////// 信號處理模塊 ///////////////////
//////////// 一個進程一個信號處理模塊 /////////////////////
typedef struct 
{
    int         signo;
    const char *signame;

    //信號處理函數
    void (*handler)(int signo,siginfo_t *siginfo,void*ucontext);
} ngx_signal_t;

//聲明信號處理函數
static void ngx_signal_handler(int signo,siginfo_t *siginfo,void *ucontext);


ngx_signal_t signals[] =  {
    // signo     signame       handler
    {SIGHUP,    "SIGHUP",           ngx_signal_handler},
    {SIGINT,    "SIGINT",           ngx_signal_handler},
    {SIGTERM,   "SIGTERM",          ngx_signal_handler},
    {SIGCHLD,  "SIGCHLD",          ngx_signal_handler},
    {SIGQUIT,   "SIGQUIT",          ngx_signal_handler},
    {SIGIO,     "SIGIO",            ngx_signal_handler},
    {SIGSYS,    "SIGYS,SIG_IGN",    NULL},
    {0,         NULL,               NULL}

};


/**
 * 設置進程的標題
 */
void ngx_setprocess_title(const char *title)
{
    size_t environlen = 0;
    for(int i = 0; environ[i] ; i++)
    {
        environlen = strlen(environ[i]) + 1;
    }

    size_t argvlen = 0;
    for(int i = 0 ; g_os_argv[i] ; i++)
    {
        argvlen += strlen(g_os_argv[i]) + 1;
    }

    size_t total = argvlen + environlen;
    if(total < strlen(title)) return ;


    char *ptmp = g_os_argv[0];
    strcpy(ptmp,title);

    ptmp += strlen(title);
    memset(ptmp,0,total-strlen(title));
}



int main(int argc,char *const *argv)
{

    g_os_argv = (char**)argv;
    

    //轉移環境變量
    size_t environlen = 0;
    for(int i = 0; environ[i] ; i++)
    {
        environlen = strlen(environ[i]) + 1;
    }

    char *env = new char [environlen];
    memset(env,0,environlen);

    char *ptmp = env;
    for(int i = 0 ; environ[i] ; i++)
    {
        size_t size = strlen(environ[i])+1; //這裏必須要+1,區分分割符的作用.
        strcpy(ptmp,environ[i]);
        environ[i] = ptmp;
        ptmp += size;
    }


    size_t argvlen = 0;
    for(int i = 0 ; i < argc ; i++)
    {
        argvlen += strlen(argv[i]) + 1;
    }

    size_t total = argvlen + environlen;


    //1個主進程,四個子進程
    for(int i = 0 ; i < 1 ; i++)
    {
        pid_t p  = fork();
        if(p == 0)
        {
            //子進程
            ngx_setprocess_title("nginx: worker process..");
            for(;;)
            {
                sleep(5);
            }
            exit(1); // 子進程退出.
        }
    }

    //ngx_init_setproctitle();    // 轉移環境變量

    ngx_setprocess_title("nginx: master process..");
    //ngx_setproctitle("nginx: master process..");


    // 初始化信號集
    ngx_signal_t        *sig;
    struct sigaction    sa;         //信號對應的處理函數,一個對象對應一個信號

    // 綁定信號對應的處理函數,來看下這個是如何實現處理的.
    for(sig = signals; sig->signo != 0 ; sig++) 
    {
        memset(&sa,0,sizeof(struct sigaction));

        if(sig->handler)
        {
            sa.sa_sigaction = sig->handler;
            sa.sa_flags = SA_SIGINFO;       //固定套路,具體參數的意義需要查下文檔.
        }
        else
        {
            sa.sa_handler = SIG_IGN; // 這些應該爲了兼容以前的設計.
        }
        
        //一個信號對應一個信號集?
        sigemptyset(&sa.sa_mask);       //清空信號集,信號集中的位都設置爲0,不屏蔽任何信號!

        //

        // 設置信號處理動作 --  一個信號對應一個信號集??? 不是多個信號對應一個信號集嗎.
        if ( sigaction(sig->signo,&sa,NULL)  == -1)
        {
            return -1;
        }
    }


    // 這樣寫沒有問題?????
    // 那麼信號之間就互相的不影響???  這裏需要進行測試和理解.

    //問題:
    // 1個信號對應一個信號集,還是說多個信號對應一個信號集?
    // 肯定可以實現多個信號對應一個信號集的,但是這個信號 處理函數的綁定確實有點操蛋,讓人有點看不明白.

    
    //非也,這個應該和3.5一起對應起來纔是合理的.


    // 這裏的寫法好像並不是特別的穩妥,sigaction指定信號對應的處理方法.

    for(;;)
    {
        sleep(5);
    }

    return 0;
}

// 對應的這個ucontext是在哪裏進行設置的呢?
static void ngx_signal_handler(int signo,siginfo_t *siginfo,void *ucontext)
{
    printf("信號來了!\n");

    if(signo == SIGCHLD)
    {
        printf("收到子進程退出的消息....\n");
    }
}
發佈了114 篇原創文章 · 獲贊 14 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章