博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

nginx 大概分析

Posted on 2016-02-03 17:59  bw_0927  阅读(491)  评论(0)    收藏  举报

http://techlog.cn/article/list/10182612

http://blog.csdn.net/livelylittlefish/article/category/838107

http://blog.csdn.net/lengzijian/article/category/1098804

http://blog.csdn.net/fjs_cloud/article/category/1268117

http://blog.sunheqiubai.com/?cat=5&paged=2

http://blog.csdn.net/dingyujie/article/category/782920

http://blog.csdn.net/fjslovejhl/article/details/8134008

http://blog.csdn.net/u012062760/article/details/48732491

http://www.pagefault.info/?cat=7

http://blog.csdn.net/brainkick/article/details/8583335

http://aosabook.org/en/nginx.html

http://blog.csdn.net/chosen0ne/article/category/915324

http://www.lenky.info/archives/category/%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/nginx

 

./configure --prefix=/home/services/nginx --with-debug --with-cc-opt='-g -O0 -ggdb3'

 

 

pread, pwrite - read from or write to a file descriptor at a given offset

 

   nginx多进程模型的入口函数为主进程的 ngx_master_process_cycle(), 在该函数内做完信号处理,设置等之后就会调用 ngx_start_worker_processes(),用于fork()创建子进程(子进程的数目通过ccf->worker_processes指定),子进程作为一个新的实体,开始充当工作者的角色运行ngx_worker_process_cycle()函数, 该函数实体为一个无限的for循环,持续不断的处理客户端的服务请求,而主进程继续执行ngx_master_process_cycle(),也就是监控子进程的for循环,主进程也是一个无限for循环,直到进程退出,才会退出循环.

 

监控进程的无限for循环内的有一个关键的函数sigsuspend()函数调用,该函数的调用使得监控进程的大部分时间处于挂起等待状态,直到监控进程接收到信号为止

 

ngx_reap(有子进程退出)

 

getsockname()   返回fd对应的socket_addr,检查对应的fd是否健康,是否可用

ngx_sock_ntop()    sockaddr[]转换成可读的字符串形式

getsockopt(fd, IPPROTO_TCP, TCP_FASTOPEN)      SYN携带应用数据

getsockopt(fd, IPPROTO_TCP, TCP_DEFER_ACCEPT)   在 accept 的 socket 上面,只有当实际收到了数据,才唤醒正在 accept 的进程,可以减少一些无聊的上下文切换

http://blog.csdn.net/fullsail/article/details/4429102

经过测试发现,设置TCP_DEFER_ACCEPT选项后,服务器受到一个CONNECT请求后,操作系统不会Accept,也不会创建IO句柄。如果Connect后面马上有后续的发送数据,那么服务器会调用Accept接收这个链接端口。

若操作系统在若干秒后还没收到数据,会释放相关的链接。但没有同时关闭相应的端口,所以客户端会一直以为处于链接状态。

这个设置对于CONNECT链接上来而又什么都不干的攻击方式处理很有效。我们原来的代码都是先允许链接,然后再进行超时处理,比他这个有点Out了。不过这个选项可能会导致定位某些问题麻烦。 

 

 

TIME_WAIT: 在recv函数返回0后,没有close句柄

SO_LINGER这个选项,发现这个选项可以避免端口的状态进入TIME_WAIT状态

TIME_WAIT的作用是保证在主动关闭端口后,保证数据让对端收到,Richard.Steven的原话是”TIME_WAIT是我们的朋友。

不能丢弃TIME_WAIT状态,但是让他缩短一些也是可以的。我们可以在l_linger参数上动点手脚,当l_onoff非0是。l_linge也非0的时候,套接口在TIME_WAIT上将拖延一段时间,l_linger秒,而不再是2MSL的超时[注]。

结论:TIME_WAIT不能关,也不能使用默认的2MSL。

 

posix_memalign()    数据对齐

 

struct utsname  u;

uname(u);

 

#include <unistd.h>

extern char **environ;

 

typedef AO_t                        ngx_atomic_uint_t;    原子操作,原子操作实现的互斥锁

typedef volatile ngx_atomic_uint_t  ngx_atomic_t;

 

ngx_memory_barrier()------->__sync_synchronize();  

memory barrier有几种类型:
    acquire barrier : 不允许将barrier之后的内存读取指令移到barrier之前(linux kernel中的wmb())。
    release barrier : 不允许将barrier之前的内存读取指令移到barrier之后 (linux kernel中的rmb())。
    full barrier    : 以上两种barrier的合集(linux kernel中的mb())。

 

 ngx_ncpu = sysconf(_SC_NPROCESSORS_ONLN);    CPU个数

ngx_cpuinfo()   

getrlimit(RLIMIT_NOFILE, &rlmt)

 

ngx_timezone_update()-->

 

ngx_init_cycle()--> module->create_conf(cycle)--->cycle->conf_ctx[ngx_modules[i]->index] = rv---->ngx_conf_parse()--->module->init_conf()--->ngx_test_lockfile()

