BOA是一个小型的C开源软件。如果看了_Advanced Programming in the UNIX Environment(APUE)_和了解网络通信之后,再读源代码可能会更清晰一些。也可以看看其他的开源项目 最值得学习阅读的10个C语言开源项目代码。
从哪儿开始?
源码位于/src下,一般来说应该从Makefile看起。我选择先从函数入口main开始。
c = umask(~0600);
umask和chmod是配合使用的,是登录系统创建文件和目录的默认权限,用反码表示。若为umask(022)
,则代表文件权限为644(6-0,6-2,6-2),目录权限为755(7-0,7-2,7-2)。
if (dup2(devnullfd, STDIN_FILENO) == -1) {
DIE("can't dup2 /dev/null to STDIN_FILENO");
}
dup2用来复制文件句柄,使得标准输入的文件项指向/dev/null。
while ((c = getopt(argc, argv, "c:r:d")) != -1) { //分析输入参数
switch (c) {
case 'c':
if (server_root) // server_root定义在config.c中的指针
free(server_root);
server_root = strdup(optarg); //拷贝optarg到server_root
if (!server_root) {
perror("strdup (for server_root)");
exit(1);
}
break;
...
}
getopt分析终端输入的参数,按照用法Usage: %s [-c serverroot] [-r chroot] [-d]在终端输入 ./boa -c /etc/boa -r [chroot] -d则可以配置服务器。-d指定不以守护进程方式运行。
./boa是argv[0],-c /etc/boa是argv[1],-r [chroot]是argv[2],-d是argv[3]。
对于argv[1]的解析是这样的,把自定义的参数/etc/boa作为optarg拷贝到server_root指针指向的内存,即由控制终端指定服务器的根目录。
然后是一个函数fixup_server_root()
#fixup_server_root()# fixup_server_root()在boa.c文件最下面,用来验证server_root是否合法。如果终端输入的参数没有指定server_root,则由defines.h中的宏
#ifndef SERVER_ROOT
#define SERVER_ROOT "/etc/boa"
#endif
指定。
if (!server_root) {
#ifdef SERVER_ROOT
server_root = strdup(SERVER_ROOT);
if (!server_root) {
perror("strdup (SERVER_ROOT)");
exit(1);
}
char *server_root只定义不初始化的时候值是一个随机的物理地址。if (!server_root)中是怎么执行的?(TBD)
dirbuf = normalize_path(server_root);
是转码server_root的相对路径,是绝对路径就不转码。
read_config_files()
read_config_files()定义在config.c中,通过yyparse读取配置文件。
void read_config_files(void) //目录已经切换到/etc/boa目录下
{
char *temp;
current_uid = getuid();
yyin = fopen("boa.conf", "r");
if (!yyin) {
fputs("Could not open boa.conf for reading.\n", stderr);
exit(1);
}
if (yyparse()) {
fputs("Error parsing config files, exiting\n", stderr);
exit(1);
}
if (!server_name) {
struct hostent *he;
char temp_name[100];
if (gethostname(temp_name, 100) == -1) {
perror("gethostname:");
exit(1);
}
he = gethostbyname(temp_name);
if (he == NULL) {
perror("gethostbyname:");
exit(1);
}
server_name = strdup(he->h_name);
if (server_name == NULL) {
perror("strdup:");
exit(1);
}
}
tempdir = getenv("TMP");
if (tempdir == NULL)
tempdir = "/tmp";
if (single_post_limit < 0) {
fprintf(stderr, "Invalid value for single_post_limit: %d\n",
single_post_limit);
exit(1);
}
if (document_root) {
temp = normalize_path(document_root);
free(document_root);
document_root = temp;
}
...
if (dirmaker) {
temp = normalize_path(dirmaker);
free(dirmaker);
dirmaker = temp;
}
}
用来解析boa.con中下列配置文件:
DocumentRoot /var/www
DirectoryMaker /usr/lib/boa/boa_indexer
Open_logs()
函数作用是打开记录文件
if (error_log_name) {
if (!(error_log = open_gen_fd(error_log_name))) {
DIE("unable to open error log");
}
特别注意DIE的用法,用来输出调试信息,包括行号和出错信息。
if (fcntl(STDERR_FILENO, F_SETFD, 1) == -1)
fcntl改变已打开的文件性质,设置close-on-exec旗标为1,原理和作用参见[4][5]。
create_server_socket()
boa服务器建立socket连接。
int server_s;
server_s = socket(SERVER_AF, SOCK_STREAM, IPPROTO_TCP);
if (server_s == -1) {
DIE("unable to create socket");
}
/* server socket is nonblocking */
if (set_nonblock_fd(server_s) == -1) {
DIE("fcntl: unable to set server socket to nonblocking");
}
/* close server socket on exec so cgi's can't write to it */
if (fcntl(server_s, F_SETFD, 1) == -1) {
DIE("can't set close-on-exec on server socket!");
}//原理同见[4][5]
/* reuse socket addr */
if ((setsockopt(server_s, SOL_SOCKET, SO_REUSEADDR, \
(void *) &sock_opt, sizeof (sock_opt))) == -1) {
DIE("setsockopt");
}
if (bind_server(server_s, server_ip) == -1) {
DIE("unable to bind");
}//server_ip由哪里传来?TBD
if (listen(server_s, backlog) == -1) {
DIE("unable to listen");
}
return server_s;
bind_server(server_s, server_ip)
绑定ip和协议配置
从这里可以看出boa每次只建立了一个连接。
init_signals()
sigaction定义如下:
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
}
函数如下:
struct sigaction sa;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);//清空此信号集
sigaddset(&sa.sa_mask, SIGSEGV);//将SIGSEGV信号加入&sa.sa_mask信号集
sigaddset(&sa.sa_mask, SIGBUS);
sigaddset(&sa.sa_mask, SIGTERM);
sigaddset(&sa.sa_mask, SIGHUP);
sigaddset(&sa.sa_mask, SIGINT);
sigaddset(&sa.sa_mask, SIGPIPE);
sigaddset(&sa.sa_mask, SIGCHLD);
sigaddset(&sa.sa_mask, SIGALRM);
sigaddset(&sa.sa_mask, SIGUSR1);
sigaddset(&sa.sa_mask, SIGUSR2);
sa.sa_handler = sigsegv;//设置对应的回调函数
sigaction(SIGSEGV, &sa, NULL);
sa.sa_handler = sigbus;
sigaction(SIGBUS, &sa, NULL);
sa.sa_handler = sigterm;
sigaction(SIGTERM, &sa, NULL);
sa.sa_handler = sighup;
sigaction(SIGHUP, &sa, NULL);
sa.sa_handler = sigint;
sigaction(SIGINT, &sa, NULL);
sa.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &sa, NULL);
sa.sa_handler = sigchld;
sigaction(SIGCHLD, &sa, NULL);
sa.sa_handler = sigalrm;
sigaction(SIGALRM, &sa, NULL);
sa.sa_handler = SIG_IGN;
sigaction(SIGUSR1, &sa, NULL);
sa.sa_handler = SIG_IGN;
sigaction(SIGUSR2, &sa, NULL);
sigaction函数用于改变进程接收到特定信号后的行为。该函数的第一个参数为信号的值,可以为除SIGKILL及SIGSTOP外的任何一个特定有效的信号。第二个参数是指向结构sigaction的一个实例的指针,在结构sigaction的实例中,指定了对特定信号的处理,可以为空,进程会以缺省方式对信号处理;第三个参数oldact指向的对象用来保存返回的原来对相应信号的处理,可指定oldact为NULL。如果把第二、第三个参数都设为NULL,那么该函数可用于检查信号的有效性。
Reference
[1].http://www.csdn123.com/html/topnews201408/93/6593.htm
[2].http://www.360doc.com/content/12/1225/15/8363527_256180353.shtml
[3].http://blog.csdn.net/jemofh159/article/details/7913120
[4].http://blog.csdn.net/chrisniu1984/article/details/7050663
[5].http://blog.csdn.net/sunlylorn/article/details/6363727
[6].http://www.cnblogs.com/hoys/archive/2012/08/19/2646377.html
[3].http://www.fookwood.com/archives/569