libuv examples

libuv examples

tcp-echo-server

https://github.com/libuv/libuv/blob/v1.x/docs/code/tcp-echo-server/main.c

主要流程

int main() {
    loop = uv_default_loop();
    
    uv_tcp_t server;
    uv_tcp_init(loop, &server);
    
    uv_ipv4_addr("0.0.0.0", ..., &addr);
    
    uv_tcp_bind(&server, (...)&addr, 0);   // bind "0.0.0.0"
    uv_listen((uv_stream_t*) &server, ..., on_new_connection);
    uv_run(loop, UV_RUN_DEFAULT)

    // on_new_connection(uv_stream_t *server, int status)
    // 1. uv_tcp_t* client = "malloc uv_tcp"
    // 2. uv_tcp_init(loop, client);
    // 3. uv_accept(server, (...) client)
    // 4. uv_read_start((...) client, alloc_buffer, echo_read);

        // echo_read(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf)
        // 1. write_req_t *req = "malloc write_req"
        // 2. req->buf = uv_buf_init(buf->base, nread);
        // 3. uv_write((uv_write_t*) req, client, &req->buf,..., echo_write);  // write back to client
        // 4. echo_write(uv_write_t *req, int status) // called after write completes
}

multi-echo-server/worker.c

https://github.com/libuv/libuv/blob/v1.x/docs/code/multi-echo-server/worker.c

主要流程

int main() {
    loop = uv_default_loop();
    
    uv_pipe_init(loop, &queue, 1 /* ipc */);
    uv_pipe_open(&queue, 0); // queue 监听从父进程传入的 IPC 管道(uv_pipe)
    uv_read_start((uv_stream_t*)&queue, alloc_buffer, on_new_connection);
    uv_run(loop, UV_RUN_DEFAULT)
    // on_new_connection(uv_stream_t *q, ssize_t nread, const uv_buf_t *buf)
    // 1. uv_pipe_pending_count(pipe) // 检查此时 pipe 中是否挂起了 TCP 句柄
    // 2. uv_tcp_t *client = "malloc client"
    // 3. uv_tcp_init(loop, client);
    // 4. uv_accept(q, (uv_stream_t*) client)
    // print("Worker %d: Accepted fd %d\n")
    // 5. uv_read_start((uv_stream_t*) client, alloc_buffer, echo_read);
    	// echo_read, 同上
    
    return uv_run(loop, UV_RUN_DEFAULT);
}

问题一:tcp-echo-server 中 loop 线程运行在哪?是当前进程的主线程吗?

是的。

问题二:multi-echo-server/worker.c 中 loop 线程运行在哪?是worker进程的主线程吗?

是的。

问题三:multi-echo-server/worker.c 中有额外线程被创建吗?

没有,除非显式使用 uv_thread_createuv_queue_work。可以从运行模型中看出

[Master 进程 - 主线程] 监听 TCP
   ├─ fork() 出 [Worker1 - 进程 - 主线程自身跑 uv_loop]
   ├─ fork() 出 [Worker2 - 进程 - 主线程自身跑 uv_loop]
   └─ fork() 出 [Worker3...]

问题四:网络请求的“等待时间”属于哪个线程?文件读取等阻塞式IO属于哪个线程?

网络请求的等待时间是由操作系统内核异步处理的,不属于任何用户线程。

其余耗时任务通过自己创建线程池来执行,只有在你使用 uv_queue_work() 或自己创建线程时,才会有真正的后台线程参与执行任务,比如:

  • 计算密集型任务(如压缩、加密)
  • 阻塞式 I/O(如文件读取)
  • 第三方库调用(如数据库查询)

这些任务会在 libuv 的线程池中执行,执行完后再通过事件循环回调通知主线程。


multi-echo-server/main.c

https://github.com/libuv/libuv/blob/v1.x/docs/code/multi-echo-server/main.c

主要流程

int main() {
    loop = uv_default_loop();

    setup_workers(); // 启动 worker
    
    uv_tcp_t server;
    uv_tcp_init(loop, &server);
    
    uv_ip4_addr("0.0.0.0", 7000, &bind_addr);
    uv_tcp_bind(&server, (const struct sockaddr *)&bind_addr, 0); // bind "0.0.0.0"
    uv_listen((uv_stream_t*) &server, 128, on_new_connection)
    uv_run(loop, UV_RUN_DEFAULT)
}

对于 tcp-echo-server 中,on_new_connection 的行为就是让接入的client和server执行连接,然后 uv_read_start -> echo_read -> uv_write,直接就处理了。在 multi-echo-server 中,on_new_connection 的行为应该是让接入的client和server执行连接,然后将拿到的client句柄通过IPC管道传递给某个worker执行。

其中 setup_workers() 的工作为:

  • 获取当前程序路径,替换为 worker 可执行文件路径。

  • 获取 CPU 核心数,准备启动等量的 worker。

  • 分配 workers 数组。

  • 初始化 IPC 管道(ipc=1 表示支持句柄传递)。

  • 设置子进程的 stdio

    • stdin:绑定到 IPC 管道(主进程写,子进程读)。
    • stdout:忽略。
    • stderr:继承父进程的 stderr。
  • 设置子进程启动参数并调用 uv_spawn 启动 worker。

  • 每个 worker 启动后会运行 worker 程序,等待接收 socket。