现在的位置: 首页 > 综合 > 正文

December 18th Friday 2009

2013年05月16日 ⁄ 综合 ⁄ 共 2967字 ⁄ 字号 评论关闭

Work进程逻辑(ngx_worker_process_cycle()函数)

 

Nginx的工作进程中嵌入了线程逻辑。下面三个部份按照代码的逻辑来说明的。如果不支持线程,线程部份代码编译时不会生成。

1.进程部份

一切从ngx_worker_process_init()函数开始:

1.先调用ngx_set_environment()函数为本进程设定环境变量,那些环境变量都是从cycle中继承过来的;

2.  有进程执行优先权,则调用setpriority(PRIO_PROCESS, 0, ccf->priority)函数设置优先权;

3.  设置进程可打开最大文件数(setrlimit(RLIMIT_NOFILE, &rlmt))

4.  设置内核转存文件最大长度(setrlimit(RLIMIT_CORE, &rlmt))

5.  支持RLIMIT_SIGPENDING的情况下,设置RLIMIT_SIGPENDING(setrlimit(RLIMIT_SIGPENDING, &rlmt))

6.  支持PR_SET_DUMPABLE时,设置PR_SET_DUMPABLE(prctl(PR_SET_DUMPABLE,1,0,0,0))

7.  换到当前工作的目录下;

8.  清空所有的信号;

9.  清掉监听socket上以前的事件;

10. 调用所的模块的init_process钩子函数;

11. 将其他进程的channel[1]关闭,自己的除外;

12. 将自己的channel[0]关闭;

13. 调用ngx_add_channel_event()函数,给ngx_channel(ngx_start_worker_processes()函数中,ngx_channel = ngx_processes[s].channel[1];,所以ngx_channel就是进程自身的channel[1],用来读取的socket)注册一个读事件处理函数;

 

ngx_add_channel_event()函数的主要工作:

1. 取得一个空闲的connection资源;

2. 创建了一个读事件和一个写事件对象,并设置了channel1

3. 检查注册的事件是读还是写,分别选用不同的事件对象,安装事件处理函数;

4. epoll事件模式并且ngx_event_actions.add_conn存在时,调用add_conn添加一个connection资源;否则用ngx_event_actions.add添加一个事件对象;

 

ngx_channel_handler()函数,这个函数作为事件驱动:

1. 事件对象中的timedout清空;

2. 从事件对象中取得connection对象;

3. 进入循环,调用ngx_read_channel()函数,读取channel对象(还记得什么地方用ngx_write_channel()函数写向子进程写入一个channel对象吗?)

4. NGX_USE_EVENTPORT_EVENT事件标志时,添加读事件;

5. 接下来是检查取得的channel对象中的command,不同的命令不同的处理:

         a)      NGX_CMD_QUIT: ngx_quit = 1;

         b)      NGX_CMD_TERMINATE: ngx_terminate = 1;

         c)      NGX_CMD_REOPEN: ngx_reopen = 1;

         d)      NGX_CMD_OPEN_CHANNEL: ngx_processes[]表中指定项中设置pid和写文件描述符;

         e)  NGX_CMD_CLOSE_CHANNEL: 关闭ngx_processes[]表中指定表项中的写文件描述符。

 

2.线程部份

如果支持线程会执行下面的工作(当然线程数不能为0)

1.调用ngx_init_threads()函数,设定线程所用堆栈大小;

2.ngx_thread_key_create()函数,创建线程所用的key

3.创建ngx_create_thread()函数,创建线程;

上面这组关于线程的函数,是对不同API的包装,如freebsd下,和posix的。相对简单明了,不作详解了。

ngx_worker_thread_cycle()函数是线程的工作函数。

1.阻塞NGX_RECONFIGURE_SIGNALNGX_REOPEN_SIGNALNGX_CHANGEBIN_SIGNAL三个信号;

2.设置线程的title——“worker thread”

3.创建线程的私有存贮空间;

4.接下来主要是循环处理事件,先假定是线程是空闲的,一旦取得一个事件调用ngx_event_thread_process_posted()函数处理(这儿有疑问,不知道为什么要连着调用两次ngx_event_thread_process_posted()函数),这时线程是忙碌状态,最后ngx_event_actions.process_change()(目前不清楚是什么作用)存在,则调用之。

如果发现ngx_terminate为真,线程退出。

 

ngx_event_thread_process_posted()函数,是一个事件驱动处理器。主逻辑比较好理解,但是细节部份没有注解,加上有些宏或函数与芯片架构有关,所以细节不好理解。

其主要工作,是从事件队列(ngx_posted_events)中取一个事件对象,调用事件对象中的handler()处理这个事件。由于是多线程模式,所以取事件时采用排他锁。这里要注意,排他锁的相关函数,是针对不同环境,自己写代码实现的,没有用api。可能是为了提高效率。

 

3.回到进程

由于在ngx_channel_handler()函数中对应收到的命令,设置了ngx_quit,ngx_terminate,ngx_reopen这些变量,于是在这里进入处理循环,分别处理之。

1. ngx_exiting: 试着关掉所在打着的connection对象。如果其中还有数据没读,试着读取完;

事件没有了(ngx_event_timer_rbtree.root == ngx_event_timer_rbtree.sentinel),调用ngx_worker_process_exit()退出;

2.调用ngx_process_events_and_timers()函数,

3. ngx_terminate: 调用ngx_worker_process_exit()退出;

  ngx_worker_process_exit()函数调用各个模块中的exit_process钩子函数;ngx_exiting状态下会在log中记录那些打开的等待读的不正常的connection;销毁内存池;退出。

4. ngx_quitngx_setproctitle("worker process is shutting down");

if (!ngx_exiting) {

     ngx_close_listening_sockets(cycle); /*关闭监听中的socket*/

     ngx_exiting = 1;

}

5. ngx_reopen:调用ngx_reopen_files()函数。

抱歉!评论已关闭.