nginx文档
吴东
April 28, 2009
Contents
1前言 5
2基本配置 7
2.1安装………………… 7
2.2配置说明………………. 10
2.3启动和控制……………… 25
3深入源码 27
3.1源码结构………………. 27
3.2configure配置 ……………. 27
3.3nginx源码习惯 ……………. 27
3.4常用基础库……………… 28
3.5core模块………………. 40
3.6event模块 ……………… 44
3.7http模块………………. 46
4模块编写 55
4.1http模块编写…………….. 55
4.2基于nginx的高性能服务器开发 . . . . . . . . . 55
5附录 57
5.1编译器参数……………… 57
5.2系统函数………………. 59
CONTENTS CONTENTS
Chapter 1
前言
在互联网编程中,http服务器编程作为一个非常重要方向一直为各种语言所重视,从c语言的apache,Lighttpd到当前非常流行的nginx。 Java有tom-cat,jetty,websphere等众多服务器,pyhoen的zope等服务器。既有重量级的服务器,又有轻量级的,嵌入式的 服务器。从互联网的应用来说,c语言的http服务器一直占有主导地位,当前最流行的三个开源服务器有apache,Lighttpd和nginx。 Apache作为经典的Web服务器,除了慢没有别的缺点了,Apache2对fcgi支持并不好,非常好用的proxy和proxy_ajp(很多人用 它作为tomcat的前端),不支持epoll(这年头,epoll几乎是性能的必备)。Lighttpd作为杀手级的静态文件能力,杀手级的fcgi能 力但是proxy模块不够稳定。Nginx速度快,占用资源少,杀手级的proxy和rewrite,非常不错的静态文件能力,最适合作为整个网站的前端 服务(将php、svn等不同请求发送往后端apache)。现在国内Nginx的用户越来越多了,多数拥抱Nginx的网站都钟意其优异的性能表现,如 果是相对比较大的网站,节约下来的服务器成本无疑是客观的。当前 Ngnix美中不足之处是相关的文档和用户经验都还是很欠缺,用户之间还很难做到可借鉴性的交流。最近因为朋友遇到一些技术问题,我也翻阅了不少 Nginx的邮件列表内容,发现大量的技术细节仍然在频繁变化中,可是中文社区内相关的记录和讨论太少了。相信国内这些Nginx用户积攒的经验肯定是不 少的,但可能是因为某些其它因素考虑而看不到相关的技术分享。本书的写作就是为了向大家展示nginx的内部结构,以便于大家更好的使用该服务器。对于一 个八万行代码的http服务器,从性能来看它是那么的出色,从功能来看它并不逊色于几十万行的apache服务器。Nginx是实现有几个非常出色的地 方:模块化设计,流水线请求处理,多进程控制,epoll的使用,平滑的升级程序或是配置文件。Nginx采用的是单线程的方式,网络服务器的几种模型可 以参考C10K的介绍。当然,如果在linux下,最好的模型自然是EPool(effectiveepoll?)。目前一台服务器支持10K个客户访 问,基本上还比较麻烦,64位的CPU,可能是一个比较好的解决方案。2块1000M网卡,10K客户端,每个客户端的带宽是 2000/10000=200K,这是理论值,勉强可行。考虑一下纯粹的线程模型。10K个客户端,需要10K个线程。考虑到每个进程3G的用户空间,那 么每个线程的空间是3000M/10K=300K,只能说非常勉强可以运行(每个线程的cruntime大约需要2-3K空间)。但是大量的线程调度会极 大的减缓系统效率。再考虑一下多个进程的模型,每个进程创建多个线程。其实与纯粹的线程模型差异不大,不过是每个线程的用户空间不受太大限制。大量的线程 调度带来的巨大性能损耗会使得整个系统效率及其低下。当然,这种模型也不是一无是处,对于客户端数目不多的情况,比如ftp服务器,这种模型是非常合适 的。然后就剩下epoll模
Chapter1.前言
型。要使用epoll模型,首先必须有Linux2.6的内核。了解select模型的人都知道,select每个句柄只能支持64个socket,这可 以通过阅读linuxselect.h知道。而Poll模型则是一种事件触发的模型,没有64个socket的限制。epoll是对Poll模型的一个改 造,让一个Poll句柄可以同时支持多个Socket句柄,好处是将大量Poll句柄的事件探测机制放到内核中处理,大大减少了将数据从内核态拷贝到用户 态的次数,从而提高效率。阅读Linux的源码可以看到,epoll的内部使用的是一个Hashmap,Hashmap中存放Poll对象,所以从根本上 来说,epoll模型是一种更好的Poll模型(我一直理解为EffectivePoll)。
当然nginx也有不足的地方,例如各个模块之间的耦合性太强,各个模块不适合直接拿出来使用。Nginx的作者过于强调按照他自己所理解的模块化,而把一些基础模块也用作者所谓的模块化来实现,导致代码相互引用太多,艰涩难懂。
本书的编写的面向的用户除了初级linux工程师外,还可以为中高级linux工程师作为开发高性能服务器的工具书,目的除了向用户展示nginx精巧的设计和完整的实现外,还希望通过本书的讲解能够让用户以nginx作为基本框架或是自己实现更多的高性能服务器。
Chapter 2
基本配置
2.1安装
安装依赖模块
1.
gzip模块需要zlib库,该模块在http://www.zlib.net/网站下载。
2.
rewrite模块需要pcre库,该模块在http://www.pcre.org/网站下载。
3.
ssl功能需要openssl库该模块在http://www.openssl.org/网站下载。
官方源代码下载
在http://sysoev.ru/nginx/download.html网站上可以下载nginx源代码。
使用源代码安装
Nginx使用 Unix下常用的 ‘./configure && make && make install’过程来编译安装。configure脚本确定系统所具有一些特性,特别是nginx用来处理连接的方法。然后,它创建Makefile文 件。configure支持下面的选项:
–prefix=
-Nginx安装路径。如果没有指定,默认为/usr/local/nginx。
–sbin-path=
-Nginx可执行文件安装路径。只能安装时指定,如果没有指定,默认为
/sbin/nginx。
–conf-path=
-在没有给定-c选项下默认的nginx.conf的路径。如果没有指定,默认为
/conf/nginx.conf。
–pid-path=
-在nginx.conf中没有指定pid指令的情况下,默认的nginx.pid的路径。如果没有指定,默认为
/logs/nginx.pid。
–lock-path=
-nginx.lock文件的路径。
–error-log-path=
-在nginx.conf中没有指定error_log指令的情
2.1.安装 Chapter2.基本配置
况下,默认的错误日志的路径。如果没有指定,默认为
/logs/er-ror.log。
–http-log-path=
-在nginx.conf中没有指定access_log指令的情况下,默认的访问日志的路径。如果没有指定,默认为
/logs/ac-cess.log。
–user=-在nginx.conf中没有指定user指令的情况下,默认的nginx使用的用户。如果没有指定,默认为nobody。 –group=-在nginx.conf中没有指定user指令的情况下,默认的nginx使用的组。如果没有指定,默认为nobody。 –builddir=DIR-指定编译的目录–with-rtsig_module-启用 rtsig模块
–with-select_module–without-select_module-允许或不允许开启SE-LECT模式,如果configure没 有找到更合适的模式,比如:kqueue(sun os),epoll (linux kenel2.6+), rtsig(实时信号)或者/dev/poll(一种类似select的模式,底层实现与SELECT基本相同,都是采用轮训方法) SELECT模式将是默认安装模式
–with-poll_module –without-poll_module -允许或不允许开启pool模式.–with-http_ssl_module-开启HTTPSSL模块,使NGINX可以支持HTTPS请求。这个模块 需要已经安装了OPENSSL,在DEBIAN上是libssl–with-http_realip_module-启用 ngx_http_realip_module。–with-http_addition_module-启用 ngx_http_addition_module–with-http_sub_module-启用ngx_http_sub_module– with-http_dav_module-启用 ngx_http_dav_module–with-http_flv_module-启用 ngx_http_flv_module –with-http_stub_status_module -启用 “server status”页 –without-http_charset_module -禁用 ngx_http_charset_module –without-http_gzip_module -禁用 ngx_http_gzip_module.如果启用,需要zlib。 –without-http_ssi_module -禁用 ngx_http_ssi_module–without-http_userid_module-禁用ngx_http_userid_module– without-http_access_module-禁用ngx_http_access_module–without- http_auth_basic_module-禁用ngx_http_auth_basic_module–without- http_autoindex_module-禁用ngx_http_autoindex_module–without- http_geo_module-禁用ngx_http_geo_module–without-http_map_module-禁用 ngx_http_map_module–without-http_referer_module-禁用 ngx_http_referer_module–without-http_rewrite_module-禁用 ngx_http_rewrite_module.如果启用需要 PCRE。–without-http_proxy_module-禁用ngx_http_proxy_module
Chapter2.基本配置 2.1.安装
–without-http_fastcgi_module-禁用ngx_http_fastcgi_module–without- http_memcached_module-禁用ngx_http_memcached_module–without- http_limit_zone_module-禁用ngx_http_limit_zone_module–without- http_empty_gif_module-禁用ngx_http_empty_gif_module–without- http_browser_module-禁用 ngx_http_browser_module–without-http_upstream_ip_hash_module-禁用 ngx_http_upstream_ip_hash_module–with-http_perl_module-启用 ngx_http_perl_module –with-perl_modules_path=PATH-指定 perl模块的路径 –with-perl=PATH
-指定 perl执行文件的路径 –http-log-path=PATH -Set path to the http access log –http-client-body-temp-path=PATH -Set path to the http client
request body temporary files –http-proxy-temp-path=PATH -Set path to the http proxy temporary files –http-fastcgi-temp-path=PATH -Set path to the http fastcgi tem-
porary files –without-http -禁用 HTTP server –with-mail -启用 IMAP4/POP3/SMTP代理模块 –with-mail_ssl_module -启用 ngx_mail_ssl_module –with-cc=PATH -指定 C编译器的路径 –with-cpp=PATH -指定 C预
处理器的路径 –with-cc-opt=OPTIONS -Additional parameters which will be added to the variable CFLAGS. With the use of the system library PCRE in FreeBSD, it is necessary to indicate –with-cc-opt=”-I /usr/local/include”. If we are using select() and it is necessary to increase the number of file descriptors, then
this also can be assigned here: –with-cc-opt=”-D FD_SETSIZE=2048″. –with-ld-opt=OPTIONS -Additional parameters passed to the linker.
With the use of the system library PCRE in FreeBSD, it is necessary to indicate –with-ld-opt=”-L /usr/local/lib”.
–with-cpu-opt=CPU -为特定的 CPU编译,有效的值包括:pentium, pentiumpro, pentium3, pentium4, athlon, opteron, amd64, sparc32, sparc64, ppc64
–without-pcre -禁止 PCRE库的使用。同时也会禁止 HTTP rewrite模块。在 “location”配置指令中的正则表达式也需要 PCRE。 –with-pcre=DIR -指定 PCRE库的源代码的路径。 –with-pcre-opt=OPTIONS -Set additional options for PCRE building. –with-md5=DIR -Set path to md5 library sources.
2.2.配置说明 Chapter2.基本配置
–with-md5-opt=OPTIONS -Set additional options for md5 building.
–with-md5-asm -Use md5 assembler sources.
–with-sha1=DIR -Set path to sha1 library sources.
–with-sha1-opt=OPTIONS -Set additional options for sha1 building.
–with-sha1-asm -Use sha1 assembler sources.
–with-zlib=DIR -Set path to zlib library sources.
–with-zlib-opt=OPTIONS -Set additional options for zlib building.
–with-zlib-asm=CPU -Use zlib assembler sources optimized for
specified CPU, valid values are: pentium, pentiumpro –with-openssl=DIR -Set path to OpenSSL library sources –with-openssl-opt=OPTIONS -Set additional options for OpenSSL
building –with-debug -启用调试日志 –add-module=PATH -Add in a third-party module found in directory
PATH在不同版本间,选项可能会有些许变化,请总是使用 ./configure–help命令来检查一下当前的选项列表。示例(最好能在同一行):
1 ./ configure \
2 ..sbin.path =/ usr / local / nginx / nginx \
3 ..conf.path =/ usr / local / nginx / nginx . conf \
4 ..pid.path =/ usr / local / nginx / nginx . pid \
5 ..with.http_ssl_module \
6 ..with.pcre =../ pcre .4.4 \
7 ..with.zlib =../ zlib .1.1.3
Example on Ubuntu/debian with libgcrypt11-dev, libpcre3-dev and libssl-dev installed (choose EITHER –with-md5 OR –with-sha1, but not both; on debian and ubuntu, they should both point to /usr/lib)
1 ./ configure \
2 ..with.openssl =/ usr / lib / ssl / \
3 ..with.md5 =/ usr / lib
An Ubuntu Edgy .deb for version 0.5.2 can be found here: nginx_0.5.2-1_i386.deb. (NOTE: According to an October 2006 message md5 was used in a now broken http cache module and sha1 is used in an incomplete mysql library module and so are currently not needed.)
2.2配置说明
主模块
daemon
Chapter2.基本配置 2.2.配置说明
语法: daemon on | off
缺省值: on生产环境中不要使用”daemon”和”master_process”指令,这
些选项仅用于开发调试。
debug_points
语法: debug_points [stop | abort]
缺省值: nonedebug_points stop;应该适用于调试,在调试器内设置断
点之类的。
error_log
语法: error_log file [ debug | info | notice | warn | error | crit
]
缺省值: ${prefix}/logs/error.log Nginx添加 –with-debug编译参数,你还能够使用以下配置: error_log LOGFILE [ debug_core | debug_alloc
| debug_mutex | debug_event | debug_http | debug_imap];
include
语法:include file | *
缺省值: none你可以在任意地方使用include指令实现配置文件的包含,类似于apache中的include方法,可减少主配置文件d。 include指令还支持像下面配置一样的全局包含的方法,例如包含一个目录下所有以”.conf”结尾的文件:include vhosts/*.conf;注意路径受到configure编译参数–prefix=<路径>指令的影响,如果没有指定,Nginx默认是 被编译在/usr/local/nginx。
lock_file
语法: lock_file file
缺省值: compile-time option lock_file /var/log/lock_file; nginx uses accept mutex to serialize accept() syscalls. If nginx is built by gcc, Intel C++, or SunPro C++ compilers on i386, amd64, sparc64, and ppc64, then nginx uses the atomic instructions to implement the mutex. In other cases the lock file would be used.
master_process
语法: master_process on | off
缺省值:on master_process off;生产环境中不要使用”daemon”和”master_process”指
令,这些选项仅用于开发调试。
pid
语法: pid file
缺省值:compile-time option Example:pid /var/log/nginx.pid;进程
id存储文件。可以使用 kill -HUP cat/var/log/nginx.pid\对Nginx进行配置文件重新加载。
SSL_ENGINE
语法: ssl_engine engine
缺省值: system dependent该指令用于指定openssl使用的引擎。你可以通过下面的命令行获知系统目前支持的openssl引擎 openssl engine -t例如: $ openssl engine -t (cryptodev) BSD cryptodev engine [ available
2.2.配置说明 Chapter2.基本配置
] (dynamic) Dynamic engine loading support [ unavailable ]
timer_resolution
语法: timer_resolution t
缺省值: none Example: timer_resolution 100ms; The directive al-lows to decrease number gettimeofday() syscalls.By缺省值 gettime-ofday() is called after each return from kevent(), epoll, /dev/poll, select(), poll(). But if you need an exact time in logs when log-ging $upstream_response_time, or $msec variables, then you should use timer_resolution.
USER
语法: user user [group]
缺省值:nobody nobody指定Nginx Worker进程运行用户,默认是nobody帐号。例如: user www users;
worker_cpu_affinity
语法: worker_cpu_affinity cpumask [cpumask...]
缺省值: none Linux only. With this option you can bind the worker process to a CPU, it calls sched_setaffinity().仅适用于linux,使用该选项可以绑定worker进程和CPU. For example, worker_proceses 4; worker_cpu_affinity 0001 0010 0100 1000; Bind each worker process to one CPU only.分别给每个worker进程绑定一个CPU. worker_proceses 2; worker_cpu_affinity 0101 1010; Bind the first worker to CPU0/CPU2, bind the second worker to CPU1/CPU3.This is suitable for HTT.将CPU0/CPU2绑定给第一个worker进程,将CPU1/CPU3绑定给第二个worker进程。
worker_priority
语法: worker_priority [-]number
缺省值: on With this option you can give to all worker processes the priority (nice) you need/wish, it calls setpriority().使用该选项可以给所有的worker进程分配优先值。
worker_processes
语法: worker_processes number
缺省值: 1 e.g.: worker_processes 5; nginx has the ability to use more than one worker process for several reasons:nginx可以使用多个worker进程,原因如下: to use SMP to decrease latency when workers blockend on disk I/O to limit number of connections per process when select()/poll() is used The worker_processes and worker_connections from the event sections allows you to calculate maxclients value: k max_clients = worker_processes * worker_connections
worker_rlimit_core
语法: worker_rlimit_core size
缺省值: ‘ Maximum size of core file per worker;
worker_rlimit_nofile
语法: worker_rlimit_nofile limit
Chapter2.基本配置 2.2.配置说明
缺省值: ‘ Specifies the value for maximum file descriptors that can be opened by this process.指定
worker_rlimit_sigpending
语法: worker_rlimit_sigpending limit
缺省值: ‘ (Since Linux 2.6.8) Specifies the limit on the number of signals that may be queued for the real user ID of the calling process.
working_directory
语法: working_directory path
缺省值: –prefix This is the working directory for the workers. It’s used for core files only. nginx uses absolute paths only, all relative paths in configuration files are relative to –prefix==PATH.
event配置
http参数
alias
语法: alias file-path|directory-path;
缺省值: no context: location This directive assigns a path to be used for the indicated location. Note that it may look similar to the root directive, but the document root doesn’t change, just the file system path used for the request. For example:
1 location /i/ {
2 alias / spool / w3 / images /;
3 }
The request “/i/top.gif” will return the file “/spool/w3/images/ top.gif”. It is possible to use variables in the replacement path. The alias directive cannot be used inside a regex-specified location. If you need to do this you must use a combination of rewrite and root.
client_body_in_file_only
语法: client_body_in_file_only on|off
缺省值: client_body_in_file_only off context: http, server, lo-cation The directive enables to store a client request body in a file. Please note that the file at the request completion will not be removed if the directive is enabled. The directive can be used for debugging and for the $r->request_body_file method in the ngx_http_perl_module module.
client_body_buffer_size
语法: client_body_buffer_size the_size
缺省值: client_body_buffer_size 8k/16k context: http, server, location The directive specifies the client request body buffer size. If the request body is more than the buffer, then the entire request
2.2.配置说明 Chapter2.基本配置
body or some part is written in a temporary file.The缺省值 size is equal to two pages size, depending on platform it is either 8K or 16K.
client_body_temp_path
语法: client_body_temp_path dir-path [ level1 [ level2 [ level3 ] ]]
缺省值: client_body_temp_path client_body_temp context: http, server, location The directive assigns the directory for storing the temporary files in it with the body of the request. In the dir-path a hierarchy of subdirectories up to three levels are possible. For example
1 client_body_temp_path /spool/nginx/client_temp 1 2;
The directory structure will be like this: /spool/nginx/client_temp/ 7/45/00000123457
client_body_timeout
语法: client_body_timeout time
缺省值: client_body_timeout 60 context: http, server, location Directive sets the read timeout for the request body from client. The timeout is set only if a body is not get in one readstep. If after this time the client send nothing, nginx returns error “Request time out” (408).
client_header_buffer_size
语法: client_header_buffer_size size
缺省值: client_header_buffer_size 1k context: http, server Direc-tive sets the headerbuffer size for the request header from client. For the overwhelming majority of requests it is completely sufficient a buffer size of 1K. However if a big cookie is in the request-header or the request has come from a wap-client the header can not be placed in 1K, therefore, the request-header or a line of request-header is not located completely in this buffer nginx allocate a bigger buffer, the size of the bigger buffer can be set with the instruction large_client_header_buffers.
client_header_timeout
语法: client_header_timeout time
缺省值: client_header_timeout 60 context: http, server Directive assigns timeout with reading of the title of the request of client. The timeout is set only if a header is not get in one readstep. If after this time the client send nothing, nginx returns error “Request time out” (408).
client_max_body_size
语法: client_max_body_size size
缺省值: client_max_body_size 1m context: http, server, location Directive assigns the maximum accepted body size of client request,
Chapter2.基本配置 2.2.配置说明
indicated by the line “Content-Length” in the header of request. If size is greater the given one, then the client gets the error “Request Entity Too Large” (413). It is necessary to keep in mind that the browsers do not know how to correctly show this error.
缺省值_type
语法:缺省值_type MIME-type
缺省值:缺省值_type text/plain context: http, server, location default_type assigns the default MIME-type to be used for files where the standard MIME map doesn’t specify anything. See also types Example:
1 location = / proxy . pac {
2 default_type application /x.ns.proxy.autoconfig ;
3 }
4 location = / wpad . dat {
5 rewrite . / proxy . pac ;
6 default_type application /x.ns.proxy.autoconfig ;
7 }
error_page
语法: error_page code [ code... ] [ = |=answer-code ] uri
缺省值: no context: http, server, location, if in location The directive specifies the URI, which will be showed for the errors indicated. Example of the use:
1 error_page 404 /404.html;
2 error_page 502 503 504 /50x.html;
3 error_page 403 http://example.com/forbidden.html;
Furthermore, it is possible to change the code of answer to another, for example: error_page 404 =200 /.empty.gif; If an erroneous answer is processed by the proxied or FastCGI server and this server can return the different answer codes, for example, 200, 302, 401 or 404, then it is possible to issue the code returned: error_page 404 = / 404.php;
index
语法: index file [file...]
缺省值: index index.html context: http, server, location Directive determines the file(s) which will be used as the index. It’s possible to use variables in the name of file. The presence of the files is checked in the order of their enumeration. A file with an absolute path can be put at the end. Example using a variable: index index. $geo.html index.0.html /index.html;
internal
语法: internal
缺省值: no context: location internal indicates that the match-ing location can be used only for so called “internal” requests. For
2.2.配置说明 Chapter2.基本配置
external requests it will return the error “Not found” (404). Inter-nal requests are the following: requests redirected by the instruc-tion error_page subrequests created by the command include virtual of the “ngx_http_ssi_module” module requests changed by the instruction rewrite of the “ngx_http_rewrite_module” module An example to prevent clients fetching error pages directly:
1 error_page 404 /404.html; 2 location /404.html {
3
internal; 4}
keepalive_timeout
语法: keepalive_timeout time [ time ]
缺省值: keepalive_timeout 75 context: http, server, location Directive assigns the timeout, for keep-alive connections with the client. The second parameter assigns value in the line “Keep-Alive: timeout=time” in the header of answer. The parameters can differ from each other. Line “Keep-Alive: timeout=time” understands Mozilla and Konqueror. MSIE itself shuts keep-alive connection approximately after 60 seconds.
large_client_header_buffers
语法: large_client_header_buffers number size
缺省值: large_client_header_buffers 4 4k/8k context: http, server Directive assigns the maximum number and size of buffers for large headers to read from client request. The request line can not be bigger then the size of one buffer, if the client send a bigger header nginx returns error “Request URI too large” (414). The longest header line of request also must be not more than the size of one buffer, otherwise the client get the error “Bad request” (400). Buffers are separated only as needed. By default the size of one buffer is equal to the size of page, depending on platform this either 4K or 8K, if at the end of working request connection converts to state keep-alive, then these buffers are freed.
limit_except
语法: limit_except methods {…}
缺省值: no context: location Directive limits HTTP-methods, acces-sible inside location. For the limitation can be used the directives of modules ngx_http_access_module and ngx_http_auth_basic_module: limit_except GET { allow 192.168.1.0/32; deny all; }
limit_rate
语法: limit_rate speed
缺省值: no context: http, server, location, if in location Direc-tive assigns the speed of transmission of the answer to client. Speed is assigned in the bytes per second. Limitation works only for one connection, i.e., if client opens 2 connections, then total velocity
Chapter2.基本配置 2.2.配置说明
will be 2 times than higher limited. If it is necessary to limit speed for the part of the clients at the level of server, then directive limit_rate for this does not be suitable. Instead of this should be assigned the necessary speed of variable $limit_rate:
1 2 3 4 server if } { ( $slow ) { set $limit_rate 4k;
5
6 …
7 8 }
listen
语法:listen address:port [缺省值 [ backlog=num | rcvbuf=size | sndbuf=size | accept_filter=filter | deferred | bind ] ]
缺省值: listen 80 context: server The directive specifies the address and port, on which the server accepts requests. It is possible to specify address or port only, besides, an address can be the server name, for example:
1 listen 127.0.0.1:8000;
2 listen 127.0.0.1;
3 listen 8000;
4 listen *:8000;
5 listen localhost :8000;
If only address is given, then缺省值 port 80 is used. If the directive has the”缺省值” parameter, then the server, in whom is described this directive, he will be the缺省值 server for the ad-dress:port pair, but if there are no directives with the”缺省值”parameter, then the缺省值 server will be the first server, in whom is described the address:port pair. In directive listen with parameter缺省值 it is possible to indicate several parameters, specific for the system calls listen(2) and bind(2). backlog=num –is assigned parameter backlog in call listen(2). By缺省值 backlog it is equal to -1. rcvbuf=size –is assigned parameter SO_RCVBUF for listening socket. sndbuf=size –is assigned parameter SO_SNDBUF for listening socket. accept_filter=filter –is assigned name accept-filter. It works only to FreeBSD, it is possible to use two filters –dataready and httpready. On the signal -HUP accept-filter it is possible to change only in the quite last versions FreeBSD: 6.0, 5.4-STABLE and 4.11-STABLE. deferred –indicates to use that postponed accept(2) on Linux with the aid of option TCP_DEFER_ACCEPT. bind –indicates that it is necessary to make bind(2) separately for this pair of address:port. The fact is that if are described several directives listen with the identical port, but by different addresses and one of the directives listen listens to on all addresses for this port (*:port), then nginx will make bind(2) only to *:port. It is necessary to consider that in
2.2.配置说明 Chapter2.基本配置
this case for determining the address, on which the connections ar-rive, is done the system call getsockname(). But if are used parameters backlog, rcvbuf, sndbuf, accept_filter or deferred, then it is always done separately for this pair of address:port bind(2). Example of the use of the parameters:listen 127.0.0.1缺省值 accept_filter=dataready backlog=1024;
location
语法: location [=|~|~*|^~] /uri/ { … }
缺省值: no context: server This directive allows different con-figurations depending on the URI. It can be configured using both conventional strings and regular expressions. To use regular expres-sions, you must use the prefix ~* for case insensitive match and ~ for case sensitive match. To determine which location directive matches a particular query, the conventional strings are checked first. Con-ventional strings match the beginning portion of the query and are case-sensitive -the most specific match will be used (see below on how nginx determines this). Afterwards, regular expressions are checked in the order defined in the configuration file. The first regular expres-sion to match the query will stop the search. If no regular expression matches are found, the result from the convention string search is used. There are two ways to modify this behavior. The first is to use the prefix “=”, which matches an exact query only. If the query matches, then searching stops and the request is handled immediately. For example, if the request “/” occurs frequently, then using “location = /” will expedite the processing of this request. The second is to use the prefix ^~. This prefix is used with a conventional string and tells nginx to not check regular expressions if the path provided is a match. For instance, “location ^~ /images/” would halt searching if the query begins with /images/ -all regular expression directives would not be checked. Furthermore it is important to know that NGINX does the comparison not URL encoded, so if you have a URL like “/ images/%20/test” then use “/images/ /test” to determine the location. To summarize, the order in which directives are checked is as follows: Directives with the = prefix that match the query exactly. If found, searching stops. All remaining directives with conventional strings, longest match first. If this match used the ^~ prefix, searching stops. Regular expressions, in order of definition in the configuration file. If #3 yielded a match, that result is used. Else the match from #2 is used. Example:
1 location = / {
2 # matches the query / only.
3 [ configuration A ]
4}
5 location / {
6 # matches any query , since all queries begin with /, but regular 7 # expressions and any longer conventional blocks will be
Chapter2. 基本配置 2.2. 配置说明
8 # matched first .
9 [ configuration B ]
10 }
11 location ^~ / images / {
12 # matches any query beginning with / images / and halts
searching ,
13 # so regular expressions will not be checked .
14 [ configuration C ]
15 }
16 location ~* \.( gif | jpg | jpeg )$ {
17 # matches any request ending in gif , jpg , or jpeg .
However , all
18 # requests to the / images / directory will be handled
by
19 # Configuration C.
20 [ configuration D ]
21 }
Example requests: / -> configuration A /documents/document.html -> configuration B /images/1.gif -> configuration C /documents/1.jpg -> configuration D Note that you could define these 4 configurations in any order and the results would remain the same. How nginx Determines Which Path Matches Most users will not need to know how nginx internally determines which path to use -know that it will choose the “most specific” match for your URL in a speedy and efficient manner. For those that are curious, however, read on. All path strings are sorted alphabetically. nginx then proceeds to search down the list looking for matches until the request URI has a “higher” value then the current string in the sorted list. This is determined using the family of strcmp() functions -once strcmp() returns 1, then searching stops. Once searching stops, the last string which matched is used. For example, lets say we have the following paths: / /a /apple /banana Now, lets say the server gets the path “/az”. nginx would begin search down this list. First, “/” would match, but “/ is less than “/az” so searching continues. “/a” also matches, but “/a” is still less than “/ az” so we continue again. “/apple” does not match. The next string, “/ banana”, is greater than “/az” so searching stops and the last match, “/a”, would be used.
msie_padding
语法: msie_padding [on|off]
缺省值: msie_padding on context: http, server, location This directive enables or disables the the msie_padding feature for MSIE browsers. When this is enabled Nginx will pad the size of the response headers up to 512 bytes for responses with status codes of more than 400. This prevents activating the “friendly” http error pages feature of the relevant browsers, so as to not hide the possibly more informative error pages.
msie_refresh
2.2.配置说明 Chapter2.基本配置
语法: msie_refresh [on|off]
缺省值: msie_refresh off context: http, server, location This directive allows or forbids issuing a refresh instead of doing a redirect for MSIE.
optimize_server_names
语法: optimize_server_names [ on|off ]
缺省值: optimize_server_names on context: http, server Directive activates or deactivates optimization of host name checks for name-based virtual servers. In particular, the check influences the name of the host used in redirects. If optimization is on, and all name-based servers listening on one address:port pair have identical configura-tion, then names are not checked during request execution and redirects use first server name. If redirect must use host name passed by the client, then the optimization must be turned off.
port_in_redirect
语法: port_in_redirect [ on|off ]
缺省值: port_in_redirect on context: http, server, location Di-rective allows or prevents port indication in redirects handled by nginx.
recursive_error_pages
语法: recursive_error_pages [on|off]
缺省值: recursive_error_pages off context: http, server, loca-tion recursive_error_pages enables or disables following a chain of error_page directives.
root
语法: root path
缺省值: root html context: http, server, location, if in location root specifies the document root for the requests. For example, with this configuration
1 location /i/ { 2 root /spool/w3; 3}
A request for “/i/top.gif” will return the file “/spool/w3/i/ top.gif”. You can use variables in the argument.
satisfy_any
语法: satisfy_any [ on|off ]
缺省值: satisfy_any off context: location Directive solves access with at least one successful checking, executed by modules ngx_http_access_module or ngx_http_auth_basic_module:
1 location / { 2 satisfy_any on;
3 allow 192.168.1.0/32;
Chapter2.基本配置 2.2.配置说明
4 deny all ;
5 auth_basic ” closed site “;
6 auth_basic_user_file conf / htpasswd ;
7 }
send_timeout
语法: send_timeout the time
缺省值: send_timeout 60 context: http, server, location Directive assigns response timeout to client. Timeout is established not on entire transfer of answer, but only between two operations of reading, if after this time client will take nothing, then nginx is shutting down the connection.
sendfile
语法: sendfile [ on|off ]
缺省值: sendfile off context: http, server, location Directive activate or deactivate the usage of sendfile().
server
语法: server {…}
缺省值: no context: http Directive assigns configuration for the virtual server. There is no clear separation of the virtual servers ip-based (on the basis ip-address) and name-based (on the basis of the name, transferred in the line “Host” of the title of request). Instead of this by directives listen are described all addresses and ports, on which it is necessary to assume connections for this server, and in directive server_name are indicated all names of servers. The example to configurations is described in tuning of virtual servers.
server_name
语法: server_name name [... ]
缺省值: server_name hostname context: server Directive assigns the names of virtual server, for example: server { server_name example.com www.example.com; } The first name becomes the basic name of server. By缺省值 the name of the machine (hostname) is used. It is possible to use “*” for replacing the first part of the name: server { server_name example.com *.example.com; } Two of the given name of the above example can be combined into one: server { server_name .example.com; } The basic name of server is used in an HTTP redirects, if no a “Host” header was in client request or that header does not match any assigned server_name. You can also use just “*” to force Nginx to use the “Host” header in the HTTP redirect (note that “*” cannot be used as the first name, but you can use a dummy name such as “_” instead): server { server_name example.com *; } server { server_name _ *; }
server_names_hash_max_size
语法: server_names_hash_max_size number
缺省值: server_names_hash_max_size 512 context: http Directive
2.2.配置说明 Chapter2.基本配置
assigns the maximum size of the hash-tables of the names of servers. In greater detail see in the description of tuning hash.
server_names_hash_bucket_size
语法: server_names_hash_bucket_size number
缺省值: server_names_hash_bucket_size 32/64/128 context: http Directive assigns the size of basket in the hash-tables of the names of servers.This value by缺省值 depends on the size of the line of processor cache. In greater detail see in the description of tuning hash.
tcp_nodelay
语法: tcp_nodelay [on|off]
缺省值: tcp_nodelay on context: http, server, location This di-rective allows or forbids the use of the socket option TCP_NODELAY. Only included in keep-alive connections. Want to know more about the TCP_NODELAY socket option? ReadMoreAboutTcpNodelay
tcp_nopush
语法: tcp_nopush [on|off]
缺省值: tcp_nopush off context: http, server, location This di-rective permits or forbids the use of the socket options TCP_NOPUSH on FreeBSD or TCP_CORK on Linux. This option is only available when using sendfile. Setting this option causes nginx to attempt to send it’s HTTP response headers in one packet on Linux and FreeBSD 4.x ReadMoreAboutTcpNopush
types
语法: types {…} context: http, server, location Directive as-signs the correspondence of expansion and MIME-types of answers. Toone MIME-type can correspond several expansions. By缺省值 it is used these correspondences:
1 types {
2 text / html html ;
3 image / gif gif ;
4 image / jpeg jpg ;
5 }
The sufficiently complete table of mappings is included and is located in file conf/mime.types. So that for that determined location’a for all answers would reveal MIME-type “application/octet-stream”, it is possible to use the following:
1 location /download/ {
2 types { }
3 default_type application/octet.stream;
4}
Chapter2.基本配置 2.2.配置说明
http配置
The module ngx_http_core_module supports the built-in variables, whose names correspond with the names of variables in Apache. First of all, these are the variables, which represent the lines of the title of the client request, for example,
$http_user_agent, $http_cookie and so forth. Furthermore, there are other variables:
$args, this variable is equal to arguments in the line of request;
$content_length, this variable is equal to line “Content-Length” in the header of request; $content_type, this variable is equal to line “Content-Type” in the header of request; $document_root, this variable is equal to the value of directive root for the current request;
$document_uri, the same as $uri;
$host, this variable is equal to line “Host” in the header of request or name of the server, to whom the request arrived, if there is no this line;
$limit_rate, the variable allows to limit connection rate;
$request_method, this variable is equal to the method of request, usually this “GET” or “POST”; $remote_addr, this variable is equal to the address of client; $remote_port, this variable is equal to the port of client;
$remote_user, this variable is equal to the name of user, authen-ticated by ngx_http_auth_basic_module;
$request_filename, this variable is equal to path to the file for the current request, formed from directives root or alias and URI request; $request_body_file, ??? $request_uri, this variable is equal to the complete initial URI together with the arguments;
$query_string, the same as $args;
$scheme, the HTTP scheme (http, https). Evaluated only on demand, for example: rewrite ^(.+)$ $scheme://example.com$1 redirect;
$server_protocol, this variable is equal to the protocol of re-quest, usually this “HTTP/1.0″ or “HTTP/1.1″;
$server_addr, the variable is equal to the server address, to whom arrived the request. As a rule, for obtaining the value of this variable is done one system call. In order to avoid system call, it is necessary to indicate addresses in directives listen and to use parameter bind;
$server_name, this variable is equal to the name of the server, to whom arrived the request;
$server_port, this variable is equal to the port of the server, to which the request arrived;
$uri, this variable is equal to current URI in the request, it can differ from initial, for example by internal redirects, or with the
2.2.配置说明 Chapter2.基本配置
use of index it is file with internal redirects.
常用配置举例nginx目录自动加斜线
1 if (.d $request_filename ){
2 rewrite ^/(.*) ([^/]) $ http :// $host / $1$2 / permanent ;
3 }
nginx防盗链
1.
针对不同的文件类型
2.
针对不同的目录
1 # Preventing hot linking of images and other file types
2 location ~* ^.+\.( gif | jpg | png | swf | flv | rar | zip )$ {
3 valid_referers none blocked server_names *. linuxtone .
org http :// localhost baidu . com ;
4 if ( $invalid_referer ) {
5 rewrite ^/ http :// www . linuxtone . org / images /
default / logo . gif ;
6 # return 403;
7 }
8 }
1 location / img / {
2 root / data / www / wwwroot / bbs / img /;
3 valid_referers none blocked server_names *. linuxtone .
org http :// localhost baidu . com ;
4 if ( $invalid_referer ) {
5 rewrite ^/ http :// www . linuxtone . org / images /
default / logo . gif ;
6 # return 403;
7 }
8 }
nginx expires
1.根据文件类型expires
1 # Add expires header for static content
2 location ~* \.(js|css|jpg|jpeg|gif|png|swf)$ { 3 if (.f $request_filename) { 4 root /data/www/wwwroot/bbs;
5
expires 1d;
Chapter2. 基本配置 2.3. 启动和控制
6 7 8 } } break ;
2.根据判断某个目录
1 # serve static files
2 location ~ ^/( images | javascript | js| css | flash | media | static
)/ {
3 root / data / www / wwwroot / down ;
4 expires 30 d;
5 }
nginx下载限制并发和速率
1
2 limit_zone one $binary_remote_addr 10 m;
3 server {
4 listen 80;
5 server_name down . linuxotne . org ; index index . html
index . htm index . php ;
6 root / data / www / wwwroot / down ;
7 # Zone limit
8 location / {
9 limit_conn one 1;
10 limit_rate 20 k;
11 }
2.3启动和控制
nginx是超级稳定的服务器,一般不会因为超载问题而需要重启,重启的目的一般都是修改配置文件后需要加载一下。最开始的时候,我是用最直接的重启方式 killall -9 nginx;/data/nginx/sbin/nginx如果机器比较慢,kill进程时一瞬间杀不完,再执行一次即可。这种重启方式不是特别安全,如 果配置有误,则会重启失败,需要重新修改配置文件然后再启动,期间会消耗一点时间。不过对于目前普遍还是不怎么严格的http界而言,这点时间还不至于产 生太大损失,只要不是在关键时刻搞出来就好。如果希望沿用这种重启办法,我提议还是先好好测试吧。后来我在nginx.net上看到了一种更奇妙的重启 kill-HUP$pid($pid就是nginxmaster进程的进程号)我一般这样用
kill .HUP `cat / data / nginx / logs / nginx .pid `
这种方式的好处是实现“平滑重启”,在ps-aux中可以看到,nginx首先启动新进程,旧的进程仍然提供服务,在一段时间后,旧的进程服务结束就自动 关闭,剩下新进程继续服务。但是这种方式也是有缺点的,如果配置文件有误,或者资源冲突,则重启失效,但nginx并没有任何的提示!这就会时常发现改动 的配置文件没有生效,又比较难找到问题。所以,最后杂和了一下问题,
2.3.启动和控制 Chapter2.基本配置
弄了一个nginx.sh,这个版本的nginx.sh还是没有解决kill-HUP的资源冲突的问题,但解决了配置文件的问题。资源冲突的比如80端口被占用、日志文件目录没有创建这种的,我再想想办法。
1
#!/ bin/sh
2 BASE_DIR =’/data/’
3 ${BASE_DIR}nginx/sbin/nginx .t .c ${BASE_DIR}nginx/conf/
nginx.conf > & ${BASE_DIR}nginx/logs/nginx.start 4 info =` cat ${BASE_DIR}nginx/logs/nginx.start ` 5 if [`echo $info | grep .c "syntax is ok " ` .eq 1 ]; then 6 if [`ps aux|grep "nginx "|grep .c "master" ` == 1 ];
then 7 kill .HUP `cat ${ BASE_DIR } nginx / logs / nginx .pid ` 8 echo “ok ”
9
else
10
killall .9 nginx
11
sleep 1 12 ${BASE_DIR}nginx/sbin/nginx
13
fi
14
else 15 echo “######## error: ########” 16 cat ${BASE_DIR}nginx/logs/nginx.start
17
fi
Chapter 3
深入源码
3.
1源码结构
3.
2configure配置
3.
3nginx源码习惯
为了能更好的看懂代码,需要对nginx的源码习惯有所了解,这样读源码会更加容易一些,在所有的nginx的中实现的模块都有一个ngx_前缀,包括源文件的文件名,结构体和函数。http模块中的结构体和函数以ngx_http作为前缀。
nginx的数组的使用习惯,如下面/src/http/ngx_http_upstream.c文件中的代码所示:
1 if (uscf.>servers == NULL ) {
2 uscf.>servers = ngx_array_create (cf.>pool , 4, sizeof (
ngx_http_upstream_server_t ));
3 if (uscf .>servers == NULL ) {
4 return NGX_CONF_ERROR ;
5 }
6 }
7
8 us = ngx_array_push (uscf.>servers );
9 if (us == NULL ) {
10 return NGX_CONF_ERROR ;
11 }
12
13 ngx_memzero (us , sizeof ( ngx_http_upstream_server_t ));
如上面的代码所示,数组首先调用ngx_array_create创建一个数组对象,然后调用ngx_array_push函数得到一个指针,这个 指针指向一个对象,该对象的内存已经分配好了,可以直接使用。首先使用一个指针指向数组结构的elts字段,这样就得到了一个数组,该数组的大小为数组结 构体的nelts字段,使用的方式如下所示:
3.4.常用基础库 Chapter3.深入源码
1 uscfp = umcf.>upstreams.elts;
2 for (i = 0; i < umcf.>upstreams.nelts; i++) {
3 if ( uscfp [i].>host.len != u.>host.len ||
ngx_strncasecmp ( uscfp [i].>host.data, u.>host .data , u.>host.len)!= 0){
4
continue ; 5} 6}
对于其他数据结构的使用大致如此,在后面的讲解基础结构的时候详细解释。
在内存使用方面,尽量使用nginx提供的内存使用方式,在初始化或是需要长时间保存的内存尽量采用在cf->pool(即由全局配置分配的内存池)中分配内存的方式来分配内存,在每个请求
3.4常用基础库
由于c语言没有C++或是java那样丰富的库函数,所以一般会自己实现一些相对简单却非常实用的库函数,在nginx中有很多值得学习的基础库函数。在本书中只介绍一些使用频繁或是设计精巧的库函数。如果您是一个经验丰富的linux开发人员,本章可以略过。
字符串
任何一门编程语言中,字符串的操作都是最基本的知识,所以把这个基本操作单列出来。该模块在ngx_string.h和ngx_string.c中。字符 串的操作一般包括:初始化,复制,格式化输出,大小写转换,查找子字符,查找子字符串,字符串转换成数字,字符串编码类型相关函数,字符串比 较,trim,split等函数。在这个类中间没有调用其他模块的函数,作为一个http服务器,还需要实现URL转换,简单的html转换等函数。字符 串的结构体非常简单实用,是非常值得刚入门的linux开发工程师学习的。字符串的结构体为ngx_str_t,其两个字段如下:
Table 3.1:字符串结构体字段
字段 说明
len 字符串长度
data 字符串指针
字符串结构体由两个字段构成,一个是字符串的长度,一个字符串的首指针。这种结构体可以使得在使用字符串的时候异常简单,但是nginx的实现不够彻底还是有些使用c的字符串规则的函数,即以0作为字符串的最后一个字符。字符串函数如下表所列:
Chapter3.深入源码 3.4.常用基础库
Table 3.2:字符串结构体字段
函数 说明
ngx_string 初始化函数
ngx_null_string 初始化空字符串函数
ngx_tolower 字符转小写函数
ngx_toupper 字符转大写函数
ngx_strncmp 比较指定长度的字符串是否相同
ngx_strcmp 比较字符串是否相同
ngx_strstr 从字符串中找到需要的字符串
ngx_strlen 字符串的长度
ngx_strchr 在字符串中找到匹配的字符,返回 0为匹
配
ngx_strlchr 在字符串中找到匹配的字符,返回匹配的
指针
ngx_memzero 把一片内存区设置为0
ngx_memset 把一片内存区设置为指定的数
ngx_memcpy 复制内存,没有返回
ngx_cpymem 复制内存,返回复制完了 dst的最后一个
字符的下一个字符的指针
ngx_copy 同ngx_cpymem
ngx_memcmp 比较内存中的数据是否相同
ngx_strlow 把字符串都转换成小写
ngx_cpystrn 复制字符串,并且返回字符串的最后一个
字符的下一个字符的指针
ngx_pstrdup 复制字符串到pool,返回字符串的指针
ngx_sprintf 把各种类型的数据格式化输出到buf,最
大的长度为65536
ngx_snprintf 把各种类型的数据格式化输出到指定长度
的buf
ngx_strcasecmp 不分大小写比较两个字符串是否相同
ngx_strncasecmp 指定长短不分大小写比较两个字符串是否
相同
ngx_strnstr 在指定大小一个字符串中是否有子字符串
ngx_strstrn 在一个字符串中是否有子指定大小的字符
串
ngx_strcasestrn 在一个字符串中是否有子指定大小的字符
串,不区分大小写
ngx_rstrncmp 从后往前比较两个字符串是否相同,返回
相同的位置
ngx_rstrncasecmp 从后往前比较两个字符串是否相同,返回
相同的位置,不区分大小写
ngx_memn2cmp 比较两个指定长度的内存是否相同,也比
较长的内存是否包含短的内存
ngx_atoi 指定长度的字符串转换成数字
ngx_atosz 指定长度的字符串转换成 ssize_t类型数
字
ngx_atoof 指定长度的字符串转换成off_t类型数字
ngx_atotm 指定长度的字符串转换成time_t类型数字
3.4.常用基础库 Chapter3.深入源码
续表 3.8
函数 说明
ngx_hextoi 指定长度的字符串转换成十六进制数字
ngx_hex_dump 把数字转换成16进制的字符串
ngx_encode_base64 base64编码
ngx_decode_base64 base64解码
ngx_utf8_decode 把 utf8字符解码成双字节的 unicode或是
单字节字符,但是该函数会移动*p的值,
请注意
ngx_utf8_length 得到utf8编码的字符占几个字节
ngx_utf8_cpystrn 赋值utf8字符串,保证完整的复制
ngx_escape_uri 对uri进行编码
ngx_unescape_uri 对uri的进行解码
ngx_escape_html 对html进行编码
ngx_sort 排序,主要是用于数组排序
ngx_qsort 快速排序
ngx_value 把宏数字转换成字符串
为了测试,我们可以用以下两种方式打印出来
1 ngx_str_t str ;
2 printf (” %* s” , str . len , str . data );
3 peinrf (”%V”, & str );
内存分配
Nginx的内存分配的技巧很值得我们学习,设计的是非常精巧的,它会根据不同的生存周期建立起一系列的内存pool,在需要使用内存的时候,直接在 pool中获取内存即可。这种方法既加快了速度,又能够非常方便的管理内存,避免内存泄露。涉及到内存分配的文件主要有 ngx_alloc.h,ngx_alloc.c,ngx_buf.c,ngx_output_chain.c,ngx_buf.h,ngx_palloc.h 和ngx_palloc.c。
我们在讲解对内存池的创建和使用之前,我们先讲解一下封装的基本的分配内存的函数:
ngx_alloc
这个函数实际上是调用malloc()函数,但是记录了日志。C语言跟内存申请相关的函数主要有 alloc,calloc,malloc,free,realloc,sbrk等,其中alloc是向栈申请内存,因此无需释放,malloc分配的内存 是位于堆中的,并且没有初始化内存的内容,因此基本上malloc之后,调用函数memset来初始化这部分的内存空间,calloc则将初始化这部分的 内存,设置为0,而realloc则对malloc申请的内存进行大小的。
ngx_calloc
这个函数调用了ngx_alloc分配了内存之后,调用ngx_memzero把分配的内存都置为0。
ngx_memalign
Chapter3.深入源码 3.4.常用基础库
这个函数在linux下实际封装调用了memalign函数。能够分配连续的内存块。这个函数主要在分配大块的内存的时候使用。
ngx_free
这个函数就是free函数的宏定义。
内存池的结构体如下代码所示
1 struct ngx_pool_s {
2 ngx_pool_data_t d;
3 size_t max ;
4 ngx_pool_t * current ;
5 ngx_chain_t * chain ;
6 ngx_pool_large_t * large ;
7 ngx_pool_cleanup_t * cleanup ;
8 ngx_log_t * log ;
9 };
d字段为池数据;max字段为分配的内存大小减去ngx_pool_s的结构体所占内存大小,也就是能够使用的内存的大小;current字段为指 向ngx_pool_s结构体的指针;chain在ngx_alloc_chain_link函数中使用,主要是用来标示chain的,large是指向 大块内存的指针,这里的大块的意思是需要分配大于max大小的内存的时候,新分配的内存块,log为记录日志的结构体。
数据结构讲完了,我们来看一下相关函数。
ngx_create_pool
创建内存池,该函数的功能为分配内存,并初始化内存池的一些字段。
ngx_palloc
使用内存池,在分配好的内存池中分配一片内存使用,在当前需要分配的内存不够的时候,则再通过ngx_palloc_block分配一块与初始化的内存一 样大小的内存供使用,如果需要分配的内存的大小size大于max时候,该函数通过调用ngx_palloc_large分配一个需要 size+sizeof(ngx_pool_t)大小的内存。我们在为nginx编写module的时候,一般只需用调用这个函数分配内存就可以了。
ngx_destroy_pool
释放由ngx_create_pool创建的内存,以及在使用过程中所分配的资源。
ngx_reset_pool
释放掉large的资源,保留已分配的基本内存资源,并把ngx_pool_s结构体初始化。
除了内存的使用,nginx还提供了buf以及chain的使用。我们首先讲解buf的相关的结构体和函数,然后再讲解chain的结构体和函数。下面为buf的结构体:
1 typedef struct ngx_buf_s ngx_buf_t;
2 struct ngx_buf_s {
3 u_char * pos ;
4 u_char * last ;
5 off_t file_pos ;
6 off_t file_last ;
7 u_char * start ; /* start of buffer */
3.4. 常用基础库 Chapter3. 深入源码
8 u_char * end ; /* end of buffer */
9 ngx_buf_tag_t tag ;
10 ngx_file_t * file ;
11 ngx_buf_t * shadow ;
12
13 /* the buf ’s content could be changed */
14 unsigned temporary :1;
15
16 /*
17 * the buf ’s content is in a memory cache or in a
read only memory
18 * and must not be changed
19 */
20 unsigned memory :1;
21
22 /* the buf ’s content is mmap () ed and must not be
changed */
23 unsigned mmap :1;
24 unsigned recycled :1;
25 unsigned in_file :1;
26 unsigned flush :1;
27 unsigned sync :1;
28 unsigned last_buf :1;
29 unsigned last_in_chain :1;
30 unsigned last_shadow :1;
31 unsigned temp_file :1;
32 /* STUB */ int num ;
33 };
pos字段为当前未使用的buf的指针的开始,last字段标示使用的buf的内存的结束指针,start字段为buf内存的起始指针,temporary字段标示该buf是不是临时的,
ngx_create_temp_buf
在pool中创建一个临时的buf,并初始化一些参数。
ngx_buf_size
得到buf中的数据的大小。
ngx_alloc_buf
在内存池中分配一个buf的结构体。
ngx_calloc_buf
在内存池中分配一个buf的结构体,并把给结构体的所有有字段初始化为
0。
ngx_buf_in_memory
判断当前的buf是不是在内存中。
ngx_buf_in_memory_only
判断当前的buf是不是只在内存中。
Chapter3.深入源码 3.4.常用基础库
chain的相关的结构体如下:
1 struct ngx_chain_s {
2 ngx_buf_t * buf ;
3 ngx_chain_t * next ;
4 };
5
6 typedef ngx_int_t (* ngx_output_chain_filter_pt )( void *ctx
, ngx_chain_t *in);
7
8 typedef struct {
9 ngx_buf_t * buf ;
10 ngx_chain_t * in ;
11 ngx_chain_t * free ;
12 ngx_chain_t * busy ;
13
14 unsigned sendfile ;
15 unsigned directio ;
16 # if ( NGX_HAVE_ALIGNED_DIRECTIO )
17 unsigned unaligned ;
18 # endif
19 unsigned need_in_memory ;
20 unsigned need_in_temp ;
21 ngx_pool_t * pool ;
22 ngx_int_t allocated ;
23 ngx_bufs_t bufs ;
24 ngx_buf_tag_t tag ;
25 ngx_output_chain_filter_pt output_filter ;
26 void * filter_ctx ;
27 } ngx_output_chain_ctx_t ;
28
29 typedef struct {
30 ngx_chain_t * out ;
31 ngx_chain_t ** last ;
32 ngx_connection_t * connection ;
33 ngx_pool_t * pool ;
34 off_t limit ;
35 } ngx_chain_writer_ctx_t ;
ngx_chain_s结构体很简单,buf字段就是一个指向缓存的指针,next指向下一个chain。
ngx_output_chain_ctx_t结构体就复杂的多,主要是在ngx_http_copy_filter_module.c和ngx_output_chain.c文件中使用
chain的函数主要在ngx_buf.c和ngx_output_chain.c文件中,其中重要的函数有如下几个:
ngx_alloc_chain_link
就是一个分配ngx_chain_t内存的函数。
ngx_create_chain_of_bufs
3.4.常用基础库 Chapter3.深入源码
在pool中建立一个bufs->num个bufs->size大小的buf,最后返回链表头的指针。 ngx_chain_add_copy把一个链表复制到另外一个链表中,只复制头,不复制内容。 ngx_chain_get_free_buf得到链表中未使用的buf,如果没有,则分配一个。 ngx_chain_update_chains把out的chain变成free的,并挂接到free的chain上。
文件读写和配置文件读取
文件读写是所有的编程语言绕不开的,对于java,python等高级语言来说都有封装好的,跨平台的文件读写文件。对于c语言来说,如果需要支持多个操 作系统,就需要封装一下文件的读写。封装文件的读写还有一个益处就是能够把读写异常,读写的内存控制,日志的记录封装起来,以便于其他的模块更好的应用。 文件的读写一般会封装成打开文件,关闭打开的文件,读写文件。在nginx的源码中,文件读写主要放在core/ngx_file.c,core /ngx_file.h,src/os/unix/ngx_files.h和src/os/unix/ngx_files.c中。由于nginx的文件读 写函数较多,我们只是详细介绍比较重要,经常使用,实现很有技巧的函数。
文件相关的的数据结构有路径,文件,临时文件等数据结构体。
路径的数据结构如下:
1 struct ngx_path_s {
2 ngx_str_t name ;
3 size_t len ;
4 size_t level [3];
5 ngx_gc_handler_pt cleaner ;
6 u_char * conf_file ;
7 ngx_uint_t line ;
8 };
ngx_path_s结构体中name字段为路径数据字符串,level为临时目录的三级目录的大小,len为目录的,conf_file为该路径的来源的配置文件,line为该路径在来源的配置文件中的行数,这两个字段主要用来记录日志,来排查错误。
我们首先介绍简单封装的函数,再介绍复杂封装的函数,简单封装的函数有如下函数:
ngx_open_file/ngx_close_file
打开/关闭文件的函数,这个函数其实就是open/close函数,在nginx中为了更好的使用系统资源,没有使用fopen/fread/fclose来处理文件,打开的参数也做了封装,如下面所示
1 # define NGX_FILE_RDONLY O_RDONLY
2 # define NGX_FILE_WRONLY O_WRONLY
3 # define NGX_FILE_RDWR O_RDWR
4 # define NGX_FILE_CREATE_OR_OPEN O_CREAT
Chapter3. 深入源码 3.4. 常用基础库
5 6 7 # define # define # define NGX_FILE_OPEN NGX_FILE_TRUNCATE NGX_FILE_APPEND 0 O_TRUNC O_APPEND
ngx_delete_file删除文件函数,调用系统的unlink函数,unlink()会删除pathname指定的文件。如果该文件名最后连接点,但有其他进程打开了此文件,则在所有关于此文件的文件描述词皆关闭后才会删除。如果参数pathname为一符号连接
(symboliclink),则此连接会被删除。复杂封装的函数 ngx_open_tempfile打开一个临时文件,配置文件一般有三个作用:根据配置文件启用某段程序,用配置文件传入
参数,设定启动程序程序之间的相互关系。Nginx的配置文件也实现了这三部分的功能,nginx的配置文件主要有以下几个主要的结构体和函数配置文件的结构体有:
Table 3.3:配置文件结构体列表
结构体 说明
ngx_command_s 定义了配置文件中的 tag的配置,以及遇到该
tag该怎么处理的函数
ngx_open_file_s 定义了打开文件的参数的结构体
ngx_conf_file_t 定义了缓存配置文件的数据的结构体
ngx_conf_s 定义了配置文件解析过程中需要缓存的数据
ngx_command_s这个结构体定义了配置文件中的tag,以及遇到该tag,该怎么处理,其结构如下表:
Table 3.4:ngx_command_s结构体字段列表
结构体 说明
name 配置文件中的tag
Type 配置类型,这个参数中会定义这个配置是什么
范围内的配置(核心配置或是普通配置),以
及有多少参数,是块配置,还是行配置
Set 解析该配置的函数
Conf
Offset 指定配置存储的位置
Post 指向模块在读配置的时候需要的一些零碎变
量。一般它是NULL
在每一个ngx_command_s数组的结尾必须有一个ngx_null_command,以用于判断是否结束。
配置文件解析的入口为ngx_conf_parse,如果了解了ngx_conf_parse,对nginx的配置文件的原理就有了基本的了解,下面我们详细的解释一下
3.4.常用基础库 Chapter3.深入源码
ngx_conf_parse,该函数在ngx_conf_file.c文件中。首先我们来看函数的定义
1 char *ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename)
这个函数有两个参数,一个是cf,这个参数既是输入参数,也是输出参数,在文件名为空的情况下,第二个参数是文件名,可能为空,一般在解析配置块或是一行的时候。对于程序员来说,简单的代码比文字说明更清晰,所以,我们用以下的伪代码说明配置文件的读取:
1 ngx_conf_parse{ 2 if (filename) { 3 ngx_open_file 4}
5
6 for ( ;; ) { 7 ngx_conf_read_token{ 8}
9
10
if (cf.>handler) { //这个函数只在模块的时候使用types 11 (*cf.>handler)(cf, NULL, cf.>handler_conf);
12 } 13
14 ngx_conf_handler{
15 }
16 }
17
18 if (filename) { 19 ngx_close_file(fd)
20 } 21 }
从上面伪代码中我们看到,ngx_conf_parse函数就只有ngx_conf_read_token函数和ngx_conf_handler函数是正真处理配置文件的函数。下面我们说明这两个函数:
ngx_conf_read_token函数的主要功能是读取并解析配置文件,解析的参数存到cf->args中,读取到“;”标点则返回,读取到“(”,则接着读取,读取到“)”则直接完成。这个函数比较简单,我们就不详细说明了。
ngx_conf_handler函数主要功能是首先验证配置有没有问题,然后调用cmd->set函数。
ngx_conf_parse函数除了在ngx_init_cycle函数中调用外,在配置块的解析中,即cmd->set函数(例如解析http 配置块的ngx_http_block函数)中也会调用该函数,从某种程度上理解该函数其实是一个递归函数,只不过中间调用了其他函数。在讲解http的 配置文件那一章我们会根据http的配置例子详细的讲解。
Chapter3.深入源码 3.4.常用基础库
日志
日志文件的作为一个程序最基本的模块,一直是受到人们的重视的,跟java程序中的log4j一样,c语言也有一些开源的c语言的log模块。Log模块 的功能一般是根据不同的级别设定决定是否写入到指定的文件中。为了不影响主程序的运行,模块一般是单独一个线程来执行写入工作的。Nginx的log实现 在nginx_log.h和nginx_log.c的文件中。 Nginx的错误日志分成九个级别,由低到高分别为:debug,info,notice,warning,error,crit日志模块的结构体为 ngx_log_s,其内容如下代码:
1 struct ngx_log_s {
2 ngx_uint_t log_level ;
3 ngx_open_file_t * file ;
4 ngx_atomic_uint_t connection ;
5 ngx_log_handler_pt handler ;
6 void * data ;
7 /*
8 * we declare ” action ” as ” char *” because the
actions are usually
9 * the static strings and in the ” u_char *” case we
have to override
10 * their types all the time
11 */
12 char * action ;
13 };
其中log_level为打印的级别,
数组、链表、队列和TREE等数据结构
在nginx中,有开发人员自己实现的数组,队列和tree等数据结构。主要在src/core/目录下。对于每个数据结构都需要实现创建,插入,删除和查找等功能。
数组
数组的定义在src/core/ngx_array.h和src/core/ngx_array.c文件中,这个数组时可以自动扩展长度的,即动态数组。这个数据结构在很多地方使用,因此我们详细介绍这个数据结构。首先我们看一下数组数怎么定义的,看下面的代码:
1 struct ngx_array_s {
2 void * elts ;
3 ngx_uint_t nelts ;
4 size_t size ;
5 ngx_uint_t nalloc ;
6 ngx_pool_t * pool ;
7 };
3.4.常用基础库 Chapter3.深入源码
数组的定义很简单elts字段即存储数据的内存块,nelts字段为已经使用了数据的个数,size为每个数据的大小,nalloc为分配的数据的个数,pool为分配内存大小的pool。
数组的函数都很简单,所以我们只是简单的列出来,并写出其功能。
Table 3.5:数组的函数列表
函数 说明
ngx_array_create 在未创建ngx_array_t结构体时创建数组
ngx_array_init 在创建ngx_array_t结构体后创建数组
ngx_array_destroy destroy数组
ngx_array_push 在数组中压入一个数据,得到一个数据指针,
在这里就不用再分配内存了,可以直接使用分
配的内存
ngx_array_push_n 在数组中压入 n个数据,得到一个数据指针,
在这里就不用再分配内存了,可以直接使用分
配的内存
链表
nginx的链表不同于其他实现的链表,是带有明显的nginx的风格的链表,它类似数组的实现。链表的源码在src/core/ngx_list.h和 src/ core/ngx_list.c中。数据结构就与普通的链表不同,既不像linux的kernel中实现的那样仅仅就是实现一个链表头,也不像普通的链表 那样把链表的前后链接头作为需要实现的数据结构的一个字段来实现,而是类似数组那样,把数据存到事先分配好的内存中。首先我们先看一下链表的数据结构吧, 如下面的代码所示:
1 struct ngx_list_part_s {
2 void * elts ;
3 ngx_uint_t nelts ;
4 ngx_list_part_t * next ;
5 };
6 typedef struct {
7 ngx_list_part_t * last ;
8 ngx_list_part_t part ;
9 size_t size ;
10 ngx_uint_t nalloc ;
11 ngx_pool_t * pool ;
12 } ngx_list_t ;
我们先看ngx_list_t数据结构,pool当然是指分配内存的内存池,size是指数据的大小,nalloc指已分配的每个 ngx_list_part_t的大小,每个分配的part所指的内存大小实际的大小为size*nalloc。part字段是初始化时分配的第一个内存 块,每次遍历的时候也是从这个内存块开始的。last是指向最后一个ngx_list_part_t内存块。
ngx_list_part_s结构体中的elts字段指向分配存储数据的内存,nelts是指在该ngx_list_part_s中,已经使用了多少个数据块,next则是指下一个
Chapter3.深入源码 3.4.常用基础库
ngx_list_part_s的指针。为了更加清晰的了解这个数据结构,我们看下图就清楚了。
数据结构弄清楚了,函数就很简单了,所以我们只是简单的列出来,并写出其功能。
Table 3.6:链表的函数列表
函数 说明
ngx_list_createngx_list_initngx_list_push 在未创建ngx_list_t结构体时创建列表 在创建ngx_list_t结构体后创建列表 在列表中压入一个数据,得到一个数据指针,在这里就不用再分配内存了,可以直接使用分配的内存
队列
队列的实现在src/core/ngx_queue.h和src/core/ngx_queue.c文件中,实现也是非常的简单,相当于一个双向链表,首先我们依然看其数据结构:
1 typedef struct ngx_queue_s ngx_queue_t ;
2 struct ngx_queue_s {
3 ngx_queue_t * prev ;
4 ngx_queue_t * next ;
5 };
这个数据结构非常简单,就一个prev指针指向上一个队列节点,next指向下一个队列节点。这个数据结构中没有数据,所以需要嵌入到数据当中作为数据结构的一个字段。
队列的函数很多,但是都很简单,我们依然只需要列出来,在使用的时候知道这个函数是做什么的就可以了,函数列表如下:
红黑树
红黑树在ngx_rbtree.c和ngx_rbtree.h中实现。红黑树能够以O(log2n)的时间复杂度进行搜索、插入、删除操作。此外,由于它的设计,任何不平衡都
3.5.core模块 Chapter3.深入源码
Table 3.7:链表的函数列表
函数说明
ngx_queue_init初始化队列 ngx_queue_empty清空节点中在某一个节点之前的所有节点 ngx_queue_insert_head插入一个节点在指定节点之后ngx_queue_insert_after插入一个节点在指定节点之后
ngx_queue_insert_tail 插入一个节点在指定节点之前
ngx_queue_head 得到节点的下一个节点
ngx_queue_next 得到节点的下一个节点
ngx_queue_last 得到节点的上一个节点
ngx_queue_prev 得到节点的上一个节点
ngx_queue_sentinel 得到当前的节点
ngx_queue_remove 删除队列中的某一个节点
ngx_queue_data 得到队列某一节点的数据
ngx_queue_split
ngx_queue_add
ngx_queue_middle
ngx_queue_sort
会在三次旋转之内解决。当然,还有一些更好的,但实现起来更复杂的数据结构能够做到一步旋转之内达到平衡,但红黑树能够给我们一个比较“便宜”的解 决方案。红黑树的算法时间复杂度和AVL相同,但统计性能比AVL树更高。当然,红黑树并不适应所有应用树的领域。如果数据基本上是静态的,那么让他们待 在他们能够插入,并且不影响平衡的地方会具有更好的性能。如果数据完全是静态的,例如,做一个哈希表,性能可能会更好一些。
hash表
HASH、CRC和MD5等算法实现
hash算法
管道、线程,时间等包装的系统函数
3.5core模块
linux服务器demo
单进程单线程
1 /* server .c */
2 # include < stdio .h >
3 # include < stdlib .h >
4 # include < string .h >
5 # include < unistd .h >
6 # include
Chapter3.深入源码 3.5.core模块
7 # include < netinet / in .h >
8
9 # define MAXLINE 80
10 # define SERV_PORT 8000
11
12 int main ( void ) {
13 struct sockaddr_in servaddr ;
14 struct sockaddr_in cliaddr ;
15 socklen_t cliaddr_len ;
16 int listenfd = .1;
17 int connfd = .1;
18 char buf [ MAXLINE ] = {0};
19 char str [ INET_ADDRSTRLEN ]= {0};
20 int i = 0;
21 int n = 0;
22
23 listenfd = socket ( AF_INET , SOCK_STREAM , 0);
24 bzero (& servaddr , sizeof ( servaddr ));
25 servaddr . sin_family = AF_INET ;
=
26 servaddr.sin_addr.s_addr htonl(INADDR_ANY); 27 servaddr.sin_port = htons(SERV_PORT);
28
bind(listenfd, ( struct sockaddr *)&servaddr, sizeof ( servaddr));
29
listen(listenfd, 20);
30
printf(”Accepting connections …\n”); 31 while (1) { 32 cliaddr_len = sizeof (cliaddr); 33 connfd = accept(listenfd, ( struct sockaddr *)&
cliaddr, &cliaddr_len); 34 n = read(connfd, buf, MAXLINE); 35 printf(”received from %s at PORT %d\n”,inet_ntop(
AF_INET , 36 &cliaddr.sin_addr, str, sizeof (str)), ntohs(
cliaddr.sin_port)); 37 for (i = 0; i 39 } 40
write(connfd, buf, n);
41
close(connfd);
42 } 43 }
以上的代码主要是根据本人的代码习惯编写的,在无数次的经验教训中的来的,有这么一点需要主意的,就是所有的参数都要给初始值(除非不能给的),这样就能更好的控制每个变量的值,尽量减少内存泄露的bug产生。
3.5.core模块 Chapter3.深入源码
单进程多线程多进程单线程多进程多线程
nginx实现机制
nginx的模块选择机制是通过生成ngx_modules.c文件的方式配置
nginx的实现是多进程单线程服务器(多线程模式基本不用,所以我们认为就是多进程单线程),nginx的主要函数的调用相关性如下代码所示:
1 main {
2 //初始化cycle
3 ngx_init_cycle {
4
5 //调用一系列核心模块的create_conf
6 ngx_core_module_create_conf
7
8 //解析配置文件,并初始化一系列的数据结构或是参数
9 ngx_conf_parse {
10
11 }
12
13 //调用一系列核心模块的init_conf
14 ngx_core_module_init_conf
15
16
17 ngx_open_listening_sockets {
18 bind
19 listen
20 }
21
22 //配置监控的listening
23 ngx_configure_listening_socket
24
25 //执行各个模块的函数init_module
26 for (i = 0; ngx_modules [i]; i ++) {
27 init_module
28 }
29 }
30
31 // daemon
32 ngx_daemon
33
34 //主进程主要处理函数
35 ngx_master_process_cycle {
36 //启动工作进程
37 ngx_start_worker_processes {
38 ngx_spawn_process {
Chapter3. 深入源码 3.5. core模块
39 //工作进程执行程序部分
40 ngx_worker_process_cycle {
41 //初始化工作进程
42 ngx_worker_process_init {
43 //执行各个模块的init_process
44 init_process
45 }
46
47 //调用模块的入口event
48 ngx_process_events_and_timers
49 }
50
51 }
52 }
53
54 //循环检查子进程的状态,以及等待信号的输入
55 for ( ;; ) {
56 //如果重新载入配置文件,例如做出了kill .HUP [ pid命
令之后]
57 if ( ngx_reconfigure ) {
58 ngx_init_cycle
59 ngx_start_worker_processes
60 }
61
62 //程序文件变化,用于平滑升级
63 if ( ngx_change_binary ) {
64 ngx_exec_new_binary {
65 ngx_execute {
66 ngx_spawn_process {
67 ngx_execute_proc {
68 //启动一个新的进程
69 execve
70 }
71 }
72 }
73 }
74 }
75 }
76 }
77 }
通过上述伪代码,我们能够大概了解nginx的整个流程,接下来我们详细的描述上述代码中的重点函数。
main函数
ngx_init_cycle函数
ngx_start_worker_processes函数
3.
6.event模块 Chapter3.深入源码
3.
6event模块
对于event模块,我们将着重说明epoll模型的相关代码,其他的event模型由于跟epoll类似,将不再累述,
epoll
由于Select和Poll在连接数增加时,性能急剧下降。这有两方面的原因:首先操作系统面对每次的select/poll操作,都需要重新建立一个当 前线程的关心事件列表,并把线程挂在这个复杂的等待队列上,这是相当耗时的。其次,应用软件在select/poll返回后也需要对传入的句柄列表做一次 扫描来dispatch,这也是很耗时的。这两件事都是和并发数相关,而I/O事件的密度也和并发数相关,导致CPU占用率和并发数近似成O(n^2)的 关系。因为以上的原因,*nix的hacker们开发了epoll,kqueue,/dev/poll这3套利器来帮助大家,让我们跪拜三分钟来感谢这些 大神。其中epoll是linux的方案,kqueue是freebsd的方案,/dev/poll是最古老的Solaris的方案,使用难度依次递增。 简单的说,这些api做了两件事:1.避免了每次调用select/poll时kernel分析参数建立事件等待结构的开销,kernel维护一个长期的 事件关注列表,应用程序通过句柄修改这个列表和捕获I/O事件。2.避免了select/poll返回后,应用程序扫描整个句柄表的开销,Kernel直 接返回具体的事件列表给应用程序。在接触具体api之前,先了解一下边缘触发(edge trigger)和条件触发(level trigger)的概念。边缘触发是指每当状态变化时发生一个io事件,条件触发是只要满足条件就发生一个io事件。举个读socket的例子,假定经过 长时间的沉默后,现在来了100个字节,这时无论边缘触发和条件触发都会产生一个read readynotification通知应用程序可读。应用程序读了50个字节,然后重新调用api等待io事件。这时条件触发的api会因为还有50个 字节可读从而立即返回用户一个read readynotification。而边缘触发的api会因为可读这个状态没有发生变化而陷入长期等待。因此在使用边缘触发的api时,要注意每次都要 读到socket返回EWOULDBLOCK为止,否则这个socket就算废了。而使用条件触发的api时,如果应用程序不需要写就不要关注 socket可写的事件,否则就会无限次的立即返回一个write readynotification。大家常用的select就是属于条件触发这一类。epoll用到的所有函数都是在头文件sys/epoll.h中声 明的,下面简要说明所用到的数据结构和函数:
所用到的数据结构:
1 typedef union epoll_data {
2 void *ptr;
3
int fd; 4 __uint32_t u32;
5 __uint64_t u64;
6 } epoll_data_t ;
7
8 struct epoll_event {
9 10 11 }; __uint32_t events ; epoll_data_t data ; /* /* Epoll events */ User data variable */
12
13 14 typedef union epoll_data void * ptr ; {
Chapter3. 深入源码 3.6. event模块
15 int fd ;
16 __uint32_t u32 ;
17 __uint64_t u64 ;
18 } epoll_data_t ;
19
20 struct epoll_event {
21 __uint32_t events ; /* Epoll events */
22 epoll_data_t data ; /* User data variable */
23 };
结构体epoll_event被用于注册所感兴趣的事件和回传所发生待处理的事件,其中epoll_data联合体用来保存触发事件的某个文件描述 符相关的数据,例如一个client连接到服务器,服务器通过调用accept函数可以得到于这个client对应的socket文件描述符,可以把这文 件描述符赋给epoll_data的fd字段以便后面的读写操作在这个文件描述符上进行。epoll_event结构体的events字段是表示感兴趣的 事件和被触发的事件可能的取值为:
1.
EPOLLIN:表示对应的文件描述符可以读;
2.
EPOLLOUT:表示对应的文件描述符可以写;
3.EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
4.
EPOLLERR:表示对应的文件描述符发生错误;
5.
EPOLLHUP:表示对应的文件描述符被挂断;
6.
EPOLLET:表示对应的文件描述符有事件发生;
所用到的函数:
1、epoll_create函数
函数声明:int epoll_create(int size)该函数生成一个epoll专用的
文件描述符,其中的参数是指定生成描述符的最大范围(我觉得这个参数和select函数的第一个参数应该是类似的但是该怎么设置才好,我也不太清 楚)。其中maxfds为你epoll所支持的最大句柄数。这个函数会返回一个新的epoll句柄,之后的所有操作将通过这个句柄来进行操作。
2、epoll_ctl函数函数声明:int epoll_ctl(int epfd, int op, int fd, struct epoll_event
*event)该函数用于控制某个文件描述符上的事件,可以注册事件,修改事件,删除事件。
参数:epfd:由epoll_create生成的epoll专用的文件描述符;op:要进行的操作例如注册事件,可能的取值EPOLL_CTL_ADD 注册、EPOLL_CTL_MOD修改、EPOLL_CTL_DEL删除fd:关联的文件描述符;event:指向epoll_event的指针;如果调 用成功返回0,不成功返回-1
3、epoll_wait函数
函数声明:int epoll_wait(int epfd,struct epoll_event * events,intmaxevents,int timeout)该函数用于轮询I/O事件的发生;
参数:epfd:由epoll_create生成的epoll专用的文件描述符; epoll_event:用于回传代处理事件的数组;maxevents:每次能处理的事件数; timeout:等待I/O事件发生的超时值;返回发生事件数。epoll完全是select/poll的升级
3.7.http模块 Chapter3.深入源码
版,支持的事件完全一致。并且epoll同时支持边缘触发和条件触发,一般来讲边缘触发的性能要好一些。这里有个简单的例子:
1 struct epoll_event ev , * events ;
2 int kdpfd = epoll_create (100) ;
3 ev . events = EPOLLIN | EPOLLET ; // 注意这个,指定了边缘触
发EPOLLET
4 ev . data . fd = listener ;
5 epoll_ctl ( kdpfd , EPOLL_CTL_ADD , listener , &ev );
6 for (;;) {
7 nfds = epoll_wait ( kdpfd , events , maxevents , .1);
8 for (n = 0; n < nfds ; ++ n) {
9 if ( events [n ]. data . fd == listener ) {
10 client = accept ( listener , ( struct sockaddr *) &
local , & addrlen );
11 if ( client < 0) {
12 perror (” accept “);
13 continue ;
14 }
15 setnonblocking ( client );
16 ev . events = EPOLLIN | EPOLLET ;
17 ev . data .fd = client ;
18 if ( epoll_ctl ( kdpfd , EPOLL_CTL_ADD , client , & ev )
< 0) { 19 fprintf ( stderr , ” epoll set insertion error : fd =% d0 , client ); 20 return .1; 21 } 22 } 23 else 24 do_use_fd ( events [n]. data . fd); 25 } 26 } nginx实现接口,对于每个 3.7http模块 http模块都在src/http/目录下,为了更好的说明http模块是怎么运行的,我们从一个简单的例子说起,这个例子其实就是一个配置文件的一部 分,他能很好的说明http模块是怎么初始化,再有请求的时候怎么处理的。配置文件如下: 1 http { 2 server { 3 listen : 80; 4 location / test / { 5 rewrite / server / test /; 6 } Chapter3.深入源码 3.7.http模块 7 8 location / { 9 rewrite / server / htm /; 10 } 11 12 } 13 server { 14 listen : 90; 15 location / { 16 rewrite / server / htm /; 17 } 18 location / test / { 19 rewrite / server / test /; 20 } 21 } 22 } http模块定义 http模块主要有两个个部分:解析配置文件并初始化,请求处理。这两部分的入口分别为ngx_http_block和 ngx_http_init_connection。 解析配置文件并初始化 在说明nginx的配置之前,我们需要说明在nginx中,http模块是有一些设定的,在http模块中,分为http范围,server范 围,location范围的配置,这三个范围的配置分别对应一些配置文件,以及对应着http配置块,server配置块,location配置块。 http配置文件处理的入口为ngx_http_block函数,在配置文件遇到http模块的时候就调用该函数,如上面的配置文件所示,全局的http 配置块只有一个,在http配置块下会有多个server配置块(虚拟主机配置),server配置块可以理解为配置的一个服务器,它监听单独的端口,而 在server配置块下有多个location配置块,location配置块配置了服务器下的某个URL下的处理模块或是代理模块;在这里用 rewrite模块作为示例。我们通过伪代码来介绍nginx怎么解析nginx配置文件的,解析配置文件并初始化结构体主要由下面三步完成: 1. 根据配置文件创建结构体并初始化参数 2. 合并配置并初始化variable以及初始化phase 3. 根据服务器的配置,初始化服务器,简单的说,就是把服务端口相关参数、函数,赋给cycle结构体中。 根据配置文件创建结构体并初始化参数,在解析过程中,系统会反复调用ngx_conf_parse函数。如下面的伪代码所示: 1 ngx_http_block { 2 3 //循环调用每一个函数,这里只有和的函数main_confcorerewrite 3.7.http模块 Chapter3.深入源码 4 ngx_http_core_create_main_conf 6 //循环调用每一个函数,这里只有和的函数srv_confcorerewrite 7 ngx_http_core_create_srv_conf 8 9 //循环调用每一个函数,这里只有和的函数loc_confcorerewrite ngx_http_core_create_loc_conf 11 ngx_http_rewrite_create_loc_conf 12 13 //循环调用每一个函数,这里只有和的函数preconfigurationcorerewrite 14 ngx_http_core_preconfiguration 16 ngx_conf_parse{ 17 ngx_http_core_server{ //监听端口的80server 18 //循环调用每一个函数,这里只有和的函数srv_confcorerewrite 19 ngx_http_core_create_srv_conf 21 //循环调用每一个函数,这里只有和的函 数loc_confcorerewrite 22 ngx_http_core_create_loc_conf 23 ngx_http_rewrite_create_loc_conf 24 ngx_conf_parse{ 26 ngx_http_core_listen 27 28 ngx_http_core_location{ // 29 //循环调用每一个函数,这里只有和的函数loc_confcorerewrite ngx_http_core_create_loc_conf 31 ngx_http_rewrite_create_loc_conf 32 33 ngx_conf_parse { 34 ngx_http_rewrite } 36 } 37 38 ngx_http_core_location { // 39 //循环调用每一个函数,这里只有和的函 数loc confcorerewrite _ ngx_http_core_create_loc_conf 41 ngx_http_rewrite_create_loc_conf 42 43 ngx_conf_parse{ 44 ngx_http_rewrite } 46 } 47 } 48 } 49 } Chapter3.深入源码 3.7.http模块 50 ngx_conf_parse{//监听端口的90,跟上面基本一致,就不累述 了server 51 …… 52 } 53 54 //对于解析后的配置的结构体进行合并等处理 55 …… 56 57 //对配置的端口进行处理,以便于全局处理 58 …… 59 } 在讲述上面的伪代码之前,我们看到,系统首先会创建main模式下的结构体内存,首先初始化一个ngx_http_conf_ctx_t结构体,这个结构 体在全局使用,这个结构体中的main_conf数组会在http的所有范围内共享。该结构体的srv_conf数组则在服务器范围内共享,例如在上述配 置中的80端口server配置块下的配置,是在该配置下均可使用,即只要是访问80端口,即可使用该配置。该结构体中的loc_conf是标示在URL 范围内的配置的,但是在ngx_http_block中创建的loc_conf只是在配置合并的时候作为初始化参数使用。 创建完毕之后即调用ngx_http_core_preconfiguration函数,这个函数会初始化一些参数,在这里就不累述了,我们会在参数那一 节讲解。 紧接着就是调用ngx_conf_parse函数,这个函数主要是解析mian范围内的配置,例如遇到 root配置则会调用 ngx_http_core_root函数,用于解析配置。除了解析这些配置之外,还会碰到server配置,这样他就会调用 ngx_http_core_server函数。在ngx_http_core_server函数中,不再创建http全局范围内的man_conf数 组,而是直接创建一个server范围内的ngx_http_conf_ctx_t结构体,用main_conf指针直接指向的 ngx_http_block中创建的main_conf。在ngx_http_core_server函数中,再次调用创建server范围和 location范围内的创建结构体函数,即 ngx_http_core_create_srv_conf,ngx_http_core_create_loc_conf和 ngx_http_rewrite_create_loc_conf等函数。 server范围内的解析函数ngx_conf_parse,除了调用ngx_http_core_listen函数解析server范围内的配置外,在 配置文件中遇到location标示时还会调用ngx_http_core_location函数,这个函数再次创建一个 ngx_http_conf_ctx_t结构体,这个结构体中的main_conf和srv_conf指向上一级server范围内的 ngx_http_conf_ctx_t结构体中的main_conf和srv_conf数组。创建一个loc_conf数组。 在解析完毕之后,在内存中会建立如下数据结构: 3.7.http模块 Chapter3.深入源码 在上述图中,我们只是标识出一个server范围内的和一个location范围内的ngx_http_conf_ctx_t结构体,实际上在解析我们提 供的样例配置文件中,会有对应的2个server范围内的ngx_http_conf_ctx_t结构体和4个location范围内的 ngx_http_conf_ctx_t结构体。 从上图中我们可以看到首先有一个http全局范围内的ngx_http_conf_ctx_t结构体,该结构体的main_conf字段指向一个http 全局范围内的数据结构数组。在该数组中有一个ngx_http_core_main_conf_t结构体,该结构体中有一个servers字段,这个字段 存储了下一级,即server范围内的配置数组的头的指针。通过遍历可以得到所有的server范围内的配置。在server范围内的配置数据结构数组中 有一个ngx_http_core_srv_conf_t结构体,该结构体中的ctx是指向该范围内的ngx_http_conf_ctx_t结构体。在 初始化完毕之后,系统实际上也是把这一级的配置的ctx绑定到相应监听的端口的。 在上图中我们还看到,在server范围内的ngx_http_conf_ctx_t结构体中 Chapter3.深入源码 3.7.http模块 的loc_conf字段所指的数组,该数组中的ngx_http_core_loc_conf_t结构体中有一个locations字段,该字段是一个 queue,里面记录了该server范围下的location配置。 总的来说,在上述的数据结构中实际上是一个三级树形结构,在http全局范围内的配置包含多个server范围内的配置结构体,在server范围内的配 置数据结构包含多个location范围的配置结构体。 1 ngx_http_block { 2 //初始化一些基本参数,并根据配置文件进行解析 3 …… 4 5 cscfp = cmcf .>servers . elts ;
6
7 ngx_http_core_init_main_conf
8 for (s = 0; s < cmcf.>servers . nelts ; s ++) {
9 ngx_http_core_merge_srv_conf
10 ngx_http_core_merge_loc_conf
11 ngx_http_rewrite_merge_loc_conf
12 ngx_http_merge_locations
13 }
14
15 ngx_http_init_phases
16
17 ngx_http_init_headers_in_hash
18
19 ngx_http_variables_init_vars
20
21 ngx_http_init_phase_handlers
22
23 //对配置的端口进行处理,以便于全局处理
24 …….
25 }
在初始化并根据配置文件创建了结构体之后,nginx再调用merge函数,这里的merge函数实际上就是把高级别范围内的相同结构体中的值复制到低级别范围内的同样的结构体中值。
合并参数完毕之后,系统创建并初始化phases。在这一节我们就不详细讲解nginx是怎么处理variables了,所以在这里就不详细说明 ngx_http_init_headers_in_hash和ngx_http_variables_init_vars函数了。系统创建并初始化 phases是由ngx_http_init_phases和ngx_http_init_phase_handlers函数完成的,其中在nginx中 所使用的phase如下表所示:
3.7. http模块 Chapter3. 深入源码
Table 3.8:字符串结构体字段
参数名 checker函数 是否唯一 说明
NGX_HTTP_POST ngx_http_core 在
_READ_PHASE _generic_phasengx_http_realip_module.c文件的 ngx_http_realip_init函数中调用,主要作用就是在处理函数之前处理一些http的头。例如获取请求的实际IP,即realip。
NGX_HTTP_SERVER ngx_http_core_generic_phase 使用rewrite,在
_REWRITE_PHASE ngx_http_rewrite_module.c文件的 ngx_http_rewrite_init函数中注册
NGX_HTTP_FIND _CON-ngx_http_core 处理请求的主要模块
FIG_PHASE _find_config_phase
NGX_HTTP_REWRITE ngx_http_core 唯一 使用rewrite,在
_PHASE _generic_phasengx_http_rewrite_module.c文件的 ngx_http_rewrite_init函数中注册
NGX_HTTP_POST ngx_http_core 唯一 当前未使用
_REWRITE_PHASE _post_rewrite_phase
NGX_HTTP_PREACCESS_PHASE 在nginx的模块中有三
ngx_http_core_generic_phase个函数push到该数组中
NGX_HTTP_ACCESS_PHASE ngx_http_core_access_phase有两个模块在该phase中注册,他们分别是access和auth_basic
NGX_HTTP_POST _AC-ngx_http_core 唯一 当前未使用
CESS_PHASE _post_access_phase
NGX_HTTP_TRY ngx_http_core 唯一 当前未使用
_FILES_PHASE _try_files_phase
NGX_HTTP_CONTENT_PHASEngx_http_core_content_phase有很多模块都是在这个phase中实现,例如dev,index,gzip等模块
NGX_HTTP_LOG _PHASE ngx_http_core 记录日志,在 _generic_phasengx_http_log_module.c文件中的ngx_http_log_init函数中注册。主要用来记录日志
1 ngx_http_block{
Chapter3. 深入源码 3.7. http模块
2 //初始化一些基本参数,并根据配置文件进行解析,并进行参数merge
3 ……
4
5 ngx_http_init_server_lists
6
7 ngx_http_optimize_servers
8 }
在我们详细了解了系统怎么解析配置文件并初始化了之后,我们还漏掉了一个重要的处理过程,那就是对location配置的一些处理
请求处理参数的处理
参数的主要的数据结构有ngx_http_variable_s(ngx_http_variable_t)等结构体,其中ngx_http_variable_s结构体如下:
1 struct ngx_http_variable_s {
2 ngx_str_t name ; /* must be
first to build the hash */
3 ngx_http_set_variable_pt set_handler ;
4 ngx_http_get_variable_pt get_handler ;
5 uintptr_t data ;
6 ngx_uint_t flags ;
7 ngx_uint_t index ;
8 };
upstrem
前面我们已经看到http模块的基本的调用规则,upstream的高性能作为nginx的最突出闪光点,我们需要详细的讲解一下,也可以为以后根据自己的需求修改代码。upstream的调用关系如下伪代码:
//请求的调用入口,同普通的网页的调用
2 ngx_http_proxy_handler {
3 ngx_http_read_client_request_body {
4 //在 条件下作为指针函数被执行post_handler
5 ngx_http_upstream_init {
6 }
7
8 ngx_http_do_read_client_request_body {
9 }
10 }
11 }
3.7.http模块 Chapter3.深入源码
Chapter 4
模块编写
4.
1http模块编写
4.
2基于nginx的高性能服务器开发
4.2.基于nginx的高性能服务器开发 Chapter4.模块编写
Chapter 5附录
5.1编译器参数 volatile
这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。使用该关键字的例子如下:
int volatile nVint;
当要求使用volatile声明的变量的值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。而且读取的数据立刻被保存。例如:
1 volatile int i=10; int a = i; … //其他代码,并未明确告诉编译器,对进行过操作i
2 int b = i;
volatile指出i是随时可能发生变化的,每次使用它的时候必须从i的地址中读取,因而编译器生成的汇编代码会重新从i的地址读取数据放在b中。而优 化做法是,由于编译器发现两次从i读数据的代码之间的代码没有对i进行过操作,它会自动把上次读的数据放在b中。而不是重新从i里面读。这样以来,如果i 是一个寄存器变量或者表示一个端口数据就容易出错,所以说volatile可以保证对特殊地址的稳定访问。注意,在vc6中,一般调试模式没有进行代码优 化,所以这个关键字的作用看不出来。下面通过插入汇编代码,测试有无volatile关键字,对程序最终代码的影响:首先,用classwizard建一 个win32console工程,插入一个voltest.cpp文件,输入下面的代码:
1 # include
2
3 void main () {
4 int i =10;
5 int a = i;
6 printf (”i =% d” ,a); //下面汇编语句的作用就是改变内存中的值,但
是又不让编译器知道i
7 __asm {
5.1.编译器参数 Chapter5.附录
8 mov dword ptr [ ebp .4], 20 h
9 }
10 int b = i;
11 printf (”i =%d”,b);
12 }
然后,在调试版本模式运行程序,输出结果如下: i = 10 i = 32然后,在release版本模式运行程序,输出结果如下: i = 10 i = 10输出的结果明显表明,release模式下,编译器对代码进行了优化,第二次
没有输出正确的i值。下面,我们把i的声明加上volatile关键字,看看有什么变化:
1 # include < stdio .h >
2 void main () {
3 volatile int i =10;
4 int a = i;
5 printf (”i =%d”,a);
6 __asm {
7 mov dword ptr [ebp .4], 20 h
8 }
9 int b = i;
10 printf (”i =%d”,b);
11 }
分别在调试版本和release版本运行程序,输出都是:
i = 10
i = 32
这说明这个关键字发挥了它的作用!volatile对应的变量可能在你的程序本身不知道的情况下发生改变,比如多线程的程序,共同访问的内存当中,多个程 序都可以操纵这个变量你自己的程序,是无法判定合适这个变量会发生变化。还比如,他和一个外部设备的某个状态对应,当外部设备发生操作的时候,通过驱动程 序和中断事件,系统改变了这个变量的数值,而你的程序并不知道。对于volatile类型的变量,系统每次用到他的时候都是直接从对应的内存当中提取,而 不会利用cache当中的原有数值,以适应它的未知何时会发生的变化,系统对这种变量的处理不会做优化——显然也是因为它的数值随时都可能变化的情况。
典型的例子 for ( int i=0; i<100000; i++);这个语句用来测试空循环的速度的但是编译器肯定要把它优化掉,根本就不执行如果你写成 for (volatile int i=0; i<100000; i++);它就会执行了volatile的本意是“易变的”由于访问寄存器的速度要快过RAM,所以编译器一般都会作减少存取外部RAM的优化。比如:
1 static int i=0;
Chapter5. 附录 5.2. 系统函数
2 int main ( void ) {
3 …
4 while (1) {
5 if (i)
6 dosomething () ;
7 }
8 }
9 /* Interrupt service routine . */
10 void ISR_2 ( void ) {
11 i =1;
12 }
程序的本意是希望ISR_2中断产生时,在main当中调用dosomething函数,但是,由于编译器判断在main函数里面没有修改过i,因 此可能只执行一次对从i到某寄存器的读操作,然后每次if判断都只使用这个寄存器里面的“i副本”,导致dosomething永远也不会被调用。如果将 将变量加上volatile修饰,则编译器保证对此变量的读写操作都不会被优化(肯定执行)。此例中i也应该如此说明。
一般说来,volatile用在如下的几个地方:
1、中断服务程序中修改的供其它程序检测的变量需要加volatile;
2、多任务环境下各任务间共享的标志应该加volatile;
3、存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能由不同意义;另外,以上这几种情况经常还要同时考虑数据的完整性 (相互关联的几个标志读了一半被打断了重写),在1中可以通过关中断来实现,2中可以禁止任务调度,3中则只能依靠硬件的良好设计了。
5.2系统函数
va_start和va_end
va_start使argp指向第一个可选参数。va_arg返回参数列表中的当前参数并使argp指向参数列表中的下一个参数。va_end把argp指针清为NULL。函数体内可以多次遍历这些参数,但是都必须以va_start开始,并以va_end结尾。
1).演示如何使用参数个数可变的函数,采用ANSI标准形式
1 # include
2 # include < string .h>
3 # include < stdarg .h>
4 /*函数原型声明,至少需要一个确定的参数,注意括号内的省略号*/
5 int demo ( char , … );
6 void main ( void ){
7 demo (” DEMO “, ” This ” , ” is” , “a” , ” demo !” , “”);
8 }
9 /*标准形式的声明方式,括号内的省略号表示可选参数ANSI */
10 int demo ( char msg , … ){
11 /*定义保存函数参数的结构*/
12 va_list argp ;
5.2.系统函数 Chapter5.附录
13 int argno = 0;
14
char para; /*指向传入的第一个可选参数,是最后一个确定的参数argpmsg*/
15 16 va_start(argp, msg ); 17 while (1) { 18 para = va_arg( argp, char ); 19 if ( strcmp( para, “”) == 0 )
20
break ; 21 printf(” Parameter #%d is: %s\n” , argno, para);
22
argno++;
23 }
24 va_end( argp );
25
/*将置为argpNULL*/ 26 return 0;
27 }
2)//示例代码1:可变参数函数的使用
1 # include ” stdio .h”
2 # include ” stdarg .h”
3 void simple_va_fun ( int start , …) {
4 va_list arg_ptr ;
5 int nArgValue = start ;
6 int nArgCout =0; //可变参数的数目
7 va_start ( arg_ptr , start ); //以固定参数的地址为起点确定变参
的内存起始地址。
8 do {
9 ++ nArgCout ;
10 printf (” the %d th arg : %d\n” , nArgCout , nArgValue );
//输出各参数的
值
11 nArgValue = va_arg ( arg_ptr , int ); //得到下一个可变参
数的值
12 }
13 while ( nArgValue != .1);
14
15 va_end ( arg_ptr );
16 return ;
17 }
18
19 int main ( int argc , char * argv []) {
20 simple_va_fun (100,.1);
21 simple_va_fun (100,200,.1);
22 return 0;
23 }
gethostbyname
使用这个东西,首先要包含2个头文件:
Chapter5.附录 5.2.系统函数
1 # include
2 # include
3
4 struct hostent * gethostbyname ( const char * name );
这个函数的传入值是域名或者主机名,例如”www.google.com”,”wpc”等等。传出值,是一个hostent的结构(如下)。如果函数调用失败,将返回NULL。
1 struct hostent { 2 char *h_name; 3 char ** h_aliases ; 4 int h_addrtype; 5 int h_length; 6 char ** h_addr_list ; 7 };
解释一下这个结构:其中,char*h_name表示的是主机的规范名。例如www.google.com的规范名其实是 www.l.google.com。char**h_aliases表示的是主机的别名。www.google.com就是google他自己的别名。有 的时候,有的主机可能有好几个别名,这些,其实都是为了易于用户记忆而为自己的网站多取的名字。 int h_addrtype表示的是主机ip地址的类型,到底是ipv4(AF_INET),还是ipv6(AF_INET6) int h_length表示的是主机ip地址的长度int **h_addr_lisst表示的是主机的ip地址,注意,这个是以网络字节序存储的。千万不要直接用printf带%s参数来打这个东西,会有问题的 哇。所以到真正需要打印出这个IP的话,需要调用inet_ntop()。
1 const char *inet_ntop( int af , const void *src , char *dst , socklen_t cnt);
这个函数,是将类型为af的网络地址结构src,转换成主机序的字符串形式,存放在长度为cnt的字符串中。这个函数,其实就是返回指向dst的一个指针。如果函数调用错误,返回值是NULL。下面是例程,有详细的注释。
1 # include
2 # include
3 int main ( int argc , char ** argv ) {
4 char * ptr ,** pptr ;
5 struct hostent * hptr ;
6 char str [32];
7
8 /* 取得命令后第一个参数,即要解析的域名或主机名 */
9 ptr = argv [1];
10
11 /* 调用gethostbyname ()。调用结果都存在中hptr */
12 if( ( hptr = gethostbyname ( ptr ) ) == NULL ) {
13 printf (” gethostbyname error for host :% s\n” , ptr );
14 return 0;
15 /* 如果调用发生错误,返回gethostbyname1 */
16 }
5.2. 系统函数 Chapter5. 附录
17
18 /* 将主机的规范名打出来 */
19 printf (” official hostname :%s\n” ,hptr .>h_name );
20
21 /* 主机可能有多个别名,将所有别名分别打出来 */
22 for ( pptr = hptr .>h_aliases ; * pptr != NULL ; pptr ++)
23 printf (” alias : %s\n” ,* pptr );
24
25 /* 根据地址类型,将地址打出来 */
26 switch (hptr.>h_addrtype ) {
27 case AF_INET :
28 case AF_INET6 :
29 pptr =hptr.>h_addr_list ;
30 /* 将刚才得到的所有地址都打出来。其中调用
了inet_ntop ()函数 */
31 for (;* pptr != NULL ; pptr ++)
32 printf (” address :% s\n”, inet_ntop ( hptr.>
h_addrtype , * pptr , str , sizeof ( str )));
33 break;缺省值
34 :
35 printf (” unknown address type \n”);
36 break ;
37 }
38 return 0;
39 }
execve
相关函数fork,execl,execle,execlp,execv,execvp表头文件 #include定义函数 int execve(const char * filename,char * const argv[ ],char
* const envp[ ]);
函数说明execve()用来执行参数filename字符串所代表的文件路径,第二个参数系利用数组指针来传递给执行文件,最后一个参数则为传递给执行文件的新环境变量数组。
返回值如果执行成功则函数不会返回,执行失败则直接返回-1,失败原因存于errno中。错误代码 EACCES
1.
欲执行的文件不具有用户可执行的权限。
2.
欲执行的文件所属的文件系统是以noexec方式挂上。
3.
欲执行的文件或script翻译器非一般文件。
EPERM
1.
进程处于被追踪模式,执行者并不具有root权限,欲执行的文件具有SUID或SGID位。
2.
欲执行的文件所属的文件系统是以nosuid方式挂上,欲执行的文件具有SUID或SGID位元,但执行者并不具有root权限。
Chapter5.附录 5.2.系统函数
E2BIG参数数组过大ENOEXEC无法判断欲执行文件的执行文件格式,有可能是格式错误或无法
在此平台执行。EFAULT参数filename所指的字符串地址超出可存取空间范围。ENAMETOOLONG参数filename所指的字符串太长。 ENOENT参数filename字符串所指定的文件不存在。ENOMEM核心内存不足ENOTDIR参数filename字符串所包含的目录路径并非有 效目录EACCES参数filename字符串所包含的目录路径无法存取,权限不足ELOOP过多的符号连接ETXTBUSY欲执行的文件已被其他进程打 开而且正把数据写入该文件中EIO I/O存取错误ENFILE已达到系统所允许的打开文件总数。EMFILE已达到系统所允许单一进程所能打开的文件总数。EINVAL欲执行文件的ELF 执行格式不只一个PT_INTERP节区EISDIRELF翻译器为一目录ELIBBADELF翻译器有问题。范例
1 #include
2
3 main() {
4 char * argv“[]={”ls”,.”al”,/etc/”passwd ,( char *) 0};
5 char * envp“[]={PATH =/”bin,0};
6
execve“(/ bin /”ls ,argv , envp); 7}
执行 -rw-r–r–1 root root 705 Sep 3 13 :52 /etc/passwd
memalign
在GNU系统中,malloc或realloc返回的内存块地址都是8的倍数(如果是64位系统,则为16的倍数)。如果你需要更大的粒度,请使用 memalign或valloc。这些函数在头文件“stdlib.h”中声明。在GNU库中,可以使用函数free释放memalign和valloc 返回的内存块。但无法在BSD系统中使用,而且BSD系统中并未提供释放这样的内存块的途径。
函数:
1 void * memalign (size_t boundary, size_t size)
函数memalign将分配一个由size指定大小,地址是boundary的倍数的内存块。参数boundary必须是2的幂!函数memalign可以分配较大的内存块,并且可以为返回的地址指定粒度。
5.2.系统函数 Chapter5.附录
函数:
1 void * valloc (size_t size)
使用函数valloc与使用函数memalign类似,函数valloc的内部实现里,使用页的大小作为对齐长度,使用memalign来分配内存。它的实现如下所示:
1 void * valloc ( size_t size ) {
2 return memalign ( getpagesize () , size );
3 }
from: smth