nginxにおけるoutput chainの処理(一)


ここではngx_を詳しく見てみましょうlinux_sendfile_chainメソッド,この関数,すなわちnginxの送信関数である.
一般的に、私たちは最終的にこの関数を呼び出して最終的なデータを送信するので、bufのいくつかのパラメータの理解に重点を置いてこの関数を分析します.
関数のプロトタイプを見てみましょう.

ngx_chain_t *
ngx_linux_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)

1番目のパラメータは現在の接続であり、2番目のパラメータは送信するchainであり、3番目のパラメータは送信できる最大値である.
次に、ここのいくつかの重要な変数を見てみましょう.


send        buf         。
sent       buf   
prev_send           ,        buf   。
fprev  prev-send  ,    file   。

complete    buf      ,   sent    send - prev_send.

header      writev    buf。   only in memory buf。

struct iovec  *iov, headers[NGX_HEADERS]        sendfile writev   ,      header       iovec。


初期化を見てみましょう
   

wev = c->write;

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

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


    send = 0;
//  header,   in memory   
    header.elts = headers;
    header.size = sizeof(struct iovec);
    header.nalloc = NGX_HEADERS;
    header.pool = c->pool;

ここでnginxの処理の核心思想は,メモリが連続して隣接するbuf(in memoryでもin fileでも)を統合することである.
次のコードはin memoryを処理する部分で、bufを対応するiovec配列に入れます.


//    
for (cl = in;
             cl && header.nelts < IOV_MAX && send < limit;
             cl = cl->next)
        {
            if (ngx_buf_special(cl->buf)) {
                continue;
            }
//      buf ,      in file                ,      buf  in memoey in file  ,nginx     in file    。
            if (!ngx_buf_in_memory_only(cl->buf)) {
                break;
            }

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

//  limit     size
            if (send + size > limit) {
                size = limit - send;
            }
//  prev  pos,      buf       buf       。
            if (prev == cl->buf->pos) {
                iov->iov_len += (size_t) size;

            } else {
//        buf,  add  iovc。
                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     buf   。
            prev = cl->buf->pos + (size_t) size;
//       
            send += size;
        }

そしてin fileの処理ここで比較コアの1つの判断はfprev==cl->buf->file_posは、上のin memoryと同様に、fprevが前回処理したbufの末尾を保存しています.ここで、この2つが等しい場合、現在の2つのbufが連続する(ファイル連続).
ok.コードを見てみましょう.


//      header     0           buf,       in file  
if (header.nelts == 0 && cl && cl->buf->in_file && send < limit) {
//  file
            file = cl->buf;

//    。
            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.
                file_size += (size_t) size;
//         
                send += size;
//    in memory           last
                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);
        }

次に送信部である、ここでin fileはsendfile、in memoryは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

//    
            rc = sendfile(c->fd, file->file->fd, &offset, file_size);
......................................................
//        
            sent = rc > 0 ? rc : 0;

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

            sent = rc > 0 ? rc : 0;
}
        

次にこの部分がタグを更新する部分で、主にbufのタグです.
ここで注意したいのはngx_ですbuf_size部分では、このマクロは簡単にbufがmemoryにあるかどうかを判断し、もしそうであればposとlastで計算し、そうでなければfileにあると考えられます.
しかし、ここで問題があります.もし1つのbufがもともとfileの中にある場合、私たちは何らかの理由でメモリにコピーがありますが、メモリのコピーを修正していません.そこで、このbufを切断する必要があります.このとき、lastとpos、つまりbufに対応するポインタが正しく設定されていなければ、ここで問題が発生します.
ここにはもう一つのマークがあると思います.それは、メモリのコピーが読み取り専用であれば、送信時にmemoryに入れるべきではありません.


//  send - prev_send == sent           。
if (send - prev_send == sent) {
            complete = 1;
        }
//  congnect sent 。
        c->sent += sent;

//      chain,                ,         buf 。
        for (cl = in; cl; cl = cl->next) {

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

            if (sent == 0) {
                break;
            }
//  buf size
            size = ngx_buf_size(cl->buf);

//       size,     buf             。,       。
            if (sent >= size){
//  sent 
                sent -= size;
//        pos
                if (ngx_buf_in_memory(cl->buf)) {
                    cl->buf->pos = cl->buf->last;
                }
//   file
                if (cl->buf->in_file) {
                    cl->buf->file_pos = cl->buf->file_last;
                }

                continue;
            }

//        buf           ,             。       。
            if (ngx_buf_in_memory(cl->buf)) {
                cl->buf->pos += (size_t) sent;
            }
//  。
            if (cl->buf->in_file) {
                cl->buf->file_pos += sent;
            }

            break;
        }

最後の部分は、ループを終了するかどうかの操作です.ここで注意したいのは、nginxで送信が不完全であれば、直接戻ってくるので、送信済みのchainが返されず、そのbufも更新されていることです.これはnginxが単一スレッドであり、何の意味もない空回りやブロックができないため、completeが0の場合、nginxはシステム負荷が大きすぎると判断し、このとき直接戻り、その後他のことを処理し、次のchainと一緒に送信するのを待つ.


if (eintr) {
            continue;
        }
//     ,   。
        if (!complete) {
            wev->ready = 0;
            return cl;
        }

        if (send >= limit || cl == NULL) {
            return cl;
        }
//  in,          chain
        in = cl;