zoukankan      html  css  js  c++  java
  • Nginx学习笔记(四) 源码分析&socket/UDP/shmem

    源码分析

      在茫茫的源码中,看到了几个好像挺熟悉的名字(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_ */
    View Code

      其中,创建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 */
    View Code

       开始就看到了这个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共享内存。

    /*
     * Copyright (C) Igor Sysoev
     * Copyright (C) Nginx, Inc.
     */
    
    
    #ifndef _NGX_SHMEM_H_INCLUDED_
    #define _NGX_SHMEM_H_INCLUDED_
    
    
    #include <ngx_config.h>
    #include <ngx_core.h>
    
    
    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;
    
    
    ngx_int_t ngx_shm_alloc(ngx_shm_t *shm);
    void ngx_shm_free(ngx_shm_t *shm);
    
    
    #endif /* _NGX_SHMEM_H_INCLUDED_ */
    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

  • 相关阅读:
    Vasya and Endless Credits CodeForces
    Dreamoon and Strings CodeForces
    Online Meeting CodeForces
    数塔取数 基础dp
    1001 数组中和等于K的数对 1090 3个数和为0
    1091 线段的重叠
    51nod 最小周长
    走格子 51nod
    1289 大鱼吃小鱼
    POJ 1979 Red and Black
  • 原文地址:https://www.cnblogs.com/coder2012/p/3147468.html
Copyright © 2011-2022 走看看