Bagaimana untuk melaksanakan kolam memori nginx

WBOY
Lepaskan: 2023-05-17 13:26:27
ke hadapan
1326 orang telah melayarinya

1. Pengenalan

Versi stabil terkini nginx1.20.2.
Untuk memperuntukkan memori dengan cekap dan cepat, dan mengurangkan pemecahan memori, nginx melaksanakan komponen kumpulan memori asasnya sendiri.
Fail pelaksanaan utamangx_palloc.h, ngx_palloc.c

2. Struktur data

2.1 Struktur utama kumpulan memori

typedef struct {
    u_char               *last;
    u_char               *end;
    ngx_pool_t           *next;
    ngx_uint_t            failed;
} ngx_pool_data_t;

struct ngx_pool_s {
    ngx_pool_data_t       d;
    size_t                max;
    ngx_pool_t           *current;
    ngx_chain_t          *chain;
    ngx_pool_large_t     *large;
    ngx_pool_cleanup_t   *cleanup;
    ngx_log_t            *log;
};
Salin selepas log masuk

Ahli pertama kumpulan memori ialah struktur:
Gunakan struktur ngx_pool_data_t untuk mewakili maklumat kumpulan memori semasa.
terakhir: alamat yang akan diperuntukkan kali seterusnya
tamat: alamat akhir kumpulan memori
seterusnya: senarai terpaut kolam memori, menyambungkan berbilang kumpulan memori

maks
keseluruhan kolam memori Saiz maksimum

arus
menunjukkan bermula dari kolam memori semasa untuk mencari memori yang tersedia

rantai
digunakan oleh penimbal, yang tidak terlibat di sini apabila

besar
Apabila memori yang diperlukan lebih besar daripada saiz maksimum kumpulan memori, ia perlu diperuntukkan terus melalui malloc dan kemudian disusun ke dalam senarai terpaut

pembersihan
Panggilan balik dipautkan senarai untuk kerja pembersihan

log
Pemegang log

2.2 Rantaian memori besar

Apabila memori yang akan diperuntukkan lebih besar daripada saiz maksimum kumpulan memori, kolam memori tidak dapat memenuhi peruntukan, jadi ia diperuntukkan terus daripada sistem dan kemudian membentuk senarai terpaut untuk penyelenggaraan.

typedef struct ngx_pool_large_s  ngx_pool_large_t;

struct ngx_pool_large_s {
    ngx_pool_large_t     *next;
    void                 *alloc;
};
Salin selepas log masuk

2.3 Rantaian tugas pembersihan

Terdapat senarai tugasan panggil balik terpaut Apabila kumpulan memori dimusnahkan, senarai terpaut ini akan dilalui mengikut urutan dan pengendali akan dipanggil semula satu demi satu. seorang untuk melakukan kerja pembersihan.

typedef void (*ngx_pool_cleanup_pt)(void *data);

typedef struct ngx_pool_cleanup_s  ngx_pool_cleanup_t;

struct ngx_pool_cleanup_s {
    ngx_pool_cleanup_pt   handler;
    void                 *data;
    ngx_pool_cleanup_t   *next;
};
Salin selepas log masuk

3. Gambar rajah struktur ingatan

3.1 Logik

Bagaimana untuk melaksanakan kolam memori nginx

3.2 Sebenar

Bagaimana untuk melaksanakan kolam memori nginx

Dapat dilihat bahawa banyak nod diperuntukkan daripada kumpulan memori, jadi anda boleh fokus pada data sebenar tanpa perlu risau tentang butiran lain.

4. Pelaksanaan

4.1 Cipta kumpulan memori

/*
 * NGX_MAX_ALLOC_FROM_POOL should be (ngx_pagesize - 1), i.e. 4095 on x86.
 * On Windows NT it decreases a number of locked pages in a kernel.
 */
#define NGX_MAX_ALLOC_FROM_POOL  (ngx_pagesize - 1)

#define NGX_DEFAULT_POOL_SIZE    (16 * 1024)
Salin selepas log masuk
rrree

Seperti yang anda lihat daripada kod, kumpulan memori maksimum tidak melebihi saiz saiz halaman

Bagaimana untuk melaksanakan kolam memori nginx

4.2 Peruntukkan ruang daripada kolam memori

