• spawn-fcgi和libfcgi源码解读


    版本:spawn-fcgi-1.6.4

    文件:spawn-fcgi-1.6.4/src/spawn-fcgi.c

    一、入口 main():

    1. 获取执行spawn-fcgi的时候传递的参数:getopt(argc, argv, "c:d:f:g:?hna:p:b:u:vC:F:s:P:U:G:M:S");

    while循环,每次获取一个参数,将获取到的参数值(每次获取的时候保存在optarg这个全局变量中)进行初始化处理;

    Options:
     -f <path>      filename of the fcgi-application (deprecated; ignored if
                    <fcgiapp> is given; needs /bin/sh)
     -d <directory> chdir to directory before spawning
     -a <address>   bind to IPv4/IPv6 address (defaults to 0.0.0.0)
     -p <port>      bind to TCP-port
     -s <path>      bind to Unix domain socket
     -M <mode>      change Unix domain socket mode (octal integer, default: allow
                    read+write for user and group as far as umask allows it)
     -C <children>  (PHP only) numbers of childs to spawn (default: not setting
                    the PHP_FCGI_CHILDREN environment variable - PHP defaults to 0)
     -F <children>  number of children to fork (default 1)
     -b <backlog>   backlog to allow on the socket (default 1024)
     -P <path>      name of PID-file for spawned process (ignored in no-fork mode)
     -n             no fork (for daemontools)
     -v             show version
     -?, -h         show this help
    (root only)
     -c <directory> chroot to directory
     -S             create socket before chroot() (default is to create the socket
                    in the chroot)
     -u <user>      change to user-id
     -g <group>     change to group-id (default: primary group of user if -u
                    is given)
     -U <user>      change Unix domain socket owner to user-id
     -G <group>     change Unix domain socket group to group-id

    2. 两种fork模式:

    1) 传参-n, 即nofork模式, 则pid_file = NULL;

    2)不传参-n,即非nofork模式,则pid_file = 参数-P指定的文件。

    如果指定了pid_file文件,则pid_fd = open(pid_file, ...);

    3. 判断指定的username, groupname,sockusername, sockgroupname是否在系统中: find_user_group()

    相关函数:读取/etc/passwd文件到struct passwd结构中:

    getpwnam()

    getpwuid()

    getgrnam()

    4.root用户执行的时候getuid()==0,可以执行执行的chroot目录,也可以指定sock的用户和组;

    5.socket的bind()操作:fcgi_fd = bind_socket();

    二、生成fcgi应用程序进程: fcgi_spawn_connection(fcgi_app, fcgi_app_argv, fcgi_fd, fork_count, child_count, pid_fd, nofork);

    fcgi_app:fcgi应用程序bin文件;

    fcgi_app_argv,:通过getopt()读取完后剩下的参数;

    fcgi_fd:socket client fd;

    fork_count:需要fork的子进程数,默认为1;

    child_count:仅仅PHP有效;

    pid_fd:pid文件;

    nofork:是否是fork模式.

    1.通信sock统一到fd=0上面:

    关键点:
          if(fcgi_fd != FCGI_LISTENSOCK_FILENO) {
            close(FCGI_LISTENSOCK_FILENO);
            dup2(fcgi_fd, FCGI_LISTENSOCK_FILENO);
            close(fcgi_fd);
          }

    使用dup2将指向sock的文件描述符fcgi_fd复制到FCGI_LISTENSOCK_FILENO。

    也就是使fcgi_fd和FCGI_LISTENSOCK_FILENO都指向sock,然后关闭fcgi_fd,改用FCGI_LISTENSOCK_FILENO来作为通信sock的文件描述符.

    2.如果是fork模式,则用setsid()脱离主进程,自己独立出来。

    3.父、子进程处理

    1)如果是子进程:用新的程序替换掉当前进程;

    如果传了新的执行程序:execv(appArgv[0], appArgv);

    否则:execl("/bin/sh", "sh", "-c", b, (char *)NULL);  //参数b是-f指定的程序.

    2)如果是父进程:监测子进程,且做资源回收waitpid(),类似pthread_join();

    select(0, NULL, NULL, NULL, &tv);

    waitpid(child, &status, WNOHANG);

    libfcgi

    一、 初始化FCGX库
    int FCGX_Init(void)

    FCGX_InitRequest(&the_request, FCGI_LISTENSOCK_FILENO, 0); =>给FCGX_Request赋初值.

    getenv("FCGI_WEB_SERVER_ADDRS");

    如果初始化成功,则 libInitialized = 1;


    二、从HTTP server接收一个新请求
    int FCGX_Accept(FCGX_Stream **in,FCGX_Stream **out,FCGX_Stream **err,FCGX_ParamArray *envp)

    int FCGX_Accept_r(FCGX_Request *reqDataPtr)

    FCGX_Finish_r(reqDataPtr);   => 完成当前的请求处理

    reqDataPtr->ipcFd  =   int OS_Accept(int listen_sock, int fail_on_intr, const char *webServerAddrs)
    for (;;) {
        AcquireLock(listen_sock, fail_on_intr)   //获取sock的读写锁.  对共享的sock调用accept
        for (;;) {
            socket = accept(listen_sock, (struct sockaddr *)&sa, &len); //注意这里:这里是真正的accept()
        }
    }

    三、FCGI应用程序demo

    #include <stdlib.h>
    #include <string.h>
    #include <syslog.h>
    #include <alloca.h>
    #include <fcgiapp.h>
    #define LISTENSOCK_FILENO 0
    #define LISTENSOCK_FLAGS 0
    int main(int argc, char **argv)
    {
      openlog("testfastcgi", LOG_CONS | LOG_NDELAY, LOG_USER);
      int err = FCGX_Init(); /* call before Accept in multithreaded apps */
      if (err)
      {
        syslog(LOG_INFO, "FCGX_Init failed: %d", err);
        return 1;
      }
      FCGX_Request cgi;
      err = FCGX_InitRequest(&cgi, LISTENSOCK_FILENO, LISTENSOCK_FLAGS);
      if (err)
      {
        syslog(LOG_INFO, "FCGX_InitRequest failed: %d", err);
        return 2;
      }
     
      while (1) 
      {
        err = FCGX_Accept_r(&cgi);
        if (err)
        {   
          syslog(LOG_INFO, "FCGX_Accept_r stopped: %d", err);
          break;
        }   
    //这里进行业务处理:cgi.in char **envp; int size = 200; char *result = (char *)alloca(size); strcpy(result, "Status: 200 OK Content-Type: text/html "); strcat(result, "<html><h1>TestCGI!</h1></html> "); FCGX_PutStr(result, strlen(result), cgi.out); } return 0; }
  • 相关阅读:
    后端结对编程报告(2018.6.6)
    Burn Down Chart(2018.6.4~2018.6.10)
    C#多线程List的非线程安全性
    C#泛型参数多线程与复杂参数多线程
    Java学习之App开发公司手机端设想
    Java学习之SpringBoot整合SSM Demo
    Java学习之Mysql结构优化
    Java学习之Dubbo+ZooKeeper分布式服务Demo
    C# 面向切面编程--监控日志记录方案
    C# 通用类型转换方法
  • 原文地址:https://www.cnblogs.com/thinksasa/p/13218519.html
Copyright © 2020-2023  润新知