Boa源码分析 – 2 – boa.c流程

于 2012年03月24日 发布在 linux应用 跳到评论

下面来看看boa.c吧

/* set umask to u+rw, u-x, go-rwx */
    c = umask(~0600);
    if (c == -1) {
        perror("umask");
        exit(1);
    }
    devnullfd = open("/dev/null", 0);
    /* make STDIN and STDOUT point to /dev/null */
    if (devnullfd == -1) {
        DIE("can't open /dev/null");
    }
    if (dup2(devnullfd, STDIN_FILENO) == -1) {
        DIE("can't dup2 /dev/null to STDIN_FILENO");
    }
    if (dup2(devnullfd, STDOUT_FILENO) == -1) {
        DIE("can't dup2 /dev/null to STDOUT_FILENO");
    }

这里设定了权限掩码为~600,也就是生成文件权限为rw——-,然后打开/dev/null,它相当于一个黑洞,输入任何东西都会丢失,读取也什么都读取不到,因为服务器进程是在守护进程,所以将标准输入输出定向到/dev/null。下面看getopt:

    while ((c = getopt(argc, argv, "c:r:d")) != -1) {
        switch (c) {
        case 'c':
            if (server_root)
                free(server_root);
            server_root = strdup(optarg);
            if (!server_root) {
                perror("strdup (for server_root)");
                exit(1);
            }
            break;
        case 'r':
            if (chdir(optarg) == -1) {
                log_error_time();
                perror("chdir (to chroot)");
                exit(1);
            }
            if (chroot(optarg) == -1) {
                log_error_time();
                perror("chroot");
                exit(1);
            }
            if (chdir("/") == -1) {
                log_error_time();
                perror("chdir (after chroot)");
                exit(1);
            }
            break;
        case 'd':
            do_fork = 0;
            break;
        default:
            fprintf(stderr, "Usage: %s [-c serverroot] [-r chroot] [-d]\n", argv[0]);
            exit(1);
        }
    }

getopt对程序的参数进行处理,boa的很简单,选项只有三个。getopt有三个参数,前两个参数就是main函数的参数,第三个参数是一个字符串,每个字符代表一个选项,如果选项后面有冒号,说明这个选项后面必须要跟参数,当然,实际调用程序的时候,参数的顺序可能会不一样。如果选项有一个关联值,则外部变量optarg会指向这个值,为了防止optarg被重新赋值,server_root = strdup(optarg) 把字符串复制到server_root。选项处理完毕的时候getopt会返回-1.

getopt还有相关变量 optind,opterr,optopt。

如果遇到一个无法识别的选项,getopt返回一个问号(?),并把它保存到外部变量optopt中
如果选项要求关联值,但是没有提供,那么返回一个问号。
optind被设置为下一个待处理单数的索引,getopt用他来记录自己的进度。getopt重写了argv数组,所有非选项参数集中在一起,从argv[optind]开始。

perror()用来将上一个函数发生错误的原因输出到标准错误(stderr)。参数所指的字符串会先打印出,后面再加上错误原因字符串。此错误原因依照全局变量errno 的值来决定要输出的字符串。

整个部分的作用是,-c指定服务器目录,-r指定chroot目录(暂时不知道为啥这样做),-d指明不以守护进程方式运行?

    fixup_server_root();//修正server_root
    read_config_files();//读取配置文件
    open_logs();//打开日志文件
    server_s = create_server_socket();//创建套接字
    init_signals();//初始化信号。。
    drop_privs();//更改权限
    create_common_env();//环境啥的?环境变量?
    build_needs_escape();//escape的意义不是很清楚,好像很常见的样子

以上函数分析是大头。。。

    if (max_connections < 1) {
        struct rlimit rl;

        /* has not been set explicitly */
        c = getrlimit(RLIMIT_NOFILE, &rl);
        if (c < 0) {
            perror("getrlimit");
            exit(1);
        }
        max_connections = rl.rlim_cur;
    }

getrlimit获取当前进程的资源限制。RLIMIT_NOFILE表示每个进程能打开的最大文件数。rlimit结构体中包含了两个变量,rlim_cur表示当前限制,rlim_max表示最大限制,硬限制。

    /* background ourself */
    if (do_fork) {
        switch(fork()) {
        case -1:
            /* error */
            perror("fork");
            exit(1);
            break;
        case 0:
            /* child, success */
            break;
        default:
            /* parent, success */
            exit(0);
            break;
        }
    }

调用fork,父进程退出,如果父进程在shell中执行,那么会被认为执行完毕。
到底为止,创建守护进程的几个规则都已经完成(APUE page 342),除了那个setsid,setsid创建一个新会话,成为新会话首进程,新进程组组长。

最后的select_loop(server_s)就开始接收链接什么的了吧。重点?

下一次将分析boa.c中后面声明的三个函数。

本文共有 一条评论 | 沙发:文章评论

  1. tao说道:

    不知你弄明白read_config_files函数怎样读取配置文件没?

留下评论!

:wink: :twisted: :roll: :oops: :mrgreen: :lol: :idea: :evil: :cry: :arrow: :?: :-| :-x :-o :-P :-D :-? :) :( :!: 8-O 8)