Nginx学习笔记4 源码分析
Nginx学习笔记(四) 源码分析
源码分析
在茫茫的源码中,看到了几个好像挺熟悉的名字(socket/UDP/shmem)。那就来看看这个文件吧!从简单的开始~~~
src/os/unix/Ngx_socket.h&Ngx_socket.c
源码如下(可用Source Insight来看源码,不错的选择):

/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/ #ifndef _NGX_SOCKET_H_INCLUDED_
#define _NGX_SOCKET_H_INCLUDED_ #include <ngx_config.h> #define NGX_WRITE_SHUTDOWN SHUT_WR typedef int ngx_socket_t; #define ngx_socket socket
#define ngx_socket_n "socket()" #if (NGX_HAVE_FIONBIO) int ngx_nonblocking(ngx_socket_t s);
int ngx_blocking(ngx_socket_t s); #define ngx_nonblocking_n "ioctl(FIONBIO)"
#define ngx_blocking_n "ioctl(!FIONBIO)" #else #define ngx_nonblocking(s) fcntl(s, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK)
#define ngx_nonblocking_n "fcntl(O_NONBLOCK)" #define ngx_blocking(s) fcntl(s, F_SETFL, fcntl(s, F_GETFL) & ~O_NONBLOCK)
#define ngx_blocking_n "fcntl(!O_NONBLOCK)" #endif int ngx_tcp_nopush(ngx_socket_t s);
int ngx_tcp_push(ngx_socket_t s); #if (NGX_LINUX) #define ngx_tcp_nopush_n "setsockopt(TCP_CORK)"
#define ngx_tcp_push_n "setsockopt(!TCP_CORK)" #else #define ngx_tcp_nopush_n "setsockopt(TCP_NOPUSH)"
#define ngx_tcp_push_n "setsockopt(!TCP_NOPUSH)" #endif #define ngx_shutdown_socket shutdown
#define ngx_shutdown_socket_n "shutdown()" #define ngx_close_socket close
#define ngx_close_socket_n "close() socket" #endif /* _NGX_SOCKET_H_INCLUDED_ */

其中,创建socket的相关函数如下:
typedef int ngx_socket_t;//这个应该是套接口的描述符了 #define ngx_socket socket //只是替换了名称,仍然是socket,没什么可怕的
#define ngx_socket_n "socket()"
接下来:

#if (NGX_HAVE_FIONBIO) //FIONBIO:设置/ 清除非阻塞I/O 标志式
//使用ioctl来设置
int ngx_nonblocking(ngx_socket_t s);
int ngx_blocking(ngx_socket_t s); #define ngx_nonblocking_n "ioctl(FIONBIO)"
#define ngx_blocking_n "ioctl(!FIONBIO)" #else
//使用fcntl(点击查看详情)设定非阻塞状态
#define ngx_nonblocking(s) fcntl(s, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK)//fcntl(s,F_GETEL)获取文件锁定状态
#define ngx_nonblocking_n "fcntl(O_NONBLOCK)" #define ngx_blocking(s) fcntl(s, F_SETFL, fcntl(s, F_GETFL) & ~O_NONBLOCK)
#define ngx_blocking_n "fcntl(!O_NONBLOCK)" #endif

然后看看Ngnix自己定义的阻塞与非阻塞函数:

int ngx_nonblocking(ngx_socket_t s)
{
int nb;
nb = 1;
return ioctl(s, FIONBIO, &nb);//FIONBIO:设置/清除非阻塞I/O 标志
} int ngx_blocking(ngx_socket_t s)
{
int nb;
nb = 0;
return ioctl(s, FIONBIO, &nb);
}

ioctl:提供对连接到fd的设备驱动程序的属性和操作的访问。
而fcntl:用来设置和修改描述符的的属性。
两种方法都可以用来设置socket阻塞与非阻塞模式。
再接着扫代码~:

int ngx_tcp_nopush(ngx_socket_t s);
//setsockopt(s, IPPROTO_TCP, TCP_NOPUSH,(const void *) &tcp_nopush, sizeof(int));---->FREEBSD
//setsockopt(s, IPPROTO_TCP, TCP_CORK,(const void *) &cork, sizeof(int)); ----->linux
int ngx_tcp_push(ngx_socket_t s);
//setsockopt(s, IPPROTO_TCP, TCP_NOPUSH,(const void *) &tcp_nopush, sizeof(int));----->FREEBSD
//setsockopt(s, IPPROTO_TCP, TCP_CORK,(const void *) &cork, sizeof(int)); ----->linux

这个指令指定是否使用socket的TCP_NOPUSH(FreeBSD)或TCP_CORK(linux)选项,这个选项只在使用sendfile时有效。设置这个选项的将导致nginx试图将它的HTTP应答头封装到一个包中。
所谓的cork就是塞子的意思,形象地理解就是用cork将连接塞住,使得数据先不发出去,等到拔去塞子后再发出去,而nodelay事实上是为了禁用Nagle算法,Nagle算法为了增加了网络的吞吐量而牺牲了响应时间体验。
在进行大量数据发送的时候可以置位TCP_CORK关闭Nagle算法保证网络利用性,尽可能的进行数据的组包,以最大mtu传输。
src/os/unix/Ngx_dup_recv.c

