信号函数

signal函数

头文件:#include <signal.h>

定义函数:void (*signal(int signum, void(* handler)(int)))(int);

函数说明:signal()会依参数signum 指定的信号编号来设置该信号的处理函数. 当指定的信号到达时就会跳转到参数handler 指定的函数执行. 如果参数handler 不是函数指针, 则必须是下列两个常数之一:
1、SIG_IGN 忽略参数signum 指定的信号.
2、SIG_DFL 将参数signum 指定的信号重设为核心预设的信号处理方式.

关于信号的编号和说明, 请参考附录D

返回值:返回先前的信号处理函数指针, 如果有错误则返回SIG_ERR(-1).

附加说明:在信号发生跳转到自定的 handler 处理函数执行后, 系统会自动将此处理函数换回原来系统预设的处理方式, 如果要改变此操作请改用sigaction().

sigaction函数

头文件:#include <signal.h>

定义函数:int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

函数说明:sigaction()会依参数signum 指定的信号编号来设置该信号的处理函数. 参数signum 可以指定SIGKILL 和SIGSTOP 以外的所有信号。如参数结构sigaction 定义如下:

1
2
3
4
5
6
7
struct sigaction
{
void (*sa_handler) (int);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer) (void);
}

1、sa_handler 此参数和signal()的参数handler 相同, 代表新的信号处理函数, 其他意义请参考signal().
2、sa_mask 用来设置在处理该信号时暂时将sa_mask 指定的信号搁置.
3、sa_restorer 此参数没有使用.
4、sa_flags 用来设置信号处理的其他相关操作, 下列的数值可用:
A_NOCLDSTOP: 如果参数signum 为SIGCHLD, 则当子进程暂停时并不会通知父进程
SA_ONESHOT/SA_RESETHAND: 当调用新的信号处理函数前, 将此信号处理方式改为系统预设的方式.
SA_RESTART: 被信号中断的系统调用会自行重启
SA_NOMASK/SA_NODEFER: 在处理此信号未结束前不理会此信号的再次到来. 如果参数oldact 不是NULL 指针, 则原来的信号处理方式会由此结构sigaction 返回.

返回值:执行成功则返回0, 如果有错误则返回-1.

错误代码:
1、EINVAL 参数signum 不合法, 或是企图拦截SIGKILL/SIGSTOPSIGKILL 信号。
2、EFAULT 参数act, oldact 指针地址无法存取。
3、EINTR 此调用被中断。

商业软件中,不用signal(),而要用sigaction();

sigaction结构体

1
2
3
4
5
6
7
struct sigaction
{
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t*, void*);
sigset_t sa_mask;
int sa_flags;
};
  • sa_handler:信号处理器函数的地址,亦或是常量SIG_IGNSIG_DFL之一。仅当sa_handler是信号处理程序的地址时,亦即sa_handler的取值在SIG_IGNSIG_DFL之外,才会对sa_masksa_flags字段加以处理。
  • sa_sigaction:如果设置了SA_SIGINFO标志位,则会使用sa_sigaction处理函数,否则使用sa_handler处理函数。
  • sa_mask:定义一组信号,在调用由sa_handler所定义的处理器程序时将阻塞该组信号,不允许它们中断此处理器程序的执行。
  • sa_flags:位掩码,指定用于控制信号处理过程的各种选项。
    • SA_NODEFER:捕获该信号时,不会在执行处理器程序时将该信号自动添加到进程掩码中。
    • SA_ONSTACK:针对此信号调用处理器函数时,使用了由sigaltstack()安装的备选栈。
    • SA_RESETHAND:当捕获该信号时,会在调用处理器函数之前将信号处置重置为默认值(即SIG_IGN)。
    • SA_SIGINFO:调用信号处理器程序时携带了额外参数,其中提供了关于信号的深入信息

初始化信号的函数

