nginxにおけるoutput chainの処理(一)
ここではngx_を詳しく見てみましょうlinux_sendfile_chainメソッド,この関数,すなわちnginxの送信関数である.
一般的に、私たちは最終的にこの関数を呼び出して最終的なデータを送信するので、bufのいくつかのパラメータの理解に重点を置いてこの関数を分析します.
関数のプロトタイプを見てみましょう.
1番目のパラメータは現在の接続であり、2番目のパラメータは送信するchainであり、3番目のパラメータは送信できる最大値である.
次に、ここのいくつかの重要な変数を見てみましょう.
初期化を見てみましょう
ここでnginxの処理の核心思想は,メモリが連続して隣接するbuf(in memoryでもin fileでも)を統合することである.
次のコードはin memoryを処理する部分で、bufを対応するiovec配列に入れます.
そしてin fileの処理ここで比較コアの1つの判断はfprev==cl->buf->file_posは、上のin memoryと同様に、fprevが前回処理したbufの末尾を保存しています.ここで、この2つが等しい場合、現在の2つのbufが連続する(ファイル連続).
ok.コードを見てみましょう.
次に送信部である、ここでin fileはsendfile、in memoryはwritevを用いる.ここでの処理は比較的簡単で,送信して送信の大きさを判断することである.
次にこの部分がタグを更新する部分で、主にbufのタグです.
ここで注意したいのはngx_ですbuf_size部分では、このマクロは簡単にbufがmemoryにあるかどうかを判断し、もしそうであればposとlastで計算し、そうでなければfileにあると考えられます.
しかし、ここで問題があります.もし1つのbufがもともとfileの中にある場合、私たちは何らかの理由でメモリにコピーがありますが、メモリのコピーを修正していません.そこで、このbufを切断する必要があります.このとき、lastとpos、つまりbufに対応するポインタが正しく設定されていなければ、ここで問題が発生します.
ここにはもう一つのマークがあると思います.それは、メモリのコピーが読み取り専用であれば、送信時にmemoryに入れるべきではありません.
最後の部分は、ループを終了するかどうかの操作です.ここで注意したいのは、nginxで送信が不完全であれば、直接戻ってくるので、送信済みのchainが返されず、そのbufも更新されていることです.これはnginxが単一スレッドであり、何の意味もない空回りやブロックができないため、completeが0の場合、nginxはシステム負荷が大きすぎると判断し、このとき直接戻り、その後他のことを処理し、次のchainと一緒に送信するのを待つ.
一般的に、私たちは最終的にこの関数を呼び出して最終的なデータを送信するので、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;