/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/ #include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h> #if (NGX_HAVE_KQUEUE) ssize_t
ngx_udp_unix_recv(ngx_connection_t *c, u_char *buf, size_t size)
{
ssize_t n;
ngx_err_t err;
ngx_event_t *rev; rev = c->read; do {
n = recv(c->fd, buf, size, 0); ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
"recv: fd:%d %d of %d", c->fd, n, size); if (n >= 0) {
if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
rev->available -= n; /*
* rev->available may be negative here because some additional
* bytes may be received between kevent() and recv()
*/ if (rev->available <= 0) {
rev->ready = 0;
rev->available = 0;
}
} return n;
} err = ngx_socket_errno; if (err == NGX_EAGAIN || err == NGX_EINTR) {
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
"recv() not ready");
n = NGX_AGAIN; } else {
n = ngx_connection_error(c, err, "recv() failed");
break;
} } while (err == NGX_EINTR); rev->ready = 0; if (n == NGX_ERROR) {
rev->error = 1;
} return n;
} #else /* ! NGX_HAVE_KQUEUE */ ssize_t
ngx_udp_unix_recv(ngx_connection_t *c, u_char *buf, size_t size)
{
ssize_t n;
ngx_err_t err;
ngx_event_t *rev; rev = c->read; do {
n = recv(c->fd, buf, size, 0); ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
"recv: fd:%d %d of %d", c->fd, n, size); if (n >= 0) {
return n;
} err = ngx_socket_errno; if (err == NGX_EAGAIN || err == NGX_EINTR) {
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
"recv() not ready");
n = NGX_AGAIN; } else {
n = ngx_connection_error(c, err, "recv() failed");
break;
} } while (err == NGX_EINTR); rev->ready = 0; if (n == NGX_ERROR) {
rev->error = 1;
} return n;
} #endif /* NGX_HAVE_KQUEUE */

开始就看到了这个NGX_HAVE_KQUEUE~~google一下得到下面的内容~
kqueue(freebsd)与epoll(linux 2.6)两个东西极其相似,写好了一个之后,移到别外一个平台下,只要稍作修改就可以了,原理是一样,个人认为,从功能角度来盾kqueue比epoll灵活得多。在写kqueue的时候,内核帮你考虑好了不少东西。但是从效率来看,从我作的压力测试来看epoll比kqueue强。
那么,就可以直接越过freebsd,直接看linux下的了,都很好理解。

ssize_t ngx_udp_unix_recv(ngx_connection_t *c, u_char *buf, size_t size)
{
ssize_t n;
ngx_err_t err;
ngx_event_t *rev;
rev = c->read;
do {
n = recv(c->fd, buf, size, 0); //简单的recv()函数
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
"recv: fd:%d %d of %d", c->fd, n, size); //添加日志
if (n >= 0) { //接收数据成功,返回接收数据的大小
return n;
}
err = ngx_socket_errno; //errno if (err == NGX_EAGAIN || err == NGX_EINTR) {
//EAGAIN重试 EINTR由于信号中断,没读到任何数据
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
"recv() not ready");
n = NGX_AGAIN;
} else {
n = ngx_connection_error(c, err, "recv() failed");
break;
} } while (err == NGX_EINTR); //EINTR由于信号中断,没读到任何数据
rev->ready = 0;
if (n == NGX_ERROR) {
rev->error = 1;
}
return n;
}

src/os/unix/shmem.c&shmem.h
从名称shmem就能看出来share memory共享内存。
其中,有个结构体ngx_shm_t:

typedef struct {
u_char *addr; //共享内存的地址
size_t size; //大小
ngx_str_t name; //名称
ngx_log_t *log;
ngx_uint_t exists; /* unsigned exists:1; */
} ngx_shm_t;

和两个相关的操作函数(分配/释放共享内存----直接看Linux部分):

ngx_int_t ngx_shm_alloc(ngx_shm_t *shm)
{
int id;
id = shmget(IPC_PRIVATE, shm->size, (SHM_R|SHM_W|IPC_CREAT));//使用linux下的shmget来创建一个共享内存对象 ...log... shm->addr = shmat(id, NULL, 0); //linux下把共享内存区对象映射到调用进程的地址空间 if (shm->addr == (void *) -1) { //失败的话
ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno, "shmat() failed");
} if (shmctl(id, IPC_RMID, NULL) == -1) { //linux下完成对共享内存的控制 IPC_RMID:删除这片共享内存
ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
"shmctl(IPC_RMID) failed");
} return (shm->addr == (void *) -1) ? NGX_ERROR : NGX_OK;
} void
ngx_shm_free(ngx_shm_t *shm)
{
if (shmdt(shm->addr) == -1) { //与shmat函数相反,是用来断开与共享内存附加点的地址,禁止本进程访问此片共享内存
ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
"shmdt(%p) failed", shm->addr);
}
}

