Spawns FastCGI Processes

基于C+Spawn-fcgi实现一个最简单的WebServer.

spawn-fcgi

[安装b包]
spawn-fcgi-1.6.3-1.el6.x86_64
fcgi-devel-2.4.0-12.el6.x86_64
fcgi-2.4.0-12.el6.x86_64

[spawn-fcgi关键参数]
Usage: spawn-fcgi [options] [-- <fcgiapp> [fcgi app arguments]]

spawn-fcgi v1.6.3 (ipv6) - spawns FastCGI processes

Options:
 -f <path>      【fcgi的应用程序,需要绝对路径】filename of the fcgi-application (deprecated; ignored if
                <fcgiapp> is given; needs /bin/sh)
 -d <directory> 【在产卵子进程之前,先chdir到指定目录】chdir to directory before spawning
 -a <address>   【绑定监听IP地址】bind to IPv4/IPv6 address (defaults to 0.0.0.0)
 -p <port>      【绑定TCP端口】bind to TCP-port
 -s <path>      【绑定关连到一个unix套接字】bind to Unix domain socket
 -M <mode>      【更改Unix套件字的模式】change Unix domain socket mode
 -C <children>  【仅PHP可用,设定PHP-CGI的子进程数量】(PHP only) numbers of childs to spawn (default: not setting
                the PHP_FCGI_CHILDREN environment variable - PHP defaults to 0)
 -F <children>  【fork出来的CGI子进程数量,默认是1】number of children to fork (default 1)
 -P <path>      【产卵出来的进程PID文件存储路径,在no-fork模式中被忽略】name of PID-file for spawned process (ignored in no-fork mode)
 -n             【不fork子进程,针对daemon模式】no fork (for daemontools)
 -v             show version
 -?, -h         show this help

(root only)	------Root账号可用参数
 -c <directory> 【chroot安全】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

cgi部分

tiny-cgi.c

#include <stdio.h>


int main(void) {
    int count = 0;

    //fastcgi accecpt
    while (FCGI_Accept() >= 0) {
        // http header
        printf("HTTP/1.0 200 OK\r\n");
        // http header
        printf("Content-Type: text/html; charset=utf-8\r\n");
        printf("Server: ByCGI\r\n");
        // content body
        printf("\r\n");
        printf("Hi,Man, I print by cgi program!!<br/>\r\n");
        printf("Server Name : %s <br/>", getenv("SERVER_NAME"));
        printf("Count number : %d ", count++);
    }
    return 1;
}

CGI编译的一些问题

