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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | #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 ; } <br> //这里进行业务处理:cgi.in char **envp; int size = 200; char *result = ( char *)alloca(size); strcpy (result, "Status: 200 OK\r\nContent-Type: text/html\r\n\r\n" ); strcat (result, "<html><h1>TestCGI!</h1></html>\r\n" ); FCGX_PutStr(result, strlen (result), cgi.out); } return 0; } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架