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

下面来看看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中后面声明的三个函数。

关于 “Boa源码分析 – 2 – boa.c流程” 的 1 个意见

发表评论

电子邮件地址不会被公开。