ngx_linux_sendfile_chain

10 篇文章 1 订阅
本函数功能:发送数据(两种发送方式内存发送和文件发送)
内存发送和文件发送的区别:(writev和sendfile)
1、文件发送的效率相对内存发送效率要高很多,效率主要高在少了内核层到用户态的拷贝,用户态到内核态的拷贝。直接在磁盘将数据从网卡发送出去
2、通常的情况下,程序可能会在多个地方产生不同的bufferwritev是读取多个不连续的buffer然后集中写入。大并发服务器的时候这个效率还是很高的。
  writev和write函数区别就是在于多个非连续buffer的读取后写入,当负载大的时候就可以很好的体现出性能效果了。
  当然如果数据足够小(小于1024)且只有唯一的一个buffer,我们直接用 send/write 就可以了。
3、对于静态文件的传输,用sendfile可以减少系统调用注意:文件发送的常见场景主要是对文件数据不进行改变,如果数据需要作改变还是得用内存发送 

引用文章:http://blog.sina.com.cn/s/blog_7303a1dc0101mh6m.html 
参  数:第一个参数是当前的连接,第二个参数是所需要发送的chain,第三个参数是所能发送的最大值
返回值:第一种情况发送完成返回NULL,第二种情况,没发送完返回剩余的chain(假如链表有1,2,3,4,5,6节点--发送了1,2,3,剩余返回的就是4,5,6)


1)send,当前要发送链表所有节点数据的大小(每个节点buf数据相加)
2) pre_send上一次发送数据的大小
3)sent已经发送出去的数据大小
4)complete表示当前链表数据有没有发送完成,主要用来重置写事件wev->ready标志位,ready为0, 在ngx_handle_write_event的时候会根据该标志位判断注册写事件

