最先處理信號是用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");
}
}