欢迎光临
我们一直在努力

实战 FastCGI(转)三-CGI教程,CGI文档

建站超值云服务器,限时71元/月

3. 撰写 fastcgi 应用程序

撰写全新的 fastcgi 应用程序,或是将旧有的 cgi 程序改写成 fastcgi 应用程序都非常的简单,只要使用 fcgi-devkit 所附的 fcgi_stdio 函式库即可。

基本上,fcgi_stdio 函式库已被设计成让开发人员撰写 fastcgi 应用程序就像写一般 cgi 程序一样,同时做到程序保有和 cgi 最大的兼容度,又能享受到 fastcgi 所带来的优点。使用 fcgi_stdio 函式库的另一项好处是,编译出来的执行档可同时以 cgi 以及 fastcgi 的方式执行。

3.1 程序架构
对 cgi 程序而言,其生命期就是从一个联机请求 (request) 开始到联机结束。而 fastcgi 程序就像是比较『长命』的 cgi 程序,其生命期横跨不同的联机请求,可从 web 服务器激活开始到 web 服务器停止。

由于 fastcgi 程序长命的特性,它和一般 cgi 程序主要的差异就在于把初始化 (initialization) 的部份和处理联机请求的部份区分开来,程序的架构如下所示:

initialization code
start of response loop

body of response loop
end of response loop

initialization code 的部份只会在 fastcgi 程序激活时执行一次,程序初始化的部份像是内存的配置,建立和数据库的联机等都可以写在这里。

而 start of response loop 到 end of response loop 之间的程序在每次发生联机请求时就会执行,这部份的程序才是真正处理每次联机请求要做的事情。例如接受使用者输入的参数,从数据库取出资料,执行运算动作,回复结果给使用者等等。

一个简单的 fastcgi 程序如下 (tiny-fcgi.c)

#include "fcgi_stdio.h"
#include <stdlib.h>
void main(void)
{
/* initialization code */
int count = 0;
/* start of response loop */
while(fcgi_accept() >= 0) {
/* body of response loop */
printf("content-type: text/html\r\n"
"\r\n"
"<title>fastcgi hello! (c, fcgi_stdio library)</title>"
"<h1>fastcgi hello! (c, fcgi_stdio library)</h1>"
"request number %d running on host <i>%s</i> "
"process id: %d\n",
++count, getenv("server_name"), getpid());
}
/* end of response loop */
}

3.2 引入 fcgi_stdio.h 标头档
开始撰写一个新的 fastcgi 应用程序时,必须引入 fcgi_stdio.h 这个标头档:

#include “fcgi_stdio.h
如果要改写旧有的 cgi 程序成 fastcgi 程序,请把原本引入 stdio.h 的部份换掉:

#ifndef fastcgi
#include <stdio.h>
#else
#include “fcgi_stdio.h
#endif

上面的写法是利用 c 的前置编译器做处理,如果在编译时加入 -dfastcgi 的参数则引入 fcgi_stdio.h ,反之则否。假设我们把 fcgi_stdio.h 标头文件放在 /usr/local/include/fastcgi 这个目录下的话,为了在编译时引入 fcgi_stdio.h 标头档,请加入 -i/usr/local/include/fastcgi 的参数。

$ cc -i/usr/local/include/fastcgi -c program.c

注意
如果你的程序使用到其它的函式库,请务必检查这些函式库是否有使用到 stdio.h 这个档,如果有的话必须一并换成 fcgi_stdio.h。举例来说,假如你的程序用到 gd 函式库4来产生 gif 图形,请记得把 gd.h 中引入 stdio.h 的部份换成 fcgi_stdio.h,否则遇到 io 的函式时会发生错误 (core dump)。

3.3 fastcgi 处理循环
在 fastcgi 程序中负责处理联机请求的程序,必须用一个 while 循环包起来,利用 fcgi_stdio 函式库中的 fcgi_accept() 函式库来判断是否有新的联机请求发生。