[编译错误]
[clark@mac clang]$ gcc tiny-cgi.c -o tiny-cgi
/tmp/ccvVMqJM.o: In function `main':
tiny-cgi.c:(.text+0x1f): undefined reference to `FCGI_printf'
tiny-cgi.c:(.text+0x2e): undefined reference to `FCGI_printf'
tiny-cgi.c:(.text+0x3d): undefined reference to `FCGI_printf'
tiny-cgi.c:(.text+0x4c): undefined reference to `FCGI_printf'
tiny-cgi.c:(.text+0x5b): undefined reference to `FCGI_printf'
/tmp/ccvVMqJM.o:tiny-cgi.c:(.text+0x7b): more undefined references to `FCGI_printf' follow
/tmp/ccvVMqJM.o: In function `main':
tiny-cgi.c:(.text+0x98): undefined reference to `FCGI_Accept'
collect2: ld returned 1 exit status

[查看fcgi安装情况]
[clark@mac clang]$ rpm -qa|grep fcgi
spawn-fcgi-1.6.3-1.el6.x86_64
fcgi-devel-2.4.0-12.el6.x86_64
fcgi-2.4.0-12.el6.x86_64

[fcgi-devel包信息]
[clark@mac clang]$ rpm -ql fcgi-devel
/usr/include/fastcgi.h
/usr/include/fcgi_config.h
/usr/include/fcgi_stdio.h
/usr/include/fcgiapp.h
/usr/include/fcgimisc.h
/usr/include/fcgio.h
/usr/include/fcgios.h
/usr/lib64/libfcgi++.so
/usr/lib64/libfcgi.so	---------- 连接加上
...

[FCGI相关函数头部信息所在]
[clark@mac clang]$ grep FCGI_printf /usr/include/* -r
/usr/include/fcgi_stdio.h:DLLAPI int        FCGI_printf(const char *format, ...);
/usr/include/fcgi_stdio.h:#define	printf   FCGI_printf

[缺少相关的连接库,缺少fcgi.so,增加链接(-lfcgi),解决]
[clark@mac clang]$ gcc tiny-cgi.c -o tiny-cgi -lfcgi
[clark@mac clang]$ ll tiny-cgi
-rwxrwxr-x 1 Terry Terry 7246 Aug 21 13:49 tiny-cgi

调试

利用spawn-fcgi程序启动fcgi脚本

[clark@mac clang]$ spawn-fcgi -f ./tiny-cgi -p 12580 -F 5 -u nginx -g nginx -P /tmp/tiny-cgi.pid
spawn-fcgi: child spawned successfully: PID: 11208
spawn-fcgi: child spawned successfully: PID: 11209
spawn-fcgi: child spawned successfully: PID: 11210
spawn-fcgi: child spawned successfully: PID: 11211
spawn-fcgi: child spawned successfully: PID: 11212

[clark@mac clang]$ cat /tmp/tiny-cgi.pid
11208
11209
11210
11211
11212
[clark@mac clang]$ pstree Aup|grep cgi

[kill掉]
[clark@mac clang]$ cat /tmp/tiny-cgi.pid |xargs -n 1 kill -15

bash shell 管理

#!/bin/bash
#
#   利用Spawn-CGI进行C编译的CGI相关管理
#

# Set up a default search path.
PATH="/sbin:/usr/sbin:/bin:/usr/bin"
export PATH

# Setting
user=www
group=www
pid_file=/tmp/tiny-cgi.pid
cgi_bin=./tiny-cgi
port=19000
process_num=2

# Cmd
sh="spawn-fcgi -f $cgi_bin -p $port -F $process_num  -u $user -g $group -P $pid_file"

# exec
#printf "共计 $# 个变量, \$@代表以IPS分隔的字符串: $@, \$*代表每个参数清单:$* \n";
case "$1" in

    "start")
        echo "Start spawn-cgi child process...";
        $sh
    ;;

    "stop")
        echo "Stop spawn-cgi child process..."
        cat $pid_file|xargs -n 1 kill -15;
        ps -ef|grep "$(basename "$cgi_bin")"|grep -v grep;
    ;;

    "restart" )
        exec_result=0
        # 有无cgi运行
        if [ `ps -ef|grep $(basename "$cgi_bin")|grep -v "grep"|wc -l` -gt 0  ]; then
            $0 stop
        fi;

        if [ "$exec_result" == "0" ]; then
            $0 start
        else
            echo "Stop faild... Can not go on start!!"
        fi
    ;;

    *)
        echo "Usage $0 start|stop|restart.";
    ;;
esac

请求结果

由于开启的是两个CGI进程,请求被轮询的丢给CGI处理,最终的结果就是,请求两次有一次

[Response]
HTTP/1.1 200 OK
Date: Mon, 21 Aug 2017 08:20:03 GMT
Content-Type: text/html; charset=utf-8
Connection: keep-alive
Server: ByCGI
Content-Length: 85

Hi,Man, I print by cgi program!!<br/>
Server Name : localhost <br/>Count number : 5

参考

  1. Nginx + CGI/FastCGI + C/Cpp <涵盖spawn-fcgi部分>
  2. 什么是CGI、FastCGI、PHP-CGI、PHP-FPM、Spawn-FCGI