- 注册时间
- 2009-12-25
- 最后登录
- 2021-7-10
- 在线时间
- 3302 小时
- 阅读权限
- 200
- 积分
- 10
- 帖子
- 13416
- 精华
- 1
- UID
- 2036
  
|
想必所有人都了解CGI技术的通用性与效率有多糟糕。那么本文所描述的所有内容仅限于一种可以大幅度提高页面处理技术的东东,它的名字叫FastCGI,他的速度效率最少要比CGI技术提高5倍以上(要知道目前各种流行的页面处理技术ASP、PHP、JSP/Servlet还没有哪个能超过)。这并不是一种新技术,笔者在2000年第一次用过此技术。大家一定惊讶,既然有这种技术为什么我们还要编写这本参考手册,原因显而易见,虽然此技术已经产生自今,但中文资料还是少的可怜。好!废话到此为之止。
4 M. ~, D! P. J1 t# u3 n, l! ~( B! u W==================2 n9 U9 p# r1 W9 S
FastCGI的技术原理
q8 ^0 s v) {& \ Q==================
+ V3 J6 `7 x p% o, g8 @& j7 k# a! U如果想了解FastCGI的技术原理就要了解何为"短生存期应用程序",何为"长生存期应用程序"。
) x- [& U7 |; @/ \/ L8 F: R, } 先从CGI技术开刀,以下是CGI技术的理论:每次当客户请求一个CGI的时候,Web服务器就请求操作系统生成一个新的CGI进程。当CGI满足要求后,服务器就杀死这个进程。服务器对客户端的每个请求都要重复这样的过程。' t7 L' o' ]* p0 y5 x
而FastCGI技术的理论为:FastCGI程序一旦产生后,他可以持续工作,足够满足客户的请求直到被明确的终止。如果你希望通过协同处理来提高程序的性能,你可以请求Web服务器运行多个FastCGI 应用程序的副本。 % p! f! R6 c9 ^$ e1 K k. w
CGI就是所谓的短生存期应用程序,FastCGI就是所谓的长生存期应用程序。
: d5 \# ]1 X) {3 ?1 c由于FastCGI程序并不需要不断的产生新进程,可以大大降低服务器的压力。并且产生较高的应用效率。
2 ]* I/ M) `' K* B! [自今,较为流行的Java语言Servlet技术在设计上是以参考FastCGI的技术运行所设计。 " w8 o) ~; Q8 Z+ N+ Z& J0 v& U
==============, I4 b+ t |* R: R4 ~3 w8 ^3 O9 t# A
FastCGI的特点7 Z: O' i9 W- u' P, X, Z# s+ @" K
==============/ r# O' C2 ^$ s( r/ H% P
1. 打破传统页面处理技术
. u! q: s) e- Q# v$ A1 G传统的页面处理技术,程序必须与Web服务器或Application服务器处于同一台服务器中。这种历史已经早N年被FastCGI技术所打破,FastCGI技术的应用程序可以被安装在服务器群中的任何一台服务器,而通过TCP/IP协议与Web服务器通讯,这样做既适合开发大型分布式Web群,也适合高效数据库控制。 Y& N- R! l# i2 I @+ P& P
2. 明确的请求模式 % E0 L3 r- C `$ N: _ M
CGI技术没有一个明确的角色,在FastCGI程序中,程序被赋予明确的角色(响应器角色、认证器角色、过滤器角色)。 ' e V# L3 N+ l3 q) P. Q! z
3. 合理的程序结构 $ D! j& E. G# ]! s" m9 e+ D% L
起初,真的很讨厌FastCGI应用程序的结构要求。没关系,您经过一段时间编写后就会喜欢这种结构,只有这种完全规范的结构才能让您的程序更有效率。
& X/ D4 F' Y& ^3 {# I===============================$ L0 \' I" I' t9 D8 E% }
FastCGI技术支持语言与Web服务器
2 c4 X8 D5 Y( V- R# @===============================% f0 s9 f) r: l1 g- G* r& x
在本手册中仅介绍如何用Perl语言来实现FastCGI技术。实际,只要符合FastCGI技术规范,都可以使用本技术。FastCGI技术目前支持语言有:C/C++、Java、Perl、Tcl、Python、SmallTalk、Ruby等。
2 K2 @1 I6 C. q lFastCGI技术需要在Web服务器中安装支持部分才能运行,目前这部分已经完全支持:Apache、IIS、Pi3Web、Zeus等。
% S& I; \; ]+ l1. Apache服务器安装方法 7 h1 n$ e4 S/ E& }0 e
Linux系统Apache: & h* a8 r: u3 ?" Z; [0 V9 r- V
首先需要准备好一些安装包,如果您已经安装过Apache请参考以下步骤重新编译。
( X: v! X7 F. }' v9 B% IApache HTTP SERVER4 ?0 X+ Z4 @) E7 b& N1 n4 J/ q
在本文编写的时候,稳定版为apache.1.3.27。我们这里所使用的是源代码版http://apache.linuxforum.net/dist/httpd/apache_1.3.27.tar.gz
% t) l5 S7 R5 }其他版或则这个地址有错误请到www.apache.org找相关版本。 & @( b# i% U7 F" L) h* y: ]( z" V
Mod_FastCGI* Y- y- e6 h( n* S, ^" \. |
当前是2.3.0版!http://www.fastcgi.com/dist/mod_fastcgi.tar.gz5 J% h' ^1 m1 Z6 h: t
如果需要下载其他版或地址有错误请到www.fastcgi.com的Servers部分找到相关版本。 ! G" a1 v3 `2 z2 z4 u: W
好!安装包准备完毕,我们可以开始安装过程了。
. A; C) s& Q( \0 U1.解压Apache安装包tar zxvf apache_1.3.27.tar.gz& e' p7 [1 v! Y
2.解压mod_fastcgi.tar.gz包
# R% M4 G6 A# t3.将解压mod_fastcgi.tar.gz包后的目录复制到apache_1.3.27解压后目录的src/modules下并更名为fastcgi
2 k3 J' i; q! q) F8 R) b4.使用Apache安装配置命令./configure --activate-module=src/modules/fastcgi/libfastcgi.a4 C8 P) n' d$ g5 f; {
5.执行make
U: k' L1 Q/ \. N6.执行make install
6 z6 ~3 P# Y9 w7.如过程没出现错误说明安装成功。请测试/bin/httpd –l
2 @! S' X) W: @) \' n8.如果列表中有mod_fastcgi.c表示成功!
* W* Q- W5 V" s! C: g, e" }配置Apache的httpd.conf
: x0 e/ ~) G3 I: O: N* j1.设置FastCGI文件的处理类型,请在httpd.conf包含Addhandler部分添加一句2 H4 d& }" u# c$ X& \
AddHandler fastcgi-script .fcgi .fpl$ y* t- w: h: u/ |
这样,Apache就知道.fcgi与.fpl处理为fastcgi文件) v# a( f7 W: B+ L7 g6 n) Z7 D
2.还要为您的网站设置ScriptAlias解析,这个解析很容易,与普通CGI设置方式相同! ~, \# R1 b4 `
ScriptAlias /fcgi-bin/ /usr/local/apache/fcgi-bin/- X7 ^) Y" a+ l) W
我这里这样设置后fcgi程序将安装在/usr/local/apache/fcgi-bin/下
" h- W h3 }# ]0 M& f+ M; B测试安装test.fcgi 2 R5 Z* d$ Y( e" x
#!/usr/bin/perl
' ]# B( {2 Z" }$ C( {/ Kuse FCGI;
7 W. t$ i8 R6 d9 d5 S9 Y* [. Umy $count =0;
2 O. U$ b8 f" D: M4 \my $request = FCGI::Request();
$ _2 c" X6 v7 N* }while($request->Accept() >= 0) {
' r+ S. \. `; g) e6 m8 w8 U" E print “Content-type: text/html\n\n”;* r$ d7 K9 N7 `+ @
print $count++;+ Y$ Y5 P9 ^' H% d
}
+ P1 r. ` `9 r) o7 \$ v4 K2 S' X3 e! bWin32系统Apache:
8 U7 X/ T0 Z% f4 v% E" E& G. c准备安装包
5 N% n" G$ |+ _* ]/ }7 P' X1.下载Apache for win32的版本,我们就下稳定版1.3.27. W: ]& H+ K" u. V$ x
http://nagoya.apache.org/mirror/httpd/binaries/win32/apache_1.3.27-win32...
* [3 i) D) ^4 I$ d! Q1 {2.下载ActivePerl% {- Z. c% t& U: f9 ~/ l
http:// hoowa .tab.net.cn/soft/ActivePerl-5.8.0.802-MSWin32-x86.msi
0 t& _" S+ Y& K* d1 W; f6 G% t7 @配置Apache以支持http服务 * D/ g' X `' ]
1.安装ActivePerl,最好安装到C盘,因为Apache 默认是在C下
8 V V- l3 A1 S2 q0 W: W) P2.安装Apache,装好后修改其目录下的conf文件夹下的httpd.conf文件7 t# V" m8 E5 G7 C, {+ ]7 M
寻找到ServerName。这里定义你的域名,如果前面有#,记得删除它。
0 @2 _( C' o9 |: C, ~9 q 寻找到ServerAdmin。这里输入你的E-Mail地址。如果你仅仅是单机使用,改不改没什么关系% y3 o. \- x N
在ServerName下面不远有个Options 和AllowOverride,将他们后面的参数去掉改为All,请注意大小写 9 B) s2 G+ R' E- ]
配置Apache以支持CGI
; l$ l: ]; [% P1 A1.假设Perl安装在C:\Perl目录,找到ScriptAlias /cgi-bin/ "C:/Apache/cgi-bin”(假设您将apache装在c盘下),那么C:/Apache/cgi-bin就是您存放cgi的目录2 Z' Q! l7 a* q$ H
2.在ScriptAlias 这句下面,Options 和AllowOverride,将他们后面的参数去掉改为All L$ F8 a8 k& ]8 W8 P: k# L+ G
3.寻找到AddHandler cgi-script .cgi。删除前面的#,在后面加上 .pl
! c6 L: I* M) P/ |6 o注意:当您浏览cgi程序出现500错误的时候可以看一下您的程序,是不是以:#!C:\perl\bin\perl开有。
8 l6 P2 e6 @1 e4 N配置Apache以支持Fastcgi
' o; N/ [2 [) w9 R3 X6 H; D1.下载mod_fastcgi-AP1.dll4 T3 ~' q. o+ H" n @# J
http://www.fastcgi.com/dist/mod_fastcgi-AP1.dll
7 C8 n( M- }+ F: t+ N2.将.dll文件复制到../modules下,并修改httpd.conf文件,加入这两句: }# D a9 C$ y+ e& m
LoadModule fastcgi_module modules/mod_fastcgi-2.4.0-AP1.dll2 y" ^' z0 M; g% G! p4 Z" @- D
AddHandler fastcgi-script .fcgi .fpl
# P: i' m2 N7 o. I$ K& X/ L3.保存后就可以运行fcgi程序了.
8 ?/ c# P' ^9 M* _* Q/ G) \注意:如果没有安装FCGI package,就在命令提示符下输入
: M. {- d9 j5 kppm$ q+ m3 L0 H+ ]+ ^/ \ I
search FCGI
7 _( L1 K) D+ I& XINSTALL FCGI $ c- l9 d1 N$ j5 ^
2.IIS服务器安装方法 (略): B. G6 Q' f- z
3.安装FCGI.pm模块 0 {) _5 Q( ]8 h, \
在Linux系统中:
+ I* F2 v6 a) v% N- B8 y. ^$ f6 i3 c 登陆以下地址http://www.cpan.org/modules/by-module/FCGI/3 x7 D# s7 ]! D
下载最新版的FCGI(在本文编写的时候为)http://www.cpan.org/modules/by-module/FCGI/FCGI-0.67.tar.gz
( {2 r. M8 u# ?4 K 解压tar zxvf FCGI-0.67.tar.gz
# s8 Y" [4 B- H5 m9 y1 S 编译perl Makefile.PL
9 S! ], t' f, g, ~% r- q" y make$ C& x; O% K( c" V
make install
4 c# m% e2 Y6 y' U1 e" S) n 完成!
0 k4 w$ F+ X* M8 E在Windows系统中,以及使用ActivePerl:8 N; X! X) _1 ?3 ? T6 G
进入MS-DOS模式或cmd环境
6 ~5 [$ w: R K' M. H k7 ~" u+ o3 Y 输入ppm命令7 R8 c$ l& m/ T1 i4 U! I
在ppm提示符下输入install FCGI
) U4 M( L9 B- N- k0 O# H 自动安装…..
* I" [, Z8 r; g 完成
5 m u v# ` o' e& L===================
* A0 o' I' }; A6 V0 M9 e5 D4 h7 R9 ^FastCGI的程序结构1 G7 e5 F% i& O( z! w- K
===================' l* N, h% j, _. E0 L
一个FastCGI的应用程序主要包含两部分:初始化部分、回应循环部分。 # O$ ?! n6 l! g
#初始化编码+ l" N) X0 {5 J$ P
#开始回应循环) e# K1 _) v& G K( r2 h. p# ~
#回应内容$ w0 a2 u- E6 \+ G. A1 I/ ]# w( T
#回应结束循环
* M; m1 |3 d5 R* G+ @#!/usr/bin/perl 1 I6 O9 ^& l$ b! Y4 m5 t! H
#以下内容都是初始化部分
. R2 L1 u4 t! k& F; fuse FCGI;
( ]( y* X) m3 X, Z1 x0 ^3 } d8 hmy $count =0;) p6 ?3 S& U. W. C, o5 a, G
my $request = FCGI::Request();
% U' ]- d% W# g6 `0 d#以下内容都是回应部分
: q$ o) r0 |* e9 o$ _& F m L zwhile($request->Accept() >= 0) {3 X' s9 r( A) A' m9 ~ L- ]
print “Content-type: text/html\n\n”;
J& i; k6 j! V9 U print $count++;
3 _$ [7 L2 q% ?, a8 a2 }} : L) @7 W( K3 K' U4 I
当应用程序被初始化后,初始编码仅仅只运行一次。初始编码就像开启数据库和编译后的应用程序一样常常是十分耗时的操作。: j: X$ C) Y7 t+ G
整个回应部分循环始复,直到客户要求终止。回应循环部分从调用FCGI_Accept开始。FCGI_Accept程序执行后会挂启程序(程序循环部分虽在内存中,但是并不运行),除非客户对FastCGI应用程序发出请求。客户的请求一旦到达程序,FCGI_Accept会运行回应部分的内容一次,然后再次中止程序,等待客户下一个请求的到来。整个回应循环部分只会在系统管理员或者Web服务器杀死了FastCGI应用程序后才能消除。
9 r7 n5 \* y3 }# a4 q7 |====================
: V9 {$ ~6 N% {4 _FastCGI应用程序举例7 Q3 \' D4 h, |# H7 s7 B, L
====================9 C: O1 h* E4 R3 o2 c' x. c. \
1.一个典型的FastCGI应用程序,这个程序将在初始化部分初始变量$count为0,每次请求运行都将累加一。
e/ W' t4 \4 ?: f2 r8 Y#!/usr/bin/perl
6 ~" E' j# S+ L' f! Uuse FCGI;
! p6 v" v6 a2 C( g: A3 suse strict;
' N: I5 A. P: j) q" b) q8 x- Gmy $count = 0;# N9 y' K$ w/ i4 Z7 S0 m
my $request = FCGI::Request;
3 p, V- {0 L: N0 twhile($request->Accept >=0){
! |! ~5 Q! G4 b, T, a) F$ k' U' V $count++;5 R! w, p [6 {5 U! ?$ h
print “Content-type: text/html\n\n”;
) _0 R* ?% f6 d9 ?6 @ print qq~ 1 ~0 S: _/ R0 w" J& G
FastCGI 7 G8 @9 Z; N& t6 T1 {0 d# t, n
请求已发生次数:$count " C! V; \. P g0 ~3 B* F( H1 h7 r: X
~;7 F/ D+ B- |2 D* j
}
% S# o6 V5 ]* p8 {2.FastCGI程序为长生存期应用程序,如在设计中有缺陷会产生内存溢出问题,对服务器造成安全隐患。且这种溢出是不可预见性。本程序初始化了一个控制变量,一旦用户请求次数达到预先设置的数量程序结束,下一次请求将重新初始化。, X' R7 k9 O( ~7 S
#!/usr/bin/perl, W& v/ n0 {/ z# a
use FCGI;
7 X* g' v3 @! f$ L' x3 a: y/ Zuse strict;
6 u" C5 n) J& M2 Mmy $count = 0;) M/ \! O# i+ h y3 X# a
my $request = FCGI::Request;$ |% z; i. ~" K- y4 b
my $session_life = 1000; * q+ y7 @6 W2 z! i2 e/ U. J4 T( P
while($request->Accept >=0){% D# q7 V$ _6 Z! e: V a! I
$count++;1 F/ g$ V9 }7 \4 U6 A
print “Content-type: text/html\n\n”;% a0 B9 B) h* _! G) t: q
print qq~ " N( R' x, Y3 a4 p) ~- R
FastCGI
' f2 j/ Q7 {1 Y: c8 K8 J% ]8 O请求已发生次数:$count 2 T; n2 r+ ]! f7 U3 R' a
~;
5 ]& G% u6 a" k# M) E9 {if ($count>$session_life) {
" m! ^* m( K, |( r end;
# S! U5 J: e2 b8 F' F}
0 v# \0 |* k+ e' v Q3 Z} 0 ?9 A! f( U& k, s' a4 s
======================: Y+ E n8 G0 \7 ~; e
书写FastCGI的规范要求
, x# l @4 M+ x5 c+ W% W- Z0 J: t======================
& S: }' L) c9 l- CFastCGI应用程序对编写者提出了一种严格要求的挑战。
0 t* c b6 u& t8 D6 k ?" u1.在程序头必须加载strict模块。格式为use strict;
$ v) s: {7 v! s8 ^! c, z" ~) T2.任何变量的使用之前一定不要忘记声明变量my $variable;
6 J2 w# Q5 ] K) W4 s% ~3.不要将一些初始化的内容放在回应部分,这样不仅无法提高效率还会降低效率。例如对数据库的连接部分。
5 U! B0 E$ I0 Q6 j4.同样,也不要把什么东西都放在初始化部分。
2 x3 O6 }$ h9 O! t$ ?" o5.在程序调试后发现有BUG或修改,请先直接修改程序。然后杀死系统中的程序进程,要不你的修改永远不被执行。如果杀不好,就重启Apache吧。
6 Y2 K; I- Q9 Z8 l+ q==================
9 q& X) J$ h$ F! qFCGI.pm模块的使用
1 s( u8 p2 T9 [. Z==================
f8 M& j! U3 s) l+ v模块名称:Fast CGI Module$ T5 H& ]3 C. ^
使用格式: - } H4 W+ \- r! |
use FCGI;
* c5 v( |. Z6 C$request = FCGI::Request();
# M( j6 e7 V+ g& g( Wwhile($request->Accept() >=0) {/ K0 r( S, W- I; G! W$ P. W
#CGI Process..9 R5 m$ d1 S# E3 u/ U6 r
}
6 H- k1 s& e! f, e8 ~8 k; \FCGI支持的使用方法:
( \: W ?2 g; Y3 S ^3 g2 ^FCGI::Request# w5 ?* f/ o) d4 S
$request = FCGI::Request(. L" u& A" [0 p9 n: u i) z
$input_fh,0 S% ]0 v! d$ \* ^
$output_fh,4 J" t' }8 g6 a
$error_fh,$ X4 A) [3 v1 Q4 N7 C, T
fileno($socket),2 J1 f" g9 B3 \& W! {! Q. U `
);' I) m* E) G q. ]
$input_fh,$output_fh,$error_fh是设置input/output/error的句柄,默认情况下分别是STDIN、STDOUT、STDERR 2 V6 E5 q( I: T' o' S1 @- K2 T6 w" w
$socket的用处在分离FastCGI应用程序与Web服务器的情况下才需要使用。它允许程序通过设置的套接字与Web服务器通讯。 ) Q- a- D1 J; @ s: |
FCGI::OpenSocket(path, backlog)) i( ^" j+ \. L" Z% \/ R
在程序中创建一个套接字,在建立独立的FastCGI应用程序中用到。- A% \# r6 s5 z0 G; M" b
FCGI::CloseSocket(socket)
3 ] {6 v; {# t5 K关闭打开的FCGI::OpenSocket。
2 ^7 q" ^+ o' o- @! R" [, O9 R$request->Accept
7 N% t8 P; p0 @4 k5 V" w+ ]8 F, q当有新的请求产生,以上句子返回0,其他情况下返回-1。
$ w- Q* k& \- Y) V( U& J% |+ B$request->Attach* ^( j% l% L; D4 [3 b' x
重新设置文件句柄连接到相关服务器上。
6 r' q1 z+ [) D, {- K, n8 Z$request->Detach
' i! b7 K6 u8 q! V% y. l临时分离文件句柄。7 k$ Z/ W$ Z/ \6 A' B7 H
$request->Finish
. [0 I' E8 Z" b: n结束当前请求,这本来由Accept来自动完成,但有写程序中在返回给客户请求后可能需要做一些其他工作,比如操作数据库。0 `6 t ]+ x' X8 ^/ F# Z. O5 r$ L2 L
$request->Flush3 ~# g* q" _; M8 D: I4 t$ W& o' J, O3 B2 s
清洗当前连接中的所有输入输出。# N- H2 N: F. I" O7 E+ x
$env = $req->GetEnvironment()
% p/ {" B, N u7 s Y9 }% Q返回来环境变量。
7 X2 p: S; S3 f! o S) a: z$ X6 c($in, $out, $err) = $req->GetHandles()
2 I& b& I; A# q2 i& {7 i5 e9 I返回标准文件句柄。
$ I5 O7 o2 b ]+ R) X$isfcgi = $req->IsFastCGI()
" S1 F- Q" F8 T返回当前程序运行的状态是否FastCGI。
, |( D" k# s2 J$ L. ^========================
, r8 v2 f! P: X$ i; uFastCGI的效率与应用范围
: b& g9 U' k B7 ~. x9 e# F======================== i9 F0 w+ I* O0 q
效率这个词不仅仅一词出现过。我也不仅一次讲解过效率的由来。7 y- X* q6 w- h, m
现在所有的解释性语言(Perl、Java、Python)的运行效率,都要比编译形语言(ANSI C)慢的多(一到几十倍)。但奇怪的是,大家似乎看到的是Jsp/Servlet不错的效率。这是因为在网络世界中,对效率的影响不在语言的本身,而在以下几个方面:1、技术模型 2、系统I/O带宽 3、程序设计。! j$ R/ _& b( v! e% E
FastCGI是一种半成熟(这样说是因为C语言成熟用了十多年,自今Java语言与C++语言都是不断在完善中)的技术,它经过多年的应用实践已经获得广泛的支持。
8 O" C2 g" d( X! ?. c. nFastCGI技术适合各种程序设计上的要求,我们建议使用在以下要求中:9 K# x# f, n5 ^+ i
1.想使用J2EE以外的一种高效率OpenSource技术。
- G# t* q2 I2 ^. ~- I( S) c1 O ^; Z2.分布式数据库控制系统
( b6 g2 B) ]3 X/ A. r- G1 o3.高访问量页面访问系统# \5 W* i6 T* B) Q @1 A
4.性能评测! y4 Q2 t1 Y- c/ U0 Q
5.混毕业设计
( L/ L" g1 a5 l) t==============3 r4 o% w! U' y- T- H5 B/ T2 ^; O( A
FastCGI的资源# ^3 i9 Y3 A. L+ |/ v% ]% A
==============
; b5 c9 n* x" v# `, fFastCGI技术官方网站:http://www.fastcgi.com6 l5 V$ A5 b0 }' L, N
FastCGI中文讨论组:http://www.ilcatperl.org: A x; A: v+ y2 y: O+ o
中国Perl组织:http://www.perlchina.org( }2 U! O+ O4 N' n2 s- Y, Z
FastCGI技术案例:( q0 }4 E6 I2 r9 |" ~
http://www.hotmail.com8 d. K# i) O" t; F# E
http://www.yahoo.com
( n4 ~' j4 Y5 h* p# h; q3 Ghttp://www.donews.com
4 W" i& ]. [& p( |' q======
1 ^7 e( ] c+ ]: t声明% ], i2 H' @' L* ?1 c. I5 k
======
1 R7 [+ O4 Z; m4 O& x- _本文作者: hoowa .sun/ K" D3 \! i! M; e
感谢:royce(部分内容是他编写的)、bloves(找到很多Bug)' z( M7 _: r+ p+ @7 c* |% |1 b
获得方法:http://www.ilcatperl.org" C0 ^$ [" a; d |3 [2 Q/ i$ r" O8 @
任何网站(非赢利性)转载无需经过作者同意,请保留任何版权声明。
4 S" L; G9 n- }1 T% S未经作者同意禁止任何杂志转载。
! t8 M& q( O+ X& \========================= |
|