信号相关结构体,这里设置一个数组,方便后续根据信号名称调用对应的信号处理函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//一个信号有关的结构 ngx_signal_t
typedef struct
{
int signo; //信号对应的数字编号 ,每个信号都有对应的#define ,大家已经学过了
const char *signame; //信号对应的中文名字 ,比如SIGHUP

//信号处理函数,这个函数由我们自己来提供,但是它的参数和返回值是固定的【操作系统就这样要求】,大家写的时候就先这么写,也不用思考这么多;
void (*handler)(int signo, siginfo_t *siginfo, void *ucontext); //函数指针, siginfo_t:系统定义的结构
} ngx_signal_t;

//数组 ,定义本系统处理的各种信号,我们取一小部分nginx中的信号,并没有全部搬移到这里,日后若有需要根据具体情况再增加
//在实际商业代码中,你能想到的要处理的信号,都弄进来
ngx_signal_t signals[] = {
// signo signame handler
{ SIGHUP, "SIGHUP", ngx_signal_handler }, //终端断开信号,对于守护进程常用于reload重载配置文件通知--标识1
{ SIGINT, "SIGINT", ngx_signal_handler }, //标识2
{ SIGTERM, "SIGTERM", ngx_signal_handler }, //标识15
{ SIGCHLD, "SIGCHLD", ngx_signal_handler }, //子进程退出时,父进程会收到这个信号--标识17
{ SIGQUIT, "SIGQUIT", ngx_signal_handler }, //标识3
{ SIGIO, "SIGIO", ngx_signal_handler }, //指示一个异步I/O事件【通用异步I/O信号】
{ SIGSYS, "SIGSYS, SIG_IGN", NULL }, //我们想忽略这个信号,SIGSYS表示收到了一个无效系统调用,如果我们不忽略,进程会被操作系统杀死,--标识31
//所以我们把handler设置为NULL,代表 我要求忽略这个信号,请求操作系统不要执行缺省的该信号处理动作(杀掉我)
//...日后根据需要再继续增加
{ 0, NULL, NULL } //信号对应的数字至少是1,所以可以用0作为一个特殊标记
};

image-20220206195356189

这种信号处理的写法十分的固定,代码简单,但是注意一些小细节:

  1. 临时定义结构体sigaction sa;利用sa临时设置信号选项和信号的处理函数或者忽略该信号
  2. 用sa.sa_sigaction指定信号处理函数,sa.sa_flags指定信号选项
  3. 然后调用sigaction(sig->signo, &sa, NULL)信号处理动作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