fastcgi 程序被激活后,首先进行初始化的动作,如变量宣告、内存配置、或是与数据库建立联机等。执行到 fcgi_accept() 时程序就会暂停,等到当某一个联机请求发生时,fcgi_accept() 会传回大于零的值,程序即刻进入循环的主体,可能是执行 sql 指令,运算以及产生 html 的输出。处理完后又回到 fcgi_accept() 循环的开始,等待下一个联机请求。

由此可见, fastcgi 激活之后,省去原本 cgi 程序中 fork,内存配置,建立数据库联机等步骤,处理每个联机请求的时间几乎等于 fcgi_accept() 这个循环中运算所需的时间。如果妥善将每次联机请求时一样的程序代码从 fcgi_accept() 循环抽出来,只保留每次会产生不同结果的部份,则程序处理每次联机请求的时间可以更短,整个网站的效率也可以大幅的提升。

了解 fastcgi 程序的基本架构后,可以发现一般的 cgi 程序,只要加入 fcgi_accept() 这个 while 循环后,大概就可以变成 fastcgi 的程序了。

3.4 炼结 libfcgi.a 函式库
fastcgi 程序要炼结到 libfcgi.a 函式库才可正确产生出执行档,假设 libfcgi.a 的位置在 /usr/local/lib 这个目录下,我们在编译 (炼结时) 要加入 -l/usr/local/lib -lfcgi 的参数。

$ cc -o program.fcg program.o -l/usr/local/lib -lfcgi

3.5 撰写 fastcgi 程序的注意事项
由于 fastcgi 程序被激活后就常驻在内存之中,对每个 fastcgi 的执行行程 (running process) 而言,每次联机请求共享的变量空间是相同的,因些选写 fastcgi 程序要特别留意一些注意事项。

1. 记住,对每个 fastcgi 程序而言,只有 fcgi_accept() 循环内的程序才是真正处理每次联机请求的主体,假设在程序中要读取和 cgi 有关的环境变量,或是使用者透过窗体 (form) 所输入的资料,必须在 fcgi_accept() 循环内才处理。
2. 对每次联机请求会改变的变量,别忘了在处理新的联机请求前把变量初始化 (除非是有特殊用途) ,以避免变量值在不同的联机请求之间互相影响。
3. 在 fcgi_accept() 循环中配置 (allocate) 的内存别忘了释放 (free)。在一般的 cgi 程序中, memory leak 不算是太大的问题,因为每处理完一次联机请求,所有用到的内存随着 cgi 程序的结束也被释放。但是 fastcgi 程序会一直常驻在内存中,如果在 fcgi_accept() 中有配置额外的内存,在循环结束前没有释放掉,则每处理一次联机请求就吃掉一些系统的内存,多跑几次下来,系统资源大概就被耗光了。memory leak 的问题是 fastcgi 最常见的错误,要特别小心处理。
4. fastcgi 程序和其所要引用的子程序中,用到 stdio.h 函式的部份,记得要改成 fcgi_stdio.h 。否则当程序在遇到 io 的动作时就会发生 core dump 的情况。
5. 在 fcgi_accept() 循环中使用 fcgi_finish() 函式以取代 exit() 函式。原本 cgi 程序中发生错误时,可能直接呼叫 exit() 函式结束 cgi 行程,fastcgi 程序也可以如此,因为 mod_fastcgi 模块在 fastcgi 程序发生错误而意外结束时,会自动再激活另一个 fastcgi 的行程。但比较好的作法是呼叫 fcgi_stdio.h 中的 fcgi_finish() 函式,呼叫 fcgi_finish() 函式会跳出目前程序正在运算中的循环,回到 fcgi_accept() 等待下一个联机请求。如此可以省去一些网站服务器激活新的 fastcgi 行程的负担。

赞(0)
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com 特别注意:本站所有转载文章言论不代表本站观点! 本站所提供的图片等素材,版权归原作者所有,如需使用,请与原作者联系。未经允许不得转载:IDC资讯中心 » 实战 FastCGI(转)三-CGI教程,CGI文档
分享到: 更多 (0)