--->ngx_shm_alloc()--->ngx_open_listening_sockets()--->ngx_configure_listening_sockets()--->ngx_log_redirect_stderr()--->ngx_modules[i]->init_module(cycle)

 

ngx_signal_process()

 

ngx_signal_t  signals[]

 

ngx_daemon()

 

ngx_

监控进程:sigsuspend(), ngx_signal_handler(), 信号处理函数一般都要足够简单,设置一些标志位就够了,实际的标志位逻辑处理必须放在主体代码里执行

ngx_reap  :有子进程退出

 

cache管理进程和cache加载进程的主流程都是ngx_cache_manager_process_cycle()函数,只是附带的参数不同

cache进程不处理客户端请求,也就没有监控的IO事件,而其处理的是超时事件,ngx_process_events_and_timers()里面只有ngx_event_expire_timers()

cache管理进程不接受客户端请求,调用ngx_close_listening_sockets()关闭了监听套接口。

总的来说,cache管理进程的任务就是循环执行清理超时缓存文件,限制缓存文件总大小,直到nginx退出。

cache加载进程的功能是在nginx正常启动后(具体是60秒)将磁盘中上次缓存的对象加载到内存中。这个过程是一次性的,所以当cache加载进程完成它的加载任务后也就自动退出了。

 

父子进程间的通信:socketpair()

子进程间的通信: 父进程fork()生成一个新子进程后,就会立即通过ngx_pass_open_channel()函数把这个子进程的相关信息告知给其前面已生成的子进程

 

事件处理机制的指定:use 命令:ngx_event_use()-->设置ecf->use字段

ngx_worker_process_init()-->ngx_event_process_init()-->ecf->use--->ngx_epoll_init()

 

负载均衡: 连接均匀地分布在多个不同的worker上

一旦变量ngx_use_accept_mutex值为1,也就开启了nginx负载均衡策略,此时在每个工作进程的初始化函数ngx_event_process_init()内,所有监听套接口都不会被加入到其事件监控机制里。

真正将监听套接口加入到事件监控机制是在函数ngx_process_events_and_timers()里(通过锁,各个worker同步,防止惊群)。

ngx_accept_disabled = x - ngx_cycle->connection_n *7/8

指令:worker_connections配置工作进程的最大可承受连接数,ngx_cycle->connection_n

 

NGX_POST_EVENTS 延时处理标志: 任何架构设计都必须遵守的一个约定:持锁者必须尽量缩短自身持锁的时间。

当一个事件发生时,一般处理(即不做延迟)会立即调用事件对应的回调函数,而延迟处理则会将事件以链表的形式缓存起来

监听套接口是以水平方式加入到事件监控机制里的。

 

ps  -F   cpu的绑定情况    PSR

 

每个工作进程都会在自身初始化时建立一颗红黑树用来管理超时事件。

 

 

事件处理机制每次返回都会更新时间,如果IO事件比较多(比如客户端请求非常多),那么将会导致比较频繁地调用gettimeofday()系统函数,这也可以说是超时检测方案2对性能影响的最大缺点。

 

在该函数ngx_spawn_process()进行fork()之前,先调用了socketpair()创建一对socket描述符,存放在变量ngx_processes[s].chanel内,(其中s标志在ngx_processes数组内第一个可用元素的下标,比如最开始产生第一个工作进程时,可用元素的下标s为0),

而在fork() 之后,由于子进程继承了父进程的资源,那么父子进程都拥有一对socket描述符,而nginx将channel[0]给父进程使用, channel[1]给子进程使用,这样分别错开使用不同的描述符,即可实现父子进程之间的双向通信.

 

网络编程中非常常用的一种校验方式就是 crc32,循环冗余码校验

 

 

===

全局数组ngx_modules存放所有的模块,数组的每一个元素均为一个全局ngx_module_t对象的指针

模块结构体ngx_module_s的字段type表示该模块的类型,现有以下类型:

#define NGX_CORE_MODULE      0x45524F43   /* "CORE" */  

#define NGX_CONF_MODULE      0x464E4F43   /* "CONF" */  

#define NGX_EVENT_MODULE      0x544E5645  /* "EVNT" */  

#define NGX_HTTP_MODULE       0x50545448  /* "HTTP" */  

十六进制的数就是其类型对应的ASCII

 

设置非阻塞: ioctl()  或者 fcntl()

* ioctl(FIONBIO) sets a non-blocking mode with the single syscall
* while fcntl(F_SETFL, O_NONBLOCK) needs to learn the current state using fcntl(F_GETFL).

ioctl - control device ioctl() performs a variety of control functions  on  devices and STREAMS. 
The fcntl() function provides control of open file descriptors. It is similar to ioctl().

ioctl(FIOASYNC)

fcntl():        F_SETOWN    FD_CLOEXEC

 

setpriority() 

setrlimit()

cpuset_setaffinity()

prctl(PR_SET_DUMPABLE)