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_create 或 uv_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。