zoukankan      html  css  js  c++  java
  • 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共享内存。

     View Code

      其中,有个结构体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

     
     
     
  • 相关阅读:
    Spring学习笔记(8)——依赖注入
    JS中Ajax的实现部分
    Hibernate4之注解零配置
    SQL中的DDL、DML、DCL、TCL
    冷启动与热启动
    双击退出
    显示或者隐式
    Android笔记01--手机振动
    github与pycharm
    正则re模块--入门
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3149143.html
Copyright © 2011-2022 走看看