Mathopd源码分析——启动
2008-12-23 23:08
155 查看
本文较为详细的分析了Mathopd的启动过程。如果将此启动过程一般化,便可以作为我们开发Linux下服务器程序的蓝本。
要看Mathopd的启动过程,当然应先从main函数看起,看看做了哪几个大的步骤。如果你对本文感兴趣,阅读时最好参考源代码。
1. 处理命令行参数
2. 打开/dev/null设备
3. 读入配置信息(这很重要,我们以后将着重分析)
4. 启动配置文件中指定的所有服务器,并侦听连接
5. 根据配置信息设置根目录
6. 根据配置信息,设置程序运行的uid和gid。
7. 打开pid文件
8. 初始化日志模块
9. 根据命令行参数,决定是否让程序后台运行。
10. 设置信号处理函数
11. 向pid文件中登记pid
12. 初始化一些缓存区
13. 进入处理http协议的主函数。
我们来详细的看看各个步骤:
1. 处理命令行参数
while ((c = getopt(argc, argv, "ndvf:t")) != EOF) {
//...
}
getopt是Unix/Linux中解析命令行参数的函数。关于getopt的用法我这里不做详细的说明。关于getopt的详细介绍请参考本文后的相关链接
-n: 将系统放在前台运行。系统默认是在后台运行守护进程(服务器程序一般作为后台守护进程执行),如果有该参数,系统将不会放到后台运行。这个参数在调试的时候很有用处。
-d: 系统以调试模式启动。
-v: 给出系统版本。若有该参数,系统不会启动服务,而是打印出版本号后直接退出。
-f <filename>: 指定配置文件路径。若有该参数,系统将从指定文件加载配置信息,若无该参数系统从标准输入中接受配置信息。
-t: 如果有该选项,系统的调试信息既会输出到日志文件,也输出到标准错误上,这个选项在调试时很有用处。
2. 打开/dev/null
null_fd = open(devnull, O_RDWR);
/dev/null是Unix-Like操作系统中的一个特殊的文件,它就像一个“黑洞”,丢弃所有向它写入的数据(但返回值为成功)。
打开此文件的目的是为以后将标准输入,标准输出和标准错误重定向到该“黑洞”做好准备工作;
一个后台运行的守护进程,不可能用到这三个标准的描述符,因为这三个描述符均和终端有关,而后台的守护进程没有一个终端作为他的父进程。
3. 读入配置信息
message = config(config_filename);
if (message)
die(0, "%s", message);
config函数完成了整个系统的配置,config模块是Mathopd设计的比较优雅的一个模块,他应用了编译原理中词法分析中的自动状态机的理论,使一个复杂的解析过程,变的清晰易懂。配置模块在以后的文章中会进行详细的分析。
这个函数和我所见过的一般的函数设计风格有所不同。一般的函数设计是返回一个预先定义好的错误码来表示是何种错误。而这个函数的返回的是一个指向一个常量字符的指针来表示错误。其实这样的设计可以简化代码编写,使代码看起来更加优美。作者预先定义好了很多全局的常量的字符串用来表示错误。这样做既可以判断是何种错误,又可以很方便的使用打印语句打印出是错误信息(不用书写冗长的错误码和错误字符串的映射代码)。下面是一个错误字符串的定义。
static const char e_memory[] = "out of memory";
4. 启动服务器,并侦听连接
s = servers;
while (s) {
startup_server(s);
s = s->next;
}
Mathopd的“服务器们”是一个全局链表。在读入配置信息的同时完成了这个链表的建立。很容易读懂,我们遍历链表,并对每一个服务器对象进行startup_server。不要以为startup_server很神秘,它是一种我们很熟悉的代码,几乎所有的关于C的socket编程的服务器范例都会有讲解。这里我再不厌其烦的重复一遍,权当是回顾一下socket服务器编程。
a). 创建socket
s->fd = socket(AF_INET, SOCK_STREAM, 0);
b). 设置socket选项
这个倒是值得一提!
SO_REUSEADDR : 这个选项在编写服务器程序时一般都要设置。如果没有设置该选项会导致两个问题:1.服务器的快速起停,会引起地址被占用的错误。2. 在一个服务器上绑定不同ip相同端口会引起地址被占用的错误。显然,这两个问题对HTTP服务器来说是不允许存在的。我在参考链接中给出了我检索的关于SO_REUSEADDR 选项的说明
FD_CLOEXEC : 这个我不太明白
O_NONBLOCK : 文件描述符设置为非阻塞,前面介绍过,这里的socket采用的是I/O多路复用的同步非阻塞模式。
c). 绑定地址和端口
bind(s->fd, (struct sockaddr *) &sa, sizeof sa);
d). 开始侦听
listen(s->fd, s->backlog);
6. 打开pid文件
这为登记pid做准备,登记pid的步骤一定要放在将程序变成后台服务之后,因为我们登记的是后台服务器的进程pid,它和当前运行的进程pid不同,当前运行的进程是后台服务器进程的父进程
7. 初始化日志模块
为记录日志做准备,如果要在标准错误上显示日志,这里要将标志错误从定向。为什么要重定向呢?因为我们马上就要将/dev/null重定向到标准错误了。到那时,我们向标准错误中写日志,就不会再有任何反应了。
8. 后台运行
关于将程序变为后台守护进程运行的理论比较复杂,这里不再叙述。但我在参考链接中将会给出这一理论的详细说明
9. 设置信号处理函数
程序成为后台守护进程后,就没有终端与之交互了。在这里我们依赖信号来与服务器交互。信号处理函数,就是我们与服务器交互的函数。
10. 登记pid
这里就是我们登记后台服务进程的pid最佳时机了
11. 初始化缓冲区
这里其实就是创建一些系统需要的资源,如连接、输入输出缓冲区等。资源的个数及大小由系统配置决定。
12. 进入处理http协议的主函数
完成启动后,我们就要干正经事了。
参考链接
1. IBM DeveloperWorks的文章一直都很经典:使用 getopt() 进行命令行处理
2. 维基百科中关于/dev/null的详细说明。
3. 维基百科中关于有限自动机(DFA)的介绍
4. 关于SO_REUSEADDR的两篇博文。SO_REUSEADDR例解 和 socket中的SO_REUSEADDR
4. 编写守护进程的理论结合实际:Linux守护进程的编程方法
阅读全文
类别:Mathopd 查看评论
要看Mathopd的启动过程,当然应先从main函数看起,看看做了哪几个大的步骤。如果你对本文感兴趣,阅读时最好参考源代码。
1. 处理命令行参数
2. 打开/dev/null设备
3. 读入配置信息(这很重要,我们以后将着重分析)
4. 启动配置文件中指定的所有服务器,并侦听连接
5. 根据配置信息设置根目录
6. 根据配置信息,设置程序运行的uid和gid。
7. 打开pid文件
8. 初始化日志模块
9. 根据命令行参数,决定是否让程序后台运行。
10. 设置信号处理函数
11. 向pid文件中登记pid
12. 初始化一些缓存区
13. 进入处理http协议的主函数。
我们来详细的看看各个步骤:
1. 处理命令行参数
while ((c = getopt(argc, argv, "ndvf:t")) != EOF) {
//...
}
getopt是Unix/Linux中解析命令行参数的函数。关于getopt的用法我这里不做详细的说明。关于getopt的详细介绍请参考本文后的相关链接
-n: 将系统放在前台运行。系统默认是在后台运行守护进程(服务器程序一般作为后台守护进程执行),如果有该参数,系统将不会放到后台运行。这个参数在调试的时候很有用处。
-d: 系统以调试模式启动。
-v: 给出系统版本。若有该参数,系统不会启动服务,而是打印出版本号后直接退出。
-f <filename>: 指定配置文件路径。若有该参数,系统将从指定文件加载配置信息,若无该参数系统从标准输入中接受配置信息。
-t: 如果有该选项,系统的调试信息既会输出到日志文件,也输出到标准错误上,这个选项在调试时很有用处。
2. 打开/dev/null
null_fd = open(devnull, O_RDWR);
/dev/null是Unix-Like操作系统中的一个特殊的文件,它就像一个“黑洞”,丢弃所有向它写入的数据(但返回值为成功)。
打开此文件的目的是为以后将标准输入,标准输出和标准错误重定向到该“黑洞”做好准备工作;
一个后台运行的守护进程,不可能用到这三个标准的描述符,因为这三个描述符均和终端有关,而后台的守护进程没有一个终端作为他的父进程。
3. 读入配置信息
message = config(config_filename);
if (message)
die(0, "%s", message);
config函数完成了整个系统的配置,config模块是Mathopd设计的比较优雅的一个模块,他应用了编译原理中词法分析中的自动状态机的理论,使一个复杂的解析过程,变的清晰易懂。配置模块在以后的文章中会进行详细的分析。
这个函数和我所见过的一般的函数设计风格有所不同。一般的函数设计是返回一个预先定义好的错误码来表示是何种错误。而这个函数的返回的是一个指向一个常量字符的指针来表示错误。其实这样的设计可以简化代码编写,使代码看起来更加优美。作者预先定义好了很多全局的常量的字符串用来表示错误。这样做既可以判断是何种错误,又可以很方便的使用打印语句打印出是错误信息(不用书写冗长的错误码和错误字符串的映射代码)。下面是一个错误字符串的定义。
static const char e_memory[] = "out of memory";
4. 启动服务器,并侦听连接
s = servers;
while (s) {
startup_server(s);
s = s->next;
}
Mathopd的“服务器们”是一个全局链表。在读入配置信息的同时完成了这个链表的建立。很容易读懂,我们遍历链表,并对每一个服务器对象进行startup_server。不要以为startup_server很神秘,它是一种我们很熟悉的代码,几乎所有的关于C的socket编程的服务器范例都会有讲解。这里我再不厌其烦的重复一遍,权当是回顾一下socket服务器编程。
a). 创建socket
s->fd = socket(AF_INET, SOCK_STREAM, 0);
b). 设置socket选项
这个倒是值得一提!
SO_REUSEADDR : 这个选项在编写服务器程序时一般都要设置。如果没有设置该选项会导致两个问题:1.服务器的快速起停,会引起地址被占用的错误。2. 在一个服务器上绑定不同ip相同端口会引起地址被占用的错误。显然,这两个问题对HTTP服务器来说是不允许存在的。我在参考链接中给出了我检索的关于SO_REUSEADDR 选项的说明
FD_CLOEXEC : 这个我不太明白
O_NONBLOCK : 文件描述符设置为非阻塞,前面介绍过,这里的socket采用的是I/O多路复用的同步非阻塞模式。
c). 绑定地址和端口
bind(s->fd, (struct sockaddr *) &sa, sizeof sa);
d). 开始侦听
listen(s->fd, s->backlog);
6. 打开pid文件
这为登记pid做准备,登记pid的步骤一定要放在将程序变成后台服务之后,因为我们登记的是后台服务器的进程pid,它和当前运行的进程pid不同,当前运行的进程是后台服务器进程的父进程
7. 初始化日志模块
为记录日志做准备,如果要在标准错误上显示日志,这里要将标志错误从定向。为什么要重定向呢?因为我们马上就要将/dev/null重定向到标准错误了。到那时,我们向标准错误中写日志,就不会再有任何反应了。
8. 后台运行
关于将程序变为后台守护进程运行的理论比较复杂,这里不再叙述。但我在参考链接中将会给出这一理论的详细说明
9. 设置信号处理函数
程序成为后台守护进程后,就没有终端与之交互了。在这里我们依赖信号来与服务器交互。信号处理函数,就是我们与服务器交互的函数。
10. 登记pid
这里就是我们登记后台服务进程的pid最佳时机了
11. 初始化缓冲区
这里其实就是创建一些系统需要的资源,如连接、输入输出缓冲区等。资源的个数及大小由系统配置决定。
12. 进入处理http协议的主函数
完成启动后,我们就要干正经事了。
参考链接
1. IBM DeveloperWorks的文章一直都很经典:使用 getopt() 进行命令行处理
2. 维基百科中关于/dev/null的详细说明。
3. 维基百科中关于有限自动机(DFA)的介绍
4. 关于SO_REUSEADDR的两篇博文。SO_REUSEADDR例解 和 socket中的SO_REUSEADDR
4. 编写守护进程的理论结合实际:Linux守护进程的编程方法
阅读全文
类别:Mathopd 查看评论
相关文章推荐
- spark源码学习(二)------------spark-shell启动分析
- DroidPlugin源码分析处理Activity的启动
- etcd启动流程源码分析笔记(-)
- Ceilometer项目源码分析----ceilometer-agent-compute服务的初始化和启动
- 源码分析Flume启动过程
- Mesos源码分析(3): Mesos Master的启动之二
- HBase 0.1.0版本源码分析--Master启动流程
- Dubbo Provider启动流程源码分析
- tomcat源码分析(二)启动---Debug方式
- [netty源码分析]--服务端启动的工作流程分析
- Activity启动过程源码分析
- spring启动component-scan类扫描加载过程---源码分析
- Linux内核源码分析--内核启动之(1)zImage自解压过程(Linux-3.0 ARMv7)
- u-boot源码分析 --- 启动第二阶段 ,基于2410 启动代码 分析
- Chromium源码分析:ContentShell启动流程
- Tomcat6源码启动分析
- Heritrix1.14源码分析(5) 如何让Heritrix在Ecplise等IDE下编程启动
- Android 4.0 Launcher2源码分析——启动过程分析
- spring源码之旅(2)_applicationcontext启动流程分析