//初始化信号的函数,用于注册信号处理程序
//返回值:0成功 ,-1失败
int ngx_init_signals()
{
ngx_signal_t *sig; //指向自定义结构数组的指针
struct sigaction sa; //sigaction:系统定义的跟信号有关的一个结构,我们后续调用系统的sigaction()函数时要用到这个同名的结构

for (sig = signals; sig->signo != 0; sig++) //将signo ==0作为一个标记,因为信号的编号都不为0;
{
//我们注意,现在要把一堆信息往 变量sa对应的结构里弄 ......
memset(&sa,0,sizeof(struct sigaction));

if (sig->handler) //如果信号处理函数不为空,这当然表示我要定义自己的信号处理函数
{
sa.sa_sigaction = sig->handler; //sa_sigaction:指定信号处理程序(函数),注意sa_sigaction也是函数指针,是这个系统定义的结构sigaction中的一个成员(函数指针成员);
sa.sa_flags = SA_SIGINFO; //sa_flags:int型,指定信号的一些选项,设置了该标记(SA_SIGINFO),就表示信号附带的参数可以被传递到信号处理函数中
//说白了就是你要想让sa.sa_sigaction指定的信号处理程序(函数)生效,你就把sa_flags设定为SA_SIGINFO
}
else
{
sa.sa_handler = SIG_IGN; //sa_handler:这个标记SIG_IGN给到sa_handler成员,表示忽略信号的处理程序,否则操作系统的缺省信号处理程序很可能把这个进程杀掉;
//其实sa_handler和sa_sigaction都是一个函数指针用来表示信号处理程序。只不过这两个函数指针他们参数不一样, sa_sigaction带的参数多,信息量大,
//而sa_handler带的参数少,信息量少;如果你想用sa_sigaction,那么你就需要把sa_flags设置为SA_SIGINFO;
} //end if

sigemptyset(&sa.sa_mask); //比如咱们处理某个信号比如SIGUSR1信号时不希望收到SIGUSR2信号,那咱们就可以用诸如sigaddset(&sa.sa_mask,SIGUSR2);这样的语句针对信号为SIGUSR1时做处理,
//这里.sa_mask是个信号集(描述信号的集合),用于表示要阻塞的信号,sigemptyset()这个函数把信号集中的所有信号清0,本意就是不准备阻塞任何信号;


//设置信号处理动作(信号处理函数),说白了这里就是让这个信号来了后调用我的处理程序,有个老的同类函数叫signal,不过signal这个函数被认为是不可靠信号语义,不建议使用,大家统一用sigaction
if (sigaction(sig->signo, &sa, NULL) == -1) //参数1:要操作的信号
//参数2:主要就是那个信号处理函数以及执行信号处理函数时候要屏蔽的信号等等内容
//参数3:返回以往的对信号的处理方式【跟sigprocmask()函数边的第三个参数是的】,跟参数2同一个类型,我们这里不需要这个东西,所以直接设置为NULL;
{
ngx_log_error_core(NGX_LOG_EMERG,errno,"sigaction(%s) failed",sig->signame); //显示到日志文件中去的
return -1; //有失败就直接返回
}
else
{
//ngx_log_error_core(NGX_LOG_EMERG,errno,"sigaction(%s) succed!",sig->signame); //成功不用写日志
//ngx_log_stderr(0,"sigaction(%s) succed!",sig->signame); //直接往屏幕上打印看看 ,不需要时可以去掉
}
} //end for
return 0; //成功
}

//信号处理函数
static void ngx_signal_handler(int signo, siginfo_t *siginfo, void *ucontext)
{
printf("来信号了\n");
}

注意void ngx_signal_handler(int signo, siginfo_t *siginfo, void *ucontext);信号处理函数是我们自己声明的,但是它的参数和返回值是固定的【操作系统就这样要求】,写的时候就先这么写,也不用思考这么多;

ngx_signal_t signals[]定义本系统处理的各种信号,我们取一小部分nginx中的信号,并没有全部搬移到这里,日后若有需要根据具体情况再增加,在实际商业代码中,你能想到的要处理的信号,都弄进来。

int ngx_init_signals()函数中设置信号为对应的处理函数或者忽略该信号。

1
2
3
4
5
6
if (sig->handler){
sa.sa_sigaction = sig->handler;
sa.sa_flags = SA_SIGINFO;
}else{
sa.sa_handler = SIG_IGN;
}

信号处理函数

这个信号处理函数根据ngx_process判断父子进程而对相同的信号执行不同的操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
static void ngx_signal_handler(int signo, siginfo_t *siginfo, void *ucontext)
{
//printf("来信号了\n");
ngx_signal_t *sig; //自定义结构
char *action; //一个字符串,用于记录一个动作字符串以往日志文件中写

for (sig = signals; sig->signo != 0; sig++) //遍历信号数组
{
//找到对应信号,即可处理
if (sig->signo == signo)
{
break;
}
} //end for

action = (char *)""; //目前还没有什么动作;

if(ngx_process == NGX_PROCESS_MASTER) //master进程,管理进程,处理的信号一般会比较多
{
//master进程的往这里走
switch (signo)
{
case SIGCHLD: //一般子进程退出会收到该信号
ngx_reap = 1; //标记子进程状态变化,日后master主进程的for(;;)循环中可能会用到这个变量【比如重新产生一个子进程】
break;

//.....其他信号处理以后待增加

default:
break;
} //end switch
}
else if(ngx_process == NGX_PROCESS_WORKER) //worker进程,具体干活的进程,处理的信号相对比较少
{
//worker进程的往这里走
//......以后再增加
//....
}
else
{
//非master非worker进程,先啥也不干
//do nothing
} //end if(ngx_process == NGX_PROCESS_MASTER)

//这里记录一些日志信息
//siginfo这个
if(siginfo && siginfo->si_pid) //si_pid = sending process ID【发送该信号的进程id】
{
ngx_log_error_core(NGX_LOG_NOTICE,0,"signal %d (%s) received from %P%s", signo, sig->signame, siginfo->si_pid, action);
}
else
{
ngx_log_error_core(NGX_LOG_NOTICE,0,"signal %d (%s) received %s",signo, sig->signame, action);//没有发送该信号的进程id,所以不显示发送该信号的进程id
}

//.......其他需要扩展的将来再处理;

//子进程状态有变化,通常是意外退出【既然官方是在这里处理,我们也学习官方在这里处理】
if (signo == SIGCHLD)
{
ngx_process_get_status(); //获取子进程的结束状态
} //end if

return;
}