Fungsi peruntukan dibahagikan kepada penjajaran memori dan salah jajaran memori, tetapi ini hanya mengawal peruntukan ruang dalam kolam memori dan tidak mengawal peruntukan memori yang besar.

(1) Peruntukkan ruang kecil

  • Penjajaran memoringx_palloc

  • Salah jajaran memoringx_pnalloc

ngx_pool_t *
ngx_create_pool(size_t size, ngx_log_t *log)
{
    ngx_pool_t  *p;

    p = ngx_memalign(NGX_POOL_ALIGNMENT, size, log);
    if (p == NULL) {
        return NULL;
    }

    p->d.last = (u_char *) p + sizeof(ngx_pool_t);
    p->d.end = (u_char *) p + size;
    p->d.next = NULL;
    p->d.failed = 0;

    size = size - sizeof(ngx_pool_t);
    p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL;

    p->current = p;
    p->chain = NULL;
    p->large = NULL;
    p->cleanup = NULL;
    p->log = log;

    return p;
}
Salin selepas log masuk

Apabila ruang yang perlu diperuntukkan kurang daripada maksimum, kaedah peruntukan memori kecil akan digunakan (iaitu, memperuntukkan ruang dari kolam memori Berbanding dengan ngx_palloc, ngx_pnalloc hanya mempunyai parameter terakhir apabila memanggil ngx_palloc_small 0.

Mula melintasi kolam memori yang ditunjuk oleh kolam->semasa, cari ruang yang memenuhi saiz peruntukan, dan jika ditemui, kembalikan alamat pertama

void *
ngx_palloc(ngx_pool_t *pool, size_t size)
{
#if !(NGX_DEBUG_PALLOC)
    if (size <= pool->max) {
        return ngx_palloc_small(pool, size, 1);
    }
#endif

    return ngx_palloc_large(pool, size);
}
Salin selepas log masuk

Apabila syarat peruntukan tidak dapat ditemui dalam kumpulan memori sedia ada , cipta kumpulan memori baharu

static ngx_inline void *
ngx_palloc_small(ngx_pool_t *pool, size_t size, ngx_uint_t align)
{
    u_char      *m;
    ngx_pool_t  *p;

    p = pool->current;

    do {
        m = p->d.last;

        if (align) {
            m = ngx_align_ptr(m, NGX_ALIGNMENT);
        }

        if ((size_t) (p->d.end - m) >= size) {
            p->d.last = m + size;

            return m;
        }

        p = p->d.next;

    } while (p);

    return ngx_palloc_block(pool, size);
}
Salin selepas log masuk

Antaranya, selepas mencipta kumpulan memori baharu, satu lagi traversal dilakukan, dan kiraan yang gagal ditambah satu apabila lebih daripada 4 , kolam memori ini akan dilangkau dan akan digunakan pada masa akan datang. Jangan mulakan carian daripadanya.
Adalah dianggap bahawa anda tidak boleh memenuhi peruntukan lebih daripada 4 kali, dan anda tidak akan dapat memenuhi peruntukan pada masa hadapan. Anda tidak akan digunakan lagi peruntukan

(2) Peruntukkan ruang yang besar

static void *
ngx_palloc_block(ngx_pool_t *pool, size_t size)
{
    u_char      *m;
    size_t       psize;
    ngx_pool_t  *p, *new;

    psize = (size_t) (pool->d.end - (u_char *) pool);

    m = ngx_memalign(NGX_POOL_ALIGNMENT, psize, pool->log);
    if (m == NULL) {
        return NULL;
    }

    new = (ngx_pool_t *) m;

    new->d.end = m + psize;
    new->d.next = NULL;
    new->d.failed = 0;

    m += sizeof(ngx_pool_data_t);
    m = ngx_align_ptr(m, NGX_ALIGNMENT);
    new->d.last = m + size;

    for (p = pool->current; p->d.next; p = p->d.next) {
        if (p->d.failed++ > 4) {
            pool->current = p->d.next;
        }
    }

    p->d.next = new;

    return m;
}
Salin selepas log masuk

Adalah dapat dilihat bahawa untuk mengelakkan peruntukan ruang, rantai besar dilalui untuk mencari nod yang boleh digunakan semula Walau bagaimanapun, jika senarai terpaut juga besar, ia mungkin terlalu perlahan, jadi hanya tiga yang pertama dicari Jika tiada satu pun daripada tiga dijumpai, ia diperuntukkan secara langsung ( Selain itu, nod juga diperuntukkan dari kolam memori, jadi semasa pembersihan berikutnya, anda tidak perlu. untuk menguruskan nod, anda hanya perlu melepaskan memori besar yang digunakan itu sendiri)

Penjajaran Memori

static void *
ngx_palloc_large(ngx_pool_t *pool, size_t size)
{
    void              *p;
    ngx_uint_t         n;
    ngx_pool_large_t  *large;

    p = ngx_alloc(size, pool->log);
    if (p == NULL) {
        return NULL;
    }

    n = 0;

    for (large = pool->large; large; large = large->next) {
        if (large->alloc == NULL) {
            large->alloc = p;
            return p;
        }

        if (n++ > 3) {
            break;
        }
    }

    large = ngx_palloc_small(pool, sizeof(ngx_pool_large_t), 1);
    if (large == NULL) {
        ngx_free(p);
        return NULL;
    }

    large->alloc = p;
    large->next = pool->large;
    pool->large = large;

    return p;
}
Salin selepas log masuk

4.3 Mendaftarkan Tugas Pembersihan

void *
ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment)
{
    void              *p;
    ngx_pool_large_t  *large;

    p = ngx_memalign(alignment, size, pool->log);
    if (p == NULL) {
        return NULL;
    }

    large = ngx_palloc_small(pool, sizeof(ngx_pool_large_t), 1);
    if (large == NULL) {
        ngx_free(p);
        return NULL;
    }

    large->alloc = p;
    large->next = pool->large;
    pool->large = large;

    return p;
}
Salin selepas log masuk

