Understanding Nginx Architecture: Daemon Process, Worker Model, Connection Handling, and Core Data Structures

This article explains how Nginx runs as a daemon with a master process and multiple worker processes, details its asynchronous non‑blocking I/O model, connection and request handling, keep‑alive and pipeline mechanisms, and describes the key internal data structures such as connections, requests, arrays, queues, lists, strings, memory pools, hash tables, and red‑black trees.

Top Architect
Top Architect
Top Architect
Understanding Nginx Architecture: Daemon Process, Worker Model, Connection Handling, and Core Data Structures

Daemon Process

Nginx starts as a daemon on Unix systems, creating a master process that manages multiple worker processes.

Master and Worker Processes

The master process handles signals, monitors worker status, and restarts workers if they exit unexpectedly. Each worker process handles network events independently.

Worker Model and Process Advantages

Using separate processes avoids shared‑resource contention and eliminates the need for locks, improving stability and simplifying programming.

Multithreading Issues

Threads consume more memory and cause costly context switches; Nginx prefers a multi‑process model to avoid these overheads.

Asynchronous Non‑Blocking I/O

Nginx processes events asynchronously; it does not create threads for each request, resulting in low memory usage and no context switches.

Each request occupies minimal memory.

Event handling is lightweight.

Tests show that on a 24 GB machine Nginx can handle up to 2 million concurrent requests.

Connection Handling

Connections are defined in ngx_connection_t (found in src/core/ngx_connection.h/.c) and are based on TCP sockets (SOCK_STREAM).

Connection Process

After loading the configuration, Nginx creates the master process, opens listening sockets, forks workers, and each worker accepts connections, creating a ngx_connection_t structure to store client information.

struct ngx_connection_s {
    void               *data;
    ngx_event_t        *read;
    ngx_event_t        *write;
    ngx_socket_t        fd;
    ngx_recv_pt         recv;
    ngx_send_pt         send;
    ngx_recv_chain_pt   recv_chain;
    ngx_send_chain_pt   send_chain;
    ngx_listening_t    *listening;
    off_t               sent;
    ngx_log_t          *log;
    ngx_pool_t         *pool;
    struct sockaddr    *sockaddr;
    socklen_t           socklen;
    ngx_str_t           addr_text;
    #if (NGX_SSL)
    ngx_ssl_connection_t  *ssl;
    #endif
    /* ... other flags ... */
};

Connection Pool

Nginx maintains a pool of pre‑allocated ngx_connection_t structures per worker (size = worker_connections) and reuses them via a free‑connections list to avoid frequent allocations.

Accept Mutex and Competition

To prevent the “thundering herd” problem, Nginx uses accept_mutex so only one worker accepts new connections at a time, controlled by ngx_accept_disabled and related logic.

ngx_accept_disabled = ngx_cycle->connection_n / 8 - ngx_cycle->free_connection_n;
if (ngx_use_accept_mutex) {
    if (ngx_accept_disabled > 0) {
        ngx_accept_disabled--;
    } else {
        if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) {
            return;
        }
        if (ngx_accept_mutex_held) {
            flags |= NGX_POST_EVENTS;
        } else {
            if (timer == NGX_TIMER_INFINITE || timer > ngx_accept_mutex_delay) {
                timer = ngx_accept_mutex_delay;
            }
        }
    }
}

HTTP Request Processing

An HTTP request is represented by ngx_http_request_t, which encapsulates all request data.

struct ngx_http_request_s {
    uint32_t                          signature;   /* "HTTP" */
    ngx_connection_t                 *connection;
    void                           **ctx;
    void                           **main_conf;
    void                           **srv_conf;
    void                           **loc_conf;
    ngx_http_event_handler_pt         read_event_handler;
    ngx_http_event_handler_pt         write_event_handler;
    /* ... many fields ... */
    ngx_uint_t                        method;
    ngx_uint_t                        http_version;
    ngx_str_t                         request_line;
    ngx_str_t                         uri;
    ngx_str_t                         args;
    /* ... */
    unsigned                          keepalive:1;
    unsigned                          pipeline:1;
    /* ... */
};

The processing flow includes initializing the request, handling headers, body, invoking the appropriate handler, and running through phase handlers.

Keep‑Alive and Pipeline

Keep‑alive allows multiple requests over a single TCP connection, reducing the number of connections and time‑wait states. Pipeline uses the same connection to send multiple requests without waiting for each response, further improving efficiency.

Lingering Close

When closing a connection, Nginx waits briefly (lingering timeout) to read any remaining client data, preventing lost ACKs.

Core Data Structures

Arrays (ngx_array_t)

typedef struct {
    void        *elts;   /* pointer to data */
    ngx_uint_t   nelts;  /* number of elements */
    size_t       size;   /* size of each element */
    ngx_uint_t   nalloc; /* allocated capacity */
    ngx_pool_t  *pool;  /* memory pool */
} ngx_array_t;

Dynamic array similar to a C++ vector.

Queues (ngx_queue_t)

struct ngx_queue_s {
    ngx_queue_t  *prev;
    ngx_queue_t  *next;
};

Doubly‑linked list used for various internal queues.

Lists (ngx_list_t)

typedef struct {
    ngx_list_part_t  *last;   /* last part */
    ngx_list_part_t   part;   /* first part */
    size_t            size;   /* size of each element */
    ngx_uint_t        nalloc; /* elements per part */
    ngx_pool_t       *pool;   /* memory pool */
} ngx_list_t;

Strings (ngx_str_t)

typedef struct {
    size_t    len;   /* length */
    u_char   *data; /* pointer to data (not NUL‑terminated) */
} ngx_str_t;

Memory Pool (ngx_pool_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;
};

Efficient memory allocator used throughout Nginx.

Hash Table (ngx_hash_t)

typedef struct {
    ngx_hash_elt_t  **buckets;
    ngx_uint_t        size;
} ngx_hash_t;

Static hash built once during initialization; each bucket stores a contiguous block of elements.

Red‑Black Tree (ngx_rbtree_t)

struct ngx_rbtree_node_s {
    ngx_rbtree_key_t   key;
    ngx_rbtree_node_t *left;
    ngx_rbtree_node_t *right;
    ngx_rbtree_node_t *parent;
    u_char             color;
    u_char             data;
};

struct ngx_rbtree_s {
    ngx_rbtree_node_t *root;
    ngx_rbtree_node_t *sentinel;
    ngx_rbtree_insert_pt insert;
};

Used for fast ordered lookups, e.g., timers.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

AsynchronousC programming
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.