获取信号状态函数

避免子进程被杀掉时变成僵尸进程
父进程要处理SIGCHILD信号并在信号处理函数中调用waitpid()来解决僵尸进程的问题;

信号处理函数中的代码,要坚持一些书写原则:
a)代码尽可能简单,尽可能快速的执行完毕返回;
b)用一些全局量做一些标记;尽可能不调用函数;
c)不要在信号处理函数中执行太复杂的代码以免阻塞其他信号的到来,甚至阻塞整个程序执行流程;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
static void ngx_process_get_status(void)
{
pid_t pid;
int status;
int err;
int one=0; //抄自官方nginx,应该是标记信号正常处理过一次

//当你杀死一个子进程时,父进程会收到这个SIGCHLD信号。
for ( ;; )
{
//waitpid,有人也用wait;这个waitpid说白了获取子进程的终止状态,这样,子进程就不会成为僵尸进程了;
//第一次waitpid返回一个> 0值,表示成功,后边显示 2022/01/14 21:43:38 [alert] 3375: pid = 3377 exited on signal 9【SIGKILL】
//第二次再循环回来,再次调用waitpid会返回一个0,表示子进程还没结束,然后这里有return来退出;
pid = waitpid(-1, &status, WNOHANG); //第一个参数为-1,表示等待任何子进程,
//第二个参数:保存子进程的状态信息(大家如果想详细了解,可以百度一下)。
//第三个参数:提供额外选项,WNOHANG表示不要阻塞,让这个waitpid()立即返回

if(pid == 0) //子进程没结束,会立即返回这个数字,但这里应该不是这个数字【因为一般是子进程退出时会执行到这个函数】
{
return;
} //end if(pid == 0)
//-------------------------------
if(pid == -1)//这表示这个waitpid调用有错误,有错误也理解返回出去,我们管不了这么多
{
//这里处理代码抄自官方nginx,主要目的是打印一些日志。考虑到这些代码也许比较成熟,所以,就基本保持原样照抄吧;
err = errno;
if(err == EINTR) //调用被某个信号中断
{
continue;
}

if(err == ECHILD && one) //没有子进程
{
return;
}

if (err == ECHILD) //没有子进程
{
ngx_log_error_core(NGX_LOG_INFO,err,"waitpid() failed!");
return;
}
ngx_log_error_core(NGX_LOG_ALERT,err,"waitpid() failed!");
return;
} //end if(pid == -1)
//-------------------------------
//走到这里,表示 成功【返回进程id】 ,这里根据官方写法,打印一些日志来记录子进程的退出
one = 1; //标记waitpid()返回了正常的返回值
if(WTERMSIG(status)) //获取使子进程终止的信号编号
{
ngx_log_error_core(NGX_LOG_ALERT,0,"pid = %P exited on signal %d!",pid,WTERMSIG(status)); //获取使子进程终止的信号编号
}
else
{
ngx_log_error_core(NGX_LOG_NOTICE,0,"pid = %P exited with code %d!",pid,WEXITSTATUS(status)); //WEXITSTATUS()获取子进程传递给exit或者_exit参数的低八位
}
} //end for
return;
}

waitpid的返回值一共有3种情况:

  1. 当正常返回的时候,waitpid返回收集到的子进程的进程ID;
  2. 如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
  3. 如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;