参考
http://www.ithov.com/linux/109313_5.shtml
http://machael.blog.51cto.com/829462/479941
http://blog.csdn.net/shellching/article/details/5592511
Nginx学习笔记4 源码分析的更多相关文章
- Hadoop学习笔记(9) ——源码初窥
Hadoop学习笔记(9) ——源码初窥 之前我们把Hadoop算是入了门,下载的源码,写了HelloWorld,简要分析了其编程要点,然后也编了个较复杂的示例.接下来其实就有两条路可走了,一条是继续 ...
- [转]OpenTK学习笔记(1)-源码、官网地址
OpenTK源码下载地址:https://github.com/opentk/opentk OpenTK使用Nuget安装命令:OpenTK:Install-Package OpenTK -Versi ...
- MQTT再学习 -- MQTT 客户端源码分析
MQTT 源码分析,搜索了一下发现网络上讲的很少,多是逍遥子的那几篇. 参看:逍遥子_mosquitto源码分析系列 参看:MQTT libmosquitto源码分析 参看:Mosquitto学习笔记 ...
- nginx健康检查模块源码分析
nginx健康检查模块 本文所说的nginx健康检查模块是指nginx_upstream_check_module模块.nginx_upstream_check_module模块是Taobao定制的用 ...
- Redis学习——ae事件处理源码分析
0. 前言 Redis在封装事件的处理采用了Reactor模式,添加了定时事件的处理.Redis处理事件是单进程单线程的,而经典Reator模式对事件是串行处理的.即如果有一个事件阻塞过久的话会导致整 ...
- Java多线程学习之ThreadLocal源码分析
0.概述 ThreadLocal,即线程本地变量,是一个以ThreadLocal对象为键.任意对象为值的存储结构.它可以将变量绑定到特定的线程上,使每个线程都拥有改变量的一个拷贝,各线程相同变量间互不 ...
- springMVC源码学习之addFlashAttribute源码分析
本文主要从falshMap初始化,存,取,消毁来进行源码分析,springmvc版本4.3.18.关于使用及验证请参考另一篇jsp取addFlashAttribute值深入理解即springMVC发r ...
- 大数据学习--day14(String--StringBuffer--StringBuilder 源码分析、性能比较)
String--StringBuffer--StringBuilder 源码分析.性能比较 站在优秀博客的肩上看问题:https://www.cnblogs.com/dolphin0520/p/377 ...
- Netty学习篇⑥--ByteBuf源码分析
什么是ByteBuf? ByteBuf在Netty中充当着非常重要的角色:它是在数据传输中负责装载字节数据的一个容器;其内部结构和数组类似,初始化默认长度为256,默认最大长度为Integer.MAX ...
随机推荐
- <<Python基础课程>>学习笔记 | 文章13章 | 数据库支持
备注:本章介绍了比较简单,只是比较使用样品,主要假设是把握连接,利用数据库.和SQLite做演示样本 ------ Python数据库API 为了解决Python中各种数据库模块间的兼容问题,如今已经 ...
- Excel基于POI导入导出的Annotation化之路(一)
Excel在web项目里的使用变得越来越广泛,特别是和线下耦合度较高的业务,Excel导入导出变得非常频繁,尽管很多人写了诸多的工具方法,但是终究没有解决一个问题:有效的控制字段英文名称和实际表头名称 ...
- 关于Java中List对象的分页思想,按10个或者n个数对list进行分组
try { List<String> timelist = DateUtils.getDateListBySETime("2015-08-01", "2015 ...
- PHP jpgraph的一点小提示(附安装方法)
PHP中的GD库本身是一套很强大的绘图库了,绘制的图像基本可以满足日常要求,但强大规强大,还是不够方便哈,因为强大方便的基于PHP的GD库的jpgraph也就诞生啦! PHP默认是不启用GD库的,因为 ...
- June本地环境搭建
python-china.org论坛使用的June程序就是这货了,使用了Python Flask + SQLite + Node.js 的轻论坛,以后就打算拿这个学习了,如果可能,进行二次开发. Gi ...
- 如何使用C API来操作UCI
https://forum.openwrt.org/viewtopic.php?pid=183335#p183335 Compiling UCI as stand alone with an exam ...
- 浏览器检测(BrowserDetect.js)使用
浏览器检测是在工作中经常用到的,如果只是简单判断当前是什么浏览器的话可以通过window.navigator.useragent这样的js来直接判断就可以了! 但是针对浏览器版本要求比较高的时候,如果 ...
- Win7下Redmine2.0.3+Mysql55+Ruby1.8.7成功安装记录分享
准备软件: Ruby 下载网页: http://rubyforge.org/frs/?group_id=167&release_id=46836 http://files.rubyforge. ...
- python chanllenge题解
网址:chanllenge 修改url最后的html的前缀为答案,就可以过关. 页面上很多只有一幅图片,实际上题目描述全在页面源码中. 然后推荐一个在线代码运行的网站 ideone 查看所有源码:ht ...
- C++ builder 中AnsiString的字符串转换方法大全
C++ builder 中AnsiString的字符串转换方法大全 //Ansistring 转 charvoid __fastcall TForm1::Button1Click(TObject *S ...