Nginx学习笔记(四) 源码分析

源码分析

  在茫茫的源码中,看到了几个好像挺熟悉的名字(socket/UDP/shmem)。那就来看看这个文件吧!从简单的开始~~~

src/os/unix/Ngx_socket.h&Ngx_socket.c

  源码如下(可用Source Insight来看源码,不错的选择):

  1. /*
  2. * Copyright (C) Igor Sysoev
  3. * Copyright (C) Nginx, Inc.
  4. */
  5.  
  6. #ifndef _NGX_SOCKET_H_INCLUDED_
  7. #define _NGX_SOCKET_H_INCLUDED_
  8.  
  9. #include <ngx_config.h>
  10.  
  11. #define NGX_WRITE_SHUTDOWN SHUT_WR
  12.  
  13. typedef int ngx_socket_t;
  14.  
  15. #define ngx_socket socket
  16. #define ngx_socket_n "socket()"
  17.  
  18. #if (NGX_HAVE_FIONBIO)
  19.  
  20. int ngx_nonblocking(ngx_socket_t s);
  21. int ngx_blocking(ngx_socket_t s);
  22.  
  23. #define ngx_nonblocking_n "ioctl(FIONBIO)"
  24. #define ngx_blocking_n "ioctl(!FIONBIO)"
  25.  
  26. #else
  27.  
  28. #define ngx_nonblocking(s) fcntl(s, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK)
  29. #define ngx_nonblocking_n "fcntl(O_NONBLOCK)"
  30.  
  31. #define ngx_blocking(s) fcntl(s, F_SETFL, fcntl(s, F_GETFL) & ~O_NONBLOCK)
  32. #define ngx_blocking_n "fcntl(!O_NONBLOCK)"
  33.  
  34. #endif
  35.  
  36. int ngx_tcp_nopush(ngx_socket_t s);
  37. int ngx_tcp_push(ngx_socket_t s);
  38.  
  39. #if (NGX_LINUX)
  40.  
  41. #define ngx_tcp_nopush_n "setsockopt(TCP_CORK)"
  42. #define ngx_tcp_push_n "setsockopt(!TCP_CORK)"
  43.  
  44. #else
  45.  
  46. #define ngx_tcp_nopush_n "setsockopt(TCP_NOPUSH)"
  47. #define ngx_tcp_push_n "setsockopt(!TCP_NOPUSH)"
  48.  
  49. #endif
  50.  
  51. #define ngx_shutdown_socket shutdown
  52. #define ngx_shutdown_socket_n "shutdown()"
  53.  
  54. #define ngx_close_socket close
  55. #define ngx_close_socket_n "close() socket"
  56.  
  57. #endif /* _NGX_SOCKET_H_INCLUDED_ */

  其中,创建socket的相关函数如下:

  1. typedef int ngx_socket_t;//这个应该是套接口的描述符了
  2.  
  3. #define ngx_socket socket //只是替换了名称,仍然是socket,没什么可怕的
  4. #define ngx_socket_n "socket()"

 接下来:

  1. #if (NGX_HAVE_FIONBIO) //FIONBIO:设置/ 清除非阻塞I/O 标志式
  2. //使用ioctl来设置
  3. int ngx_nonblocking(ngx_socket_t s);
  4. int ngx_blocking(ngx_socket_t s);
  5.  
  6. #define ngx_nonblocking_n "ioctl(FIONBIO)"
  7. #define ngx_blocking_n "ioctl(!FIONBIO)"
  8.  
  9. #else

  10. //使用fcntl(点击查看详情)设定非阻塞状态
  11. #define ngx_nonblocking(s) fcntl(s, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK)//fcntl(s,F_GETEL)获取文件锁定状态
  12. #define ngx_nonblocking_n "fcntl(O_NONBLOCK)"
  13.  
  14. #define ngx_blocking(s) fcntl(s, F_SETFL, fcntl(s, F_GETFL) & ~O_NONBLOCK)
  15. #define ngx_blocking_n "fcntl(!O_NONBLOCK)"
  16.  
  17. #endif

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

  1. int ngx_nonblocking(ngx_socket_t s)
  2. {
  3. int nb;
  4. nb = 1;
  5. return ioctl(s, FIONBIO, &nb);//FIONBIO:设置/清除非阻塞I/O 标志
  6. }
  7.  
  8. int ngx_blocking(ngx_socket_t s)
  9. {
  10. int nb;
  11. nb = 0;
  12. return ioctl(s, FIONBIO, &nb);
  13. }

  ioctl:提供对连接到fd的设备驱动程序的属性和操作的访问。

  而fcntl:用来设置和修改描述符的的属性。

  两种方法都可以用来设置socket阻塞与非阻塞模式。

  再接着扫代码~:

  1. 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
  1. 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

  1. /*
  2. * Copyright (C) Igor Sysoev
  3. * Copyright (C) Nginx, Inc.
  4. */
  5.  
  6. #include <ngx_config.h>
  7. #include <ngx_core.h>
  8. #include <ngx_event.h>
  9.  
  10. #if (NGX_HAVE_KQUEUE)
  11.  
  12. ssize_t
  13. ngx_udp_unix_recv(ngx_connection_t *c, u_char *buf, size_t size)
  14. {
  15. ssize_t n;
  16. ngx_err_t err;
  17. ngx_event_t *rev;
  18.  
  19. rev = c->read;
  20.  
  21. do {
  22. n = recv(c->fd, buf, size, 0);
  23.  
  24. ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
  25. "recv: fd:%d %d of %d", c->fd, n, size);
  26.  
  27. if (n >= 0) {
  28. if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
  29. rev->available -= n;
  30.  
  31. /*
  32. * rev->available may be negative here because some additional
  33. * bytes may be received between kevent() and recv()
  34. */
  35.  
  36. if (rev->available <= 0) {
  37. rev->ready = 0;
  38. rev->available = 0;
  39. }
  40. }
  41.  
  42. return n;
  43. }
  44.  
  45. err = ngx_socket_errno;
  46.  
  47. if (err == NGX_EAGAIN || err == NGX_EINTR) {
  48. ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
  49. "recv() not ready");
  50. n = NGX_AGAIN;
  51.  
  52. } else {
  53. n = ngx_connection_error(c, err, "recv() failed");
  54. break;
  55. }
  56.  
  57. } while (err == NGX_EINTR);
  58.  
  59. rev->ready = 0;
  60.  
  61. if (n == NGX_ERROR) {
  62. rev->error = 1;
  63. }
  64.  
  65. return n;
  66. }
  67.  
  68. #else /* ! NGX_HAVE_KQUEUE */
  69.  
  70. ssize_t
  71. ngx_udp_unix_recv(ngx_connection_t *c, u_char *buf, size_t size)
  72. {
  73. ssize_t n;
  74. ngx_err_t err;
  75. ngx_event_t *rev;
  76.  
  77. rev = c->read;
  78.  
  79. do {
  80. n = recv(c->fd, buf, size, 0);
  81.  
  82. ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
  83. "recv: fd:%d %d of %d", c->fd, n, size);
  84.  
  85. if (n >= 0) {
  86. return n;
  87. }
  88.  
  89. err = ngx_socket_errno;
  90.  
  91. if (err == NGX_EAGAIN || err == NGX_EINTR) {
  92. ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
  93. "recv() not ready");
  94. n = NGX_AGAIN;
  95.  
  96. } else {
  97. n = ngx_connection_error(c, err, "recv() failed");
  98. break;
  99. }
  100.  
  101. } while (err == NGX_EINTR);
  102.  
  103. rev->ready = 0;
  104.  
  105. if (n == NGX_ERROR) {
  106. rev->error = 1;
  107. }
  108.  
  109. return n;
  110. }
  111.  
  112. #endif /* NGX_HAVE_KQUEUE */

  开始就看到了这个NGX_HAVE_KQUEUE~~google一下得到下面的内容~

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

  那么,就可以直接越过freebsd,直接看linux下的了,都很好理解。

  1. ssize_t ngx_udp_unix_recv(ngx_connection_t *c, u_char *buf, size_t size)
  2. {
  3. ssize_t n;
  4. ngx_err_t err;
  5. ngx_event_t *rev;
  6. rev = c->read;
  7. do {
  8. n = recv(c->fd, buf, size, 0); //简单的recv()函数
  9. ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
  10. "recv: fd:%d %d of %d", c->fd, n, size); //添加日志
  11. if (n >= 0) { //接收数据成功,返回接收数据的大小
  12. return n;
  13. }
  14. err = ngx_socket_errno;    //errno
  15.  
  16. if (err == NGX_EAGAIN || err == NGX_EINTR) {
           //EAGAIN重试 EINTR由于信号中断,没读到任何数据
    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
  17. "recv() not ready");
  18. n = NGX_AGAIN;
  19. } else {
  20. n = ngx_connection_error(c, err, "recv() failed");
  21. break;
  22. }
  23.  
  24. } while (err == NGX_EINTR);   //EINTR由于信号中断,没读到任何数据
  25. rev->ready = 0;
  26. if (n == NGX_ERROR) {
  27. rev->error = 1;
  28. }
  29. return n;
  30. }