当pid所指示的子进程不存在,或此进程存在,但不是调用进程的子进程,waitpid就会出错返回,这时errno被设置为ECHILD;

注意在for循环当中
第一次waitpid返回一个> 0值,表示成功,后边显示 2019/01/14 21:43:38 [alert] 3375: pid = 3377 exited on signal 9【SIGKILL】
第二次再循环回来,再次调用waitpid会返回一个0,表示没有已退出的子进程且子进程还没结束,然后这里有return来退出;

ngx_master_process_cycle父进程死循环

  1. 先调用sigaddset防止10个信号的干扰,当master该做的事情做完了进入一个死循环for
  2. for里面调用sigsuspend(&set);进程是挂起的,不占用cpu时间,只有收到信号才会被唤醒

sigsuspend是一个原子操作,包含4个步骤:

  1. 根据给定的参数设置新的mask 并 阻塞当前进程【因为是个空集,所以不阻塞任何信号】
  2. 此时,一旦收到信号,便恢复原先的信号屏蔽【我们原来调用sigprocmask()的mask在上边设置的,阻塞了多达10个信号,从而保证我下边的执行流程不会再次被其他信号截断】
  3. 调用该信号对应的信号处理函数
  4. 信号处理函数返回后,sigsuspend返回,使程序流程继续往下走
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
void ngx_master_process_cycle()
{
sigset_t set; //信号集

sigemptyset(&set); //清空信号集

//下列这些信号在执行本函数期间不希望收到【考虑到官方nginx中有这些信号,就都搬过来了】(保护不希望由信号中断的代码临界区)
//建议fork()子进程时学习这种写法,防止信号的干扰;
sigaddset(&set, SIGCHLD); //子进程状态改变
sigaddset(&set, SIGALRM); //定时器超时
sigaddset(&set, SIGIO); //异步I/O
sigaddset(&set, SIGINT); //终端中断符
sigaddset(&set, SIGHUP); //连接断开
sigaddset(&set, SIGUSR1); //用户定义信号
sigaddset(&set, SIGUSR2); //用户定义信号
sigaddset(&set, SIGWINCH); //终端窗口大小改变
sigaddset(&set, SIGTERM); //终止
sigaddset(&set, SIGQUIT); //终端退出符
//.........可以根据开发的实际需要往其中添加其他要屏蔽的信号......

//设置,此时无法接受的信号;阻塞期间,你发过来的上述信号,多个会被合并为一个,暂存着,等你放开信号屏蔽后才能收到这些信号。。。
if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) //第一个参数用了SIG_BLOCK表明设置 进程 新的信号屏蔽字 为 “当前信号屏蔽字 和 第二个参数指向的信号集的并集
{
ngx_log_error_core(NGX_LOG_ALERT,errno,"ngx_master_process_cycle()中sigprocmask()失败!");
}
//即便sigprocmask失败,程序流程 也继续往下走

//首先我设置主进程标题---------begin
size_t size;
int i;
size = sizeof(master_process); //注意我这里用的是sizeof,所以字符串末尾的\0是被计算进来了的
size += g_argvneedmem; //argv参数长度加进来
if(size < 1000) //长度小于这个,我才设置标题
{
char title[1000] = {0};
strcpy(title,(const char *)master_process); //"master process"
strcat(title," "); //跟一个空格分开一些,清晰 //"master process "
for (i = 0; i < g_os_argc; i++) //"master process ./nginx"
{
strcat(title,g_os_argv[i]);
}//end for
ngx_setproctitle(title); //设置标题
ngx_log_error_core(NGX_LOG_NOTICE,0,"%s %P 【master进程】启动并开始运行......!",title,ngx_pid); //设置标题时顺便记录下来进程名,进程id等信息到日志
}
//首先我设置主进程标题---------end

//从配置文件中读取要创建的worker进程数量
CConfig *p_config = CConfig::GetInstance(); //单例类
int workprocess = p_config->GetIntDefault("WorkerProcesses",1); //从配置文件中得到要创建的worker进程数量
ngx_start_worker_processes(workprocess); //这里要创建worker子进程

