BOA源码笔记1

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



Previous     Next
/
Published under (CC) BY-NC-SA in categories c&&c++  tagged with