src/os/unix/shmem.c&shmem.h

  从名称shmem就能看出来share memory共享内存。

 

  其中,有个结构体ngx_shm_t:

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

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

  1. ngx_int_t ngx_shm_alloc(ngx_shm_t *shm)
  2. {
  3. int id;
  4. id = shmget(IPC_PRIVATE, shm->size, (SHM_R|SHM_W|IPC_CREAT));//使用linux下的shmget来创建一个共享内存对象
  5.  
  6. ...log...
  7.  
  8. shm->addr = shmat(id, NULL, 0); //linux下把共享内存区对象映射到调用进程的地址空间
  9.  
  10. if (shm->addr == (void *) -1) { //失败的话
  11. ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno, "shmat() failed");
  12. }
  13.  
  14. if (shmctl(id, IPC_RMID, NULL) == -1) { //linux下完成对共享内存的控制 IPC_RMID:删除这片共享内存
  15. ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
  16. "shmctl(IPC_RMID) failed");
  17. }
  18.  
  19. return (shm->addr == (void *) -1) ? NGX_ERROR : NGX_OK;
  20. }
  21.  
  22. void
  23. ngx_shm_free(ngx_shm_t *shm)
  24. {
  25. if (shmdt(shm->addr) == -1) { //与shmat函数相反,是用来断开与共享内存附加点的地址,禁止本进程访问此片共享内存
  26. ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
  27. "shmdt(%p) failed", shm->addr);
  28. }
  29. }