//创建子进程后,父进程的执行流程会返回到这里,子进程不会走进来
sigemptyset(&set); //信号屏蔽字为空,表示不屏蔽任何信号,但以往阻塞的信号仍然被阻塞,需要sigprocmask来解除阻塞

for ( ;; )
{

// usleep(100000);
//ngx_log_error_core(0,0,"haha--这是父进程,pid为%P",ngx_pid);

// sigsuspend(const sigset_t *mask))用于在接收到某个信号之前, 临时用mask替换进程的信号掩码, 并暂停进程执行,直到收到信号为止。
// sigsuspend 返回后将恢复调用之前的信号掩码。信号处理函数完成后,进程将继续执行。该系统调用始终返回-1,并将errno设置为EINTR。

//sigsuspend是一个原子操作,包含4个步骤:
//a)根据给定的参数设置新的mask 并 阻塞当前进程【因为是个空集,所以不阻塞任何信号】
//b)此时,一旦收到信号,便恢复原先的信号屏蔽【我们原来调用sigprocmask()的mask在上边设置的,阻塞了多达10个信号,从而保证我下边的执行流程不会再次被其他信号截断】
//c)调用该信号对应的信号处理函数
//d)信号处理函数返回后,sigsuspend返回,使程序流程继续往下走
//printf("for进来了!\n"); //发现,如果print不加\n,无法及时显示到屏幕上,是行缓存问题,以往没注意;可参考https://blog.csdn.net/qq_26093511/article/details/53255970

sigsuspend(&set); //阻塞在这里,等待一个信号,此时进程是挂起的,不占用cpu时间,只有收到信号才会被唤醒(返回);
//此时master进程完全靠信号驱动干活

// printf("执行到sigsuspend()下边来了\n");

//printf("master进程休息1秒\n");
//ngx_log_stderr(0,"haha--这是父进程,pid为%P",ngx_pid);
sleep(1); //休息1秒
//以后扩充.......

}// end for(;;)
return;
}

ngx_worker_process_init

原先屏蔽的十个信号一定要释放开,否则子进程将无法接收信号。

注意worker进程中不该使用sigsuspend来阻塞住,因为worker进程是用来干活的,阻塞住了程序无法正常运行了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//描述:子进程创建时调用本函数进行一些初始化工作
static void ngx_worker_process_init(int inum)
{
sigset_t set; //信号集

sigemptyset(&set); //清空信号集
if (sigprocmask(SIG_SETMASK, &set, NULL) == -1) //原来是屏蔽那10个信号【防止fork()期间收到信号导致混乱】,现在不再屏蔽任何信号【接收任何信号】
{
ngx_log_error_core(NGX_LOG_ALERT,errno,"ngx_worker_process_init()中sigprocmask()失败!");
}

//线程池代码,率先创建,至少要比和socket相关的内容优先
CConfig *p_config = CConfig::GetInstance();
int tmpthreadnums = p_config->GetIntDefault("ProcMsgRecvWorkThreadCount",5); //处理接收到的消息的线程池中线程数量
if(g_threadpool.Create(tmpthreadnums) == false) //创建线程池中线程
{
//内存没释放,但是简单粗暴退出;
exit(-2);
}
sleep(1); //再休息1秒;

if(g_socket.Initialize_subproc() == false) //初始化子进程需要具备的一些多线程能力相关的信息
{
//内存没释放,但是简单粗暴退出;
exit(-2);
}

//如下这些代码参照官方nginx里的ngx_event_process_init()函数中的代码
g_socket.ngx_epoll_init(); //初始化epoll相关内容,同时 往监听socket上增加监听事件,从而开始让监听端口履行其职责
//g_socket.ngx_epoll_listenportstart();//往监听socket上增加监听事件,从而开始让监听端口履行其职责【如果不加这行,虽然端口能连上,但不会触发ngx_epoll_process_events()里边的epoll_wait()往下走】


//....将来再扩充代码
//....
return;
}