Ia boleh dilihat bahawa hanya satu nod diperuntukkan di sini, dan pengendali dan data data tidak ditetapkan, jadi ia bergantung pada pemanggil khusus untuk tetapan, kerana nod yang diperuntukkan dikembalikan di sini.

Sebagai contoh, dalam fungsi ngx_create_temp_file,

ngx_pool_cleanup_t *
ngx_pool_cleanup_add(ngx_pool_t *p, size_t size)
{
    ngx_pool_cleanup_t  *c;

    c = ngx_palloc(p, sizeof(ngx_pool_cleanup_t));
    if (c == NULL) {
        return NULL;
    }

    if (size) {
        c->data = ngx_palloc(p, size);
        if (c->data == NULL) {
            return NULL;
        }

    } else {
        c->data = NULL;
    }

    c->handler = NULL;
    c->next = p->cleanup;

    p->cleanup = c;

    ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, p->log, 0, "add cleanup: %p", c);

    return c;
}
Salin selepas log masuk

menjana fail sementara, mendaftarkan fd dan nama fail dalam tugas pembersihan, dan tiada pemprosesan khas diperlukan jika fail berikutnya tidak digunakan, dan kolam memori dilepaskan akan dibersihkan secara seragam.

4.4 Tetapkan semula kumpulan memori

  • Keluarkan memori besar

  • Tetapkan semula yang terakhir dalam ingatan

  • Tetapkan semula kiraan yang gagal

ngx_int_t
ngx_create_temp_file(ngx_file_t *file, ngx_path_t *path, ngx_pool_t *pool,
    ngx_uint_t persistent, ngx_uint_t clean, ngx_uint_t access)
{
    ...

    cln = ngx_pool_cleanup_add(pool, sizeof(ngx_pool_cleanup_file_t));
    if (cln == NULL) {
        return NGX_ERROR;
    }

       ...
        file->fd = ngx_open_tempfile(file->name.data, persistent, access);
				...
        if (file->fd != NGX_INVALID_FILE) {

            cln->handler = clean ? ngx_pool_delete_file : ngx_pool_cleanup_file;
            clnf = cln->data;

            clnf->fd = file->fd;
            clnf->name = file->name.data;
            clnf->log = pool->log;

            return NGX_OK;
        }
			...
}
Salin selepas log masuk