参考

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. SQL Server 板机

    触发器是一种特殊类型的存储过程.们介绍的存储过程. 触发器主要是通过事件进行触发被自己主动调用运行的. 而存储过程能够通过存储过程的名称被调用. Ø 什么是触发器 触发器对表进行插入.更新.删除的时候 ...

  2. 利用svn自动同步更新到网站服务器 -- 网摘

    首先在服务器上安装VisualSVN Server ,根据提示选好安装的路径,一路确定.安装好后运行VisualSVN Server ,在Repositories上点击右键,选择create New ...

  3. 第3章3节《MonkeyRunner源码剖析》脚本编写示例: MonkeyImage API使用示例(原创)

    天地会珠海分舵注:本来这一系列是准备出一本书的,详情请见早前博文“寻求合作伙伴编写<深入理解 MonkeyRunner>书籍“.但因为诸多原因,没有如愿.所以这里把草稿分享出来,所以错误在 ...

  4. object 插入元素,插入HTML页面

    object标签用于定义一个嵌入的对象,包括:图像.音频.Java applets.ActiveX.PDF以及Flash.该标签允许您规定插入HTML文档中的对象的数据和参数,以及可用来显示和操作数据 ...

  5. Mockito使用注意事项

    已使用mockito有些问题.例如:配合可变长度参数.定义自己的参数匹配,在这些mockito官方文件(http://docs.mockito.googlecode.com/hg/latest/org ...

  6. 再谈ORACLE CPROCD进程

    罗列一下有关oprocd的知识点 oprocd是oracle在rac中引入用来fencing io的 在unix系统下,假设我们没有採用oracle之外的第三方集群软件,才会存在oprocd进程 在l ...

  7. PLSQL配置登录用户信息

    PLSQL配置登录用户信息 2012-08-30 08:47:02     我来说两句      作者:lsxy117 收藏    我要投稿 PLSQL配置登录用户信息   工作中经常使用PLSQL ...

  8. windows系统SVN和apache的下载和安装

    原文:windows系统SVN和apache的下载和安装 版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明http://ejb3.blogbus.com/logs/107443052. ...

  9. rapid-framework脚手架快速搭建springMVC框架项目

    rapid-framework介绍:   一个类似ruby on rails的java web快速开发脚手架,本着不重复发明轮子的原则,框架只是将零散的struts(struts2)+spring+h ...

  10. C#程序的157个建议

    编写高质量代码改善C#程序的157个建议——导航开篇   前言 由于最近工作重心的转移,原来和几个同事一起开发的项目也已经上线了,而新项目就是在现有的项目基础上进行优化延伸扩展.打个比方,现在已经上线 ...