ngx_chain_t *ngx_linux_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
{
    int            rc, tcp_nodelay;
    off_t          size, send, prev_send, aligned, sent, fprev;
    u_char        *prev;
    size_t         file_size;
    ngx_err_t      err;
    ngx_buf_t     *file;
    ngx_uint_t     eintr, complete;
    ngx_array_t    header;
    ngx_event_t   *wev;
    ngx_chain_t   *cl;
    struct iovec  *iov, headers[NGX_HEADERS];
#if (NGX_HAVE_SENDFILE64)
    off_t          offset;
#else
    int32_t        offset;
#endif
    wev = c->write;
    if (!wev->ready)
<span style="white-space:pre">	</span>{
<span style="white-space:pre">		</span>//printf("no ready \n");
        return in;
    }


    /* the maximum limit size is 2G-1 - the page size */


    if (limit == 0 || limit > (off_t) (NGX_SENDFILE_MAXSIZE - ngx_pagesize)) {
        limit = NGX_SENDFILE_MAXSIZE - ngx_pagesize;
    }




    send = 0;

    header.elts = headers;
    header.size = sizeof(struct iovec);
    header.nalloc = NGX_HEADERS;
    header.pool = c->pool;

    for ( ;; ) {
        file = NULL;
        file_size = 0;
        eintr = 0;
        complete = 0;
        prev_send = send;


        header.nelts = 0;


        prev = NULL;
        iov = NULL;


        /* create the iovec and coalesce the neighbouring bufs */
     <span style="color:#ff6666;">   //主要是读取链表参数in的长度大小</span>
        for (cl = in; cl && send < limit; cl = cl->next) {


            if (ngx_buf_special(cl->buf)) {
                continue;
            }


#if 1
            if (!ngx_buf_in_memory(cl->buf) && !cl->buf->in_file) {
                ngx_log_error(NGX_LOG_ALERT, c->log, 0,
                              "zero size buf in sendfile "
                              "t:%d r:%d f:%d %p %p-%p %p %O-%O",
                              cl->buf->temporary,
                              cl->buf->recycled,
                              cl->buf->in_file,
                              cl->buf->start,
                              cl->buf->pos,
                              cl->buf->last,
                              cl->buf->file,
                              cl->buf->file_pos,
                              cl->buf->file_last);


                ngx_debug_point();


<span style="white-space:pre">	</span>//printf("sendfile_chain error 1\n");
                return NGX_CHAIN_ERROR;
            }
#endif


            if (!ngx_buf_in_memory_only(cl->buf)) {
                break;
            }


            size = cl->buf->last - cl->buf->pos;


            if (send + size > limit) {
                size = limit - send;
            }


            if (prev == cl->buf->pos) {
                iov->iov_len += (size_t) size;


            } else {
                if (header.nelts >= IOV_MAX) {
                    break;
                }


                iov = ngx_array_push(&header);
                if (iov == NULL) {
                    return NGX_CHAIN_ERROR;
                }


                iov->iov_base = (void *) cl->buf->pos;
                iov->iov_len = (size_t) size;
            }


            prev = cl->buf->pos + (size_t) size;
            send += size;
        }


        /* set TCP_CORK if there is a header before a file */


        if (c->tcp_nopush == NGX_TCP_NOPUSH_UNSET
            && header.nelts != 0
            && cl
            && cl->buf->in_file)
        {
            /* the TCP_CORK and TCP_NODELAY are mutually exclusive */


            if (c->tcp_nodelay == NGX_TCP_NODELAY_SET) {


                tcp_nodelay = 0;


                if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY,
                               (const void *) &tcp_nodelay, sizeof(int)) == -1)
                {
                    err = ngx_errno;


                    /*
                     * there is a tiny chance to be interrupted, however,
                     * we continue a processing with the TCP_NODELAY
                     * and without the TCP_CORK
                     */


                    if (err != NGX_EINTR) {
                        wev->error = 1;
                        ngx_connection_error(c, err,
                                             "setsockopt(TCP_NODELAY) failed");
                        return NGX_CHAIN_ERROR;
                    }


                } else {
                    c->tcp_nodelay = NGX_TCP_NODELAY_UNSET;


                    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
                                   "no tcp_nodelay");
                }
            }


            if (c->tcp_nodelay == NGX_TCP_NODELAY_UNSET) {


                if (ngx_tcp_nopush(c->fd) == NGX_ERROR) {
                    err = ngx_errno;


                    /*
                     * there is a tiny chance to be interrupted, however,
                     * we continue a processing without the TCP_CORK
                     */


                    if (err != NGX_EINTR) {
                        wev->error = 1;
                        ngx_connection_error(c, err,
                                             ngx_tcp_nopush_n " failed");
                        return NGX_CHAIN_ERROR;
                    }


                } else {
                    c->tcp_nopush = NGX_TCP_NOPUSH_SET;


                    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
                                   "tcp_nopush");
                }
            }
        }


        /* get the file buf */
        if (header.nelts == 0 && cl && cl->buf->in_file && send < limit) {
            file = cl->buf;
            /* coalesce the neighbouring file bufs */


            do {
                size = cl->buf->file_last - cl->buf->file_pos;


                if (send + size > limit) {
                    size = limit - send;


                    aligned = (cl->buf->file_pos + size + ngx_pagesize - 1)
                               & ~((off_t) ngx_pagesize - 1);


                    if (aligned <= cl->buf->file_last) {
                        size = aligned - cl->buf->file_pos;
                    }
                }


                file_size += (size_t) size;
                send += size;
                fprev = cl->buf->file_pos + size;
                cl = cl->next;


            } while (cl
                     && cl->buf->in_file
                     && send < limit
                     && file->file->fd == cl->buf->file->fd
                     && fprev == cl->buf->file_pos);
        }
    //下面这个if...else...主要是数据发送,if中是文件发送,else中是内存数据发送,两者分别调用了内核系统调用sendfile/writev
        if (file) {
#if 1
            if (file_size == 0) {
                ngx_debug_point();
                return NGX_CHAIN_ERROR;
            }
#endif
#if (NGX_HAVE_SENDFILE64)
            offset = file->file_pos;
#else
            offset = (int32_t) file->file_pos;
#endif


            ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
                           "sendfile: @%O %uz", file->file_pos, file_size);


            rc = sendfile(c->fd, file->file->fd, &offset, file_size);

            if (rc == -1) {
                err = ngx_errno;


                switch (err) {
                case NGX_EAGAIN:
                    break;


                case NGX_EINTR:
                    eintr = 1;
                    break;


                default:
                    wev->error = 1;
                    ngx_connection_error(c, err, "sendfile() failed");
                    return NGX_CHAIN_ERROR;
                }


                ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
                               "sendfile() is not ready");
            }


            sent = rc > 0 ? rc : 0;


            ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
                           "sendfile: %d, @%O %O:%uz",
                           rc, file->file_pos, sent, file_size);


        } else {
            rc = writev(c->fd, header.elts, header.nelts);
            if (rc == -1) {
                err = ngx_errno;


                switch (err) {
                case NGX_EAGAIN:
                    break;


                case NGX_EINTR:
                    eintr = 1;
                    break;


                default:
                    wev->error = 1;
                    ngx_connection_error(c, err, "writev() failed");
                    return NGX_CHAIN_ERROR;
                }


                ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
                               "writev() not ready");
<span style="white-space:pre">		</span>//printf("writev size=%d not ready\n",rc);
            }


            sent = rc > 0 ? rc : 0;


            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "writev: %O", sent);
        }


        if (send - prev_send == sent) {
            complete = 1;
        }
#if 0
<span style="white-space:pre">		</span>if(sent == 0)
<span style="white-space:pre">		</span>printf("send=%lld  prev_send=%lld sent=%lld complete=%d rc=%d\n",send,prev_send,sent,complete,rc);
#endif
        c->sent += sent;
<span style="white-space:pre">	</span>//下面这个主要是给发送完数据的chain的buf重置数据标志位,将chain中buf的pos的位置移动到last的位置,但是内存数据其实仍然还在buf中<span style="white-space:pre">	</span>
        for ( /* void */ ; in; in = in->next) {


            if (ngx_buf_special(in->buf)) {
                continue;
            }
     <span style="white-space:pre">	</span>//sent表示上次发送完成的数据,如果sent小于send剩下的in链表将会作为返回值返回<span style="white-space:pre">	</span>
            if (sent == 0) {
                break;
            }


            size = ngx_buf_size(in->buf);


            if (sent >= size) {
                sent -= size;


                if (ngx_buf_in_memory(in->buf)) {
                    in->buf->pos = in->buf->last;
                }


                if (in->buf->in_file) {
                    in->buf->file_pos = in->buf->file_last;
                }


                continue;
            }


            if (ngx_buf_in_memory(in->buf)) {
                in->buf->pos += (size_t) sent;
            }


            if (in->buf->in_file) {
                in->buf->file_pos += sent;
            }


            break;
        }


        if (eintr) {
            continue;
        }


        if (!complete) {
            wev->ready = 0;
            return in;
        }


        if (send >= limit || in == NULL) {
            return in;
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值