zoukankan      html  css  js  c++  java
  • nginx中父子进程工作的主体函数

    依据Nginx(0.7.67版本号)的代码。对Nginx主要的进程创建,进程主体以及事件处理进行了简要的分析。

    基本上,父进程(即主进程)一開始会初始化及读取配置。并载入各模块的功能,然后fork()出N个子进程(即工作进程),具有同样的工作逻辑和功能。

    父进程负责监听信号(如HUP,QUIT等),通过socket pair把信号传递给子进程(子进程间一般不通信)。子进程通过事件来处理父进程传递的信号。由于每一个子进程都共享服务监听port(如http 80),当用户发送请求时,会触发子进程的事件调用函数。因此在accept()请求的时候,须要用到mutex,保证仅仅有一个工作进程接受并处理请求。


    Nginx主进程的入口是跟一般的程序一样的main()函数。它的代码是这种:
    int ngx_cdecl main(int argc, char *const *argv)
    {
    /*...*/

    //nginx 启动的时候会尝试从环境变量中读取前次运行时候的监听套接口的id,
    //并会创建相应数量的ngx_listening_t结构变量(存于 cycle->listening数组中)。
    //然后调用这个接口通过getsockname,getsockopt等系统调用把原来套接口的属性信息和
    //设置參数读取出来去设置那些新创建的ngx_listening_t结构变量。这样就继承了前次运行
    //时候的监听套接口了。这个接口是在 ngx_init_cycle之前调用的

    if (ngx_add_inherited_sockets(&init_cycle) != NGX_OK) { 
    return 1; }

    /*...*/

    //ngx_modules数组在objs/ngx_modules.c定义ngx_max_module = 0;for (i = 0; ngx_modules[i]; i++) { ngx_modules[i]->index = ngx_max_module++; //将每一个模块编号
    }

    //ngx_init_cycle()函数,这是个比較重要的函数。被main, ngx_master_process_cycle
    //ngx_single_process_cycle 调用, 当中后两者是在reconfigure的时候被调用的
    //它主要工作是。初始化cycle是基于旧有的cycle进行的; 会继承old cycle的非常多属性。 
    //比方log等, 可是同一时候会对非常多资源又一次分配, 比方pool, shared mem, file handler, 
    //listening socket等,同一时候清除旧有的cycle的资源
    //读取配置文件也是在这里完毕的
    cycle = ngx_init_cycle(&init_cycle);

    if (ngx_process == NGX_PROCESS_SINGLE) { ngx_single_process_cycle(cycle); } else {
    //一般採用多进程处理请求
    ngx_master_process_cycle(cycle);
    }
    }

    //主进程(即父进程)的主体
    //这个函数会启动工作进程干活。而且会处理信号量。处理的过程中会杀死或者创建新的进程
    void ngx_master_process_cycle(ngx_cycle_t *cycle)
    {
    //把信号添加到监听集合set sigemptyset(&set);//sigemptyset()用来将參数set信号集初始化并清空 sigaddset(&set, SIGCHLD);//sigaddset() 添加一个信号至信号集 sigaddset(&set, SIGALRM); sigaddset(&set, SIGIO); sigaddset(&set, SIGINT); sigaddset(&set, ngx_signal_value(NGX_RECONFIGURE_SIGNAL)); sigaddset(&set, ngx_signal_value(NGX_REOPEN_SIGNAL)); sigaddset(&set, ngx_signal_value(NGX_NOACCEPT_SIGNAL)); sigaddset(&set, ngx_signal_value(NGX_TERMINATE_SIGNAL)); sigaddset(&set, ngx_signal_value(NGX_SHUTDOWN_SIGNAL)); sigaddset(&set, ngx_signal_value(NGX_CHANGEBIN_SIGNAL));

    /*...*/

    //依照ngx_core_conf_t中worker_processes数,启动若干个work进程 ngx_start_worker_processes(cycle, ccf->worker_processes, NGX_PROCESS_RESPAWN);

    //后面一个循环对不同的状态进行不同处理。而那些状态多数是进程收到的不同信号//ngx_signal_t signals[]数组定义信号以及处理方法:ngx_signal_handler()//信号在ngx_init_signals()里面初始化//在ngx_signal_handler()里,依据ngx_process来决定是master process还是
    //worker process的处理流程(即是说master和worker都调用ngx_signal_handler
    //来处理信号)

    for ( ;; ) {
    //挂起当前进程,等到有信号。就会从挂起状态退出。继续运行//set记录当前监听的信号 sigsuspend(&set);
    /*各种信号的处理,如NGX_SHUTDOWN_SIGNAL,NGX_RECONFIGURE_SIGNAL等*/
    /*省略*/
    }
    }


    //ngx_start_worker_processes()函数。这个函数按指定数目n,以ngx_worker_process_cycle()函
    //数为參数调用ngx_spawn_process()创建work进程并初始化相关资源和属性。
    //运行子进程的运行函数ngx_worker_process_cycle;向之前已经创建的全部worker广播当前创建的
    //worker进程的信息。每一个进程打开一个通道(ngx_pass_open_channel())
    //n是worker process的数目
    //type 即创建新进程式的方式,如NGX_PROCESS_RESPAWN, NGX_PROCESS_JUST_RESPAWN
    static void ngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t n, ngx_int_t type)
    {
         ngx_int_t      i;
    ngx_channel_t  ch;

         ch.command = NGX_CMD_OPEN_CHANNEL;

         for (i = 0; i < n; i++) {
    //从config读取CPU的分配
             cpu_affinity = ngx_get_cpu_affinity(i);
    //ngx_spawn_process中设置ngx_process_slot为被分配到子进程的
    //空暇slot
             ngx_spawn_process(cycle, ngx_worker_process_cycle, NULL,
    "worker process", type);
             ch.pid = ngx_processes[ngx_process_slot].pid;
             ch.slot = ngx_process_slot;
             ch.fd = ngx_processes[ngx_process_slot].channel[0];
    //ngx_pass_open_channel 把这个worker的channel[0]和进程id
    //在进程表中的偏移slot广播(ngx_write_channel())给全部其它已经
    //创建的worker。

    //这样,创建全然部进程之后,每一个worker就获得了全部其它worker的
    //channel[0]了
             ngx_pass_open_channel(cycle, &ch);
        }
    }


    ngx_pid_t ngx_spawn_process(ngx_cycle_t *cycle, ngx_spawn_proc_pt proc, void *data, char *name, ngx_int_t respawn)
    {
    /*...*/
    //假设类型为NGX_PROCESS_DETACHED。则说明是热代码替换(热代码替换也是通过这
    //个函数进行处理的),因此不须要新建socketpair。

    if (respawn != NGX_PROCESS_DETACHED) {//建立socketpair //创建socketpair用于进程间通信。master进程为每一个worker创建一对
    //socket, master进程空间打开全部socketpair的channel[0],
    //channel[1]两端句柄。

    //当创建一个worker的时候,这个worker会继承master当前已经创建并

    //打开的全部socketpair。//这个worker初始化的时候(调用ngx_worker_process_init)会关闭
    //本进程相应socketpair的channel[0]和其它worker相应的
    //channel[1]保持打开本进程相应socketpair的channel[1]和其它
    //worker相应的channel[0],并监听本进程相应socketpair的
    //channel[1]的可读事件。这样,每一个worker就拥有了其它worker的
    //channel[0],能够sendmsg(channel[0], ...)向其它worker发送消息
    /*...*/
    }

    /*...*/
    //创建子进程
    pid = fork();
    switch (pid) {case -1:ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, 
    "fork() failed while spawning "%s"", name);
    ngx_close_channel(ngx_processes[s].channel, cycle->log);return NGX_INVALID_PID;//在子进程里,运行传递进来的子进程的函数 case 0:ngx_pid = ngx_getpid();
    //调用ngx_worker_process_cycle() //注意:这个函数中定义了子进程处理事件的循环,即子进程不会
    //运行这个函数之后的语句
    proc(cycle, data);break;default:break; }

    }

    //worker进程的主体
    static void ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data)
    {
    //设置一些环境变量
    //调用全部模块的init_process钩子函数
    //将其它进程的channel[1]关闭。自己的除外//子进程继承了父进程的ngx_processes数组。但子进程仅仅监听自己的channel[1]
    //将自己的channel[0]关闭//由于自己的channel[0]是给其它子进程。用来发送消息的sendmsg
    //调用ngx_add_channel_event()函数,给ngx_channel注冊一个读事件处理函数。
    ngx_worker_process_init(cycle, 1);
    //主循环。处理事件
    for ( ;; ) {
    /*...*/

    //调用epoll,周期性监听事件
    //先接收连接(并不处理事件)。以及处理进程间信号(如有)
    //处理accept queue和event queue里面的事件
    ngx_process_events_and_timers(cycle);

    /*...*/
    }

    /*...*/
    }

  • 相关阅读:
    Uri编码,包括javascript前端与C#服务器端
    HttpWebResponse类
    HTTP报文
    HTTP权威指南阅读记录
    XMPP通讯开发-1
    开源安卓播放器:Dolphin Player 简单分析
    Spring MVC框架
    Median of Two Sorted Arrays (找两个序列的中位数,O(log (m+n))限制) 【面试算法leetcode】
    C语言指针5分钟教程
    2013中国互联网安全大会---关于季昕华老师的分享(不喜请勿入!)
  • 原文地址:https://www.cnblogs.com/cynchanpin/p/7105534.html
Copyright © 2011-2022 走看看