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

http://www.oschina.net/question/234345_47602

http://baike.baidu.com.cn/view/3170880.htm

 
 
 

Nginx学习笔记4 源码分析的更多相关文章

  1. Hadoop学习笔记(9) ——源码初窥

    Hadoop学习笔记(9) ——源码初窥 之前我们把Hadoop算是入了门,下载的源码,写了HelloWorld,简要分析了其编程要点,然后也编了个较复杂的示例.接下来其实就有两条路可走了,一条是继续 ...

  2. [转]OpenTK学习笔记(1)-源码、官网地址

    OpenTK源码下载地址:https://github.com/opentk/opentk OpenTK使用Nuget安装命令:OpenTK:Install-Package OpenTK -Versi ...

  3. MQTT再学习 -- MQTT 客户端源码分析

    MQTT 源码分析,搜索了一下发现网络上讲的很少,多是逍遥子的那几篇. 参看:逍遥子_mosquitto源码分析系列 参看:MQTT libmosquitto源码分析 参看:Mosquitto学习笔记 ...

  4. nginx健康检查模块源码分析

    nginx健康检查模块 本文所说的nginx健康检查模块是指nginx_upstream_check_module模块.nginx_upstream_check_module模块是Taobao定制的用 ...

  5. Redis学习——ae事件处理源码分析

    0. 前言 Redis在封装事件的处理采用了Reactor模式,添加了定时事件的处理.Redis处理事件是单进程单线程的,而经典Reator模式对事件是串行处理的.即如果有一个事件阻塞过久的话会导致整 ...

  6. Java多线程学习之ThreadLocal源码分析

    0.概述 ThreadLocal,即线程本地变量,是一个以ThreadLocal对象为键.任意对象为值的存储结构.它可以将变量绑定到特定的线程上,使每个线程都拥有改变量的一个拷贝,各线程相同变量间互不 ...

  7. springMVC源码学习之addFlashAttribute源码分析

    本文主要从falshMap初始化,存,取,消毁来进行源码分析,springmvc版本4.3.18.关于使用及验证请参考另一篇jsp取addFlashAttribute值深入理解即springMVC发r ...

  8. 大数据学习--day14(String--StringBuffer--StringBuilder 源码分析、性能比较)

    String--StringBuffer--StringBuilder 源码分析.性能比较 站在优秀博客的肩上看问题:https://www.cnblogs.com/dolphin0520/p/377 ...

  9. Netty学习篇⑥--ByteBuf源码分析

    什么是ByteBuf? ByteBuf在Netty中充当着非常重要的角色:它是在数据传输中负责装载字节数据的一个容器;其内部结构和数组类似,初始化默认长度为256,默认最大长度为Integer.MAX ...

随机推荐

  1. Windows Phone 8.1 多媒体(1):相片

    原文:Windows Phone 8.1 多媒体(1):相片 Windows Phone 8.1 多媒体(1):相片 Windows Phone 8.1 多媒体(2):视频 Windows Phone ...

  2. angularJS之使用过滤器转化输出 (angularJS系列最后一篇)

    在视图模板中使用过滤器 过滤器也是一种服务,负责对输入的内容进行处理转换,以便更好地向用户显示. 过滤器可以在模板中的{{}}标记中使用: {{ expression | filter:arg1:ar ...

  3. Java设计模式论述

    为何须要设计模式: 模式是做事的方法,是实现目标,研磨技术的方法.这样的对高效技术不懈追求的思想,广泛见于诸多领域,比如制作精美佳肴的过程.对于不论什么一种迈向成熟的全新技艺,身处这个行业的人都须要寻 ...

  4. Android-Launcher开发之ShortCut(1)

    下面源代码来自Launcher2.3的样例 1.默认每一个应用的主Activity都会自带 <category android:name="android.intent.categor ...

  5. expected number,sequence,or string.map evaluated instead of freemarker.template.smplehash

    expected number,sequence,or string.map evaluated instead of freemarker.template.smplehash 使用freemark ...

  6. 大endian和little endian

    大endian和little endian      一般Intel处理器或X86平台是小端 ,只是有点老了摩托罗拉的处理器将采用大端,掌握一下小端序.     小端序一般指低地址存低字节.高地址存高 ...

  7. Spring Resource之内置的Resource实现

    Spring提供了大量的并且可以直接使用的Resource实现 1.UrlResource UrlResource封装了一个java.net.URL,而且可以通过一个URL用于访问任何对象,例如文件. ...

  8. ubuntu新内核不能用启动回滚到旧内核的方法

    先看一看自己电脑上有哪些内核文件 merlin@tfAnalysis:~$ dpkg --get-selections|grep linux libselinux1:i386 install linu ...

  9. 告别alert,拥抱console

    记得学习javascript的第一个demo就是alert("Hello  World");可是学习接触javascript这么长时间了还是在alert,因为javascript调 ...

  10. 分享UI设计模型

    UI设计模型是可重用的界面设计解决方案,可以让开发人员少走弯路,节约不少开发时间.下面慧都小编跟大家分享6个很有用的UI设计模型资源,希望对你有用: 1.UI Patterns 由一个丹麦人开发的UI ...