Terdapat fenomena di sini:
Apabila ruang tidak mencukupi dalam kolam memori, ngx_palloc_block akan dipanggil untuk mencipta kumpulan memori baharu . Dan terakhir menunjuk ke m += sizeof(ngx_pool_data_t);, jadi kumpulan memori yang baru diperuntukkan pada masa ini akan lebih besar daripada saiz kumpulan memori pertama yang tersedia (maks, semasa, rantai, besar, pembersihan, log Saiz medan ini (mungkin tidak sebanyak itu, kerana ia perlu dijajarkan, ia mungkin betul-betul sama selepas penjajaran), dan kini apabila menetapkan semula, p->d.last = (u_char *) p + sizeof(ngx_pool_t); saiz yang tersedia bagi setiap kumpulan memori menjadi sama semula.

4.5 Musnahkan kumpulan memori

  • Tugas pembersihan panggil balik

  • Lepaskan memori yang besar

  • <🎜 🎜>Lepaskan kumpulan memori itu sendiri

void
ngx_destroy_pool(ngx_pool_t *pool)
{
    ngx_pool_t          *p, *n;
    ngx_pool_large_t    *l;
    ngx_pool_cleanup_t  *c;

    for (c = pool->cleanup; c; c = c->next) {
        if (c->handler) {
            ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0,
                           "run cleanup: %p", c);
            c->handler(c->data);
        }
    }


    for (l = pool->large; l; l = l->next) {
        if (l->alloc) {
            ngx_free(l->alloc);
        }
    }

    for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {
        ngx_free(p);

        if (n == NULL) {
            break;
        }
    }
}
Salin selepas log masuk

4.6 大内存释放

通过遍历找到要释放的节点,将内存释放,并且将alloc设置成NULL,则有了节点重用的情况。

ngx_int_t
ngx_pfree(ngx_pool_t *pool, void *p)
{
    ngx_pool_large_t  *l;

    for (l = pool->large; l; l = l->next) {
        if (p == l->alloc) {
            ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0,
                           "free: %p", l->alloc);
            ngx_free(l->alloc);
            l->alloc = NULL;

            return NGX_OK;
        }
    }

    return NGX_DECLINED;
}
Salin selepas log masuk

4.7 分配并清空数据

void *
ngx_pcalloc(ngx_pool_t *pool, size_t size)
{
    void *p;

    p = ngx_palloc(pool, size);
    if (p) {
        ngx_memzero(p, size);
    }

    return p;
}
Salin selepas log masuk

正常分配的空间中都是垃圾数据,所以当前函数在分配空间后,将分配的空间清零。

4.8 回调文件清理

(1) 手动关闭指定fd

遍历清理任务,找到ngx_pool_cleanup_file的handler,如果是要关闭的fd,则回调

void
ngx_pool_run_cleanup_file(ngx_pool_t *p, ngx_fd_t fd)
{
    ngx_pool_cleanup_t       *c;
    ngx_pool_cleanup_file_t  *cf;

    for (c = p->cleanup; c; c = c->next) {
        if (c->handler == ngx_pool_cleanup_file) {

            cf = c->data;

            if (cf->fd == fd) {
                c->handler(cf);
                c->handler = NULL;
                return;
            }
        }
    }
}
Salin selepas log masuk

(2) 关闭fd

void
ngx_pool_cleanup_file(void *data)
{
    ngx_pool_cleanup_file_t  *c = data;

    ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, c->log, 0, "file cleanup: fd:%d",
                   c->fd);

    if (ngx_close_file(c->fd) == NGX_FILE_ERROR) {
        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
                      ngx_close_file_n " \"%s\" failed", c->name);
    }
}
Salin selepas log masuk

(3) 删除文件并关闭fd

void
ngx_pool_delete_file(void *data)
{
    ngx_pool_cleanup_file_t  *c = data;

    ngx_err_t  err;

    ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, c->log, 0, "file cleanup: fd:%d %s",
                   c->fd, c->name);

    if (ngx_delete_file(c->name) == NGX_FILE_ERROR) {
        err = ngx_errno;

        if (err != NGX_ENOENT) {
            ngx_log_error(NGX_LOG_CRIT, c->log, err,
                          ngx_delete_file_n " \"%s\" failed", c->name);
        }
    }

    if (ngx_close_file(c->fd) == NGX_FILE_ERROR) {
        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
                      ngx_close_file_n " \"%s\" failed", c->name);
    }
}
Salin selepas log masuk

Atas ialah kandungan terperinci Bagaimana untuk melaksanakan kolam memori nginx. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Label berkaitan:
sumber:yisu.com
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan