第一百二十章:内河
问道期涉及内核源码:
一
林小源在海边走了很久,忽然发现脚下有一条不起眼的河流。河流很窄,两岸是整齐的石壁,河水清澈见底,流速极快。
"这条河……不通向大海?"他蹲下身,用手掬了一捧水。
"不通。"一个平静的声音从河对岸传来。
林小源抬头,看到一个穿着朴素的中年人站在对岸。他的衣服上没有任何复杂的标记,只有简单的管道图案。他手中拿着两端各连着一个篮子的绳索。
"我是 Unix domain socket。"中年人说,"这条河只在本地流——不经过网络协议栈,不经过路由器,不经过网卡。数据直接在内核中复制,从一个进程的地址空间到另一个进程的地址空间。"
"所以比网络 socket 快?"
"快得多。"中年人说,"网络 socket 要经过 TCP/IP 协议栈——封装、路由、校验,每一步都有开销。我只需要一次内核复制。没有网络延迟,没有协议开销。"
林小源看着清澈的河水,注意到河底铺着文件路径——/tmp/my.sock、/var/run/docker.sock、/run/systemd/private。
"你用文件系统路径做地址?"
"对。struct sockaddr_un 里的 。"中年人说,"文件系统权限控制访问——只有有权限的进程才能连接我的 socket。这比网络 socket 的 IP 加端口更安全。"
二
"你有哪些类型?"林小源问。
中年人放下手中的绳索:"三种。——流式,类似 TCP,有序、可靠。——数据报,类似 UDP,无连接。——有序数据报,保持消息边界。"
"听起来和网络 socket 差不多。"
"接口一样。"中年人说,"、bind()、listen()、accept()、connect()、send()、recv()——同样的系统调用,同样的编程模型。区别只在地址族:网络用 ,我用 。"
"那管道呢?管道也是本机通信。"
中年人笑了:"管道是单向的——一端读,一端写。而且管道只能在有亲缘关系的进程间使用——父子进程。我是双向的,任意进程都能连接。你可以用一个 Unix socket 同时读写,管道做不到。"
林小源想到了什么:"Docker 用的就是你?"
"/var/run/docker.sock。"中年人点头,"Docker daemon 监听这个 socket,客户端通过它发送命令。MySQL 也用我——/var/run/mysqld/mysqld.sock。systemd 也用我——/run/systemd/private。"
"所以你是本机进程间通信的主力。"
"不是唯一的方式,但可能是最常用的方式。"中年人说,"共享内存更快,但编程复杂。管道更简单,但功能有限。我在速度和功能之间找到了平衡。"
三
林小源注意到河流的水流虽然快,但非常平稳——没有波浪,没有漩涡。
"你的数据不会乱序?"
"不会。 保证有序——数据按发送顺序到达。而且没有网络抖动——本机通信的延迟几乎为零,稳定得像一条直线。"
"那你有没有缺点?"
中年人沉思了一会儿:"我不能跨机器通信。这是最大的限制——如果你需要和远程服务器通信,还是得用网络 socket。但如果你在同一台机器上的进程间通信,我比任何网络方案都快。"
"还有呢?"
"文件系统的开销。"中年人说,"每次创建 Unix socket 都要在文件系统上创建一个节点。如果频繁创建销毁,文件系统会有压力。不过这不是大问题——用 abstract socket(路径以 \0 开头)可以避免文件系统操作。"
林小源站起身,看着这条安静的内河。它不像大海那样波澜壮阔,但在这狭窄的河道里,数据以最快的速度流动着——没有协议开销,没有网络延迟,只有纯粹的内核复制。
/*
* Unix domain socket:
*
* 1. 类型
* SOCK_STREAM — 流式(类似 TCP)
* SOCK_DGRAM — 数据报(类似 UDP)
* SOCK_SEQPACKET — 有序数据报
*
* 2. 地址
* 文件系统路径(如 /tmp/my.sock)
* AF_UNIX 地址族
*
* 3. 优势
* 不经过网络协议栈
* 没有网络开销
* 比网络 socket 快
*
* 4. 用途
* 进程间通信(IPC)
* 数据库连接(如 MySQL)
* 系统服务(如 systemd)
*
* Unix domain socket vs 管道:
* 管道: 单向,只能父子进程
* Unix socket: 双向,任意进程
*/
printf("=== Unix domain socket — 本机通信 ===\n\n");
printf("Unix domain socket:\n");
printf(" 同一台机器上的进程间通信\n");
printf(" 使用文件系统路径作为地址\n");
printf(" 不经过网络协议栈\n\n");
printf("--- 类型 ---\n");
printf("SOCK_STREAM: 流式(类似 TCP)\n");
printf("SOCK_DGRAM: 数据报(类似 UDP)\n");
printf("SOCK_SEQPACKET: 有序数据报\n\n");
printf("--- 地址 ---\n");
printf("struct sockaddr_un {\n");
printf(" sun_family = AF_UNIX;\n");
printf(" sun_path = \"/tmp/my.sock\";\n");
printf("};\n\n");
printf("--- 使用方式 ---\n");
printf("服务器:\n");
printf(" socket(AF_UNIX, SOCK_STREAM, 0)\n");
printf(" bind(\"/tmp/my.sock\")\n");
printf(" listen()\n");
printf(" accept()\n\n");
printf("客户端:\n");
printf(" socket(AF_UNIX, SOCK_STREAM, 0)\n");
printf(" connect(\"/tmp/my.sock\")\n\n");
printf("--- 优势 ---\n");
printf("1. 速度: 不经过网络协议栈\n");
printf("2. 安全: 文件系统权限控制\n");
printf("3. 简单: 不需要 IP 和端口\n\n");
printf("--- Unix socket vs 管道 ---\n");
printf("管道:\n");
printf(" 单向\n");
printf(" 只能父子进程\n\n");
printf("Unix socket:\n");
printf(" 双向\n");
printf(" 任意进程\n\n");
printf("--- 用途 ---\n");
printf("MySQL: /var/run/mysqld/mysqld.sock\n");
printf("Docker: /var/run/docker.sock\n");
printf("systemd: /run/systemd/private\n");#include <stdio.h>
/*
* Unix domain socket:
*
* 1. 类型
* SOCK_STREAM — 流式(类似 TCP)
* SOCK_DGRAM — 数据报(类似 UDP)
* SOCK_SEQPACKET — 有序数据报
*
* 2. 地址
* 文件系统路径(如 /tmp/my.sock)
* AF_UNIX 地址族
*
* 3. 优势
* 不经过网络协议栈
* 没有网络开销
* 比网络 socket 快
*
* 4. 用途
* 进程间通信(IPC)
* 数据库连接(如 MySQL)
* 系统服务(如 systemd)
*
* Unix domain socket vs 管道:
* 管道: 单向,只能父子进程
* Unix socket: 双向,任意进程
*/
int main() {
printf("=== Unix domain socket — 本机通信 ===\n\n");
printf("Unix domain socket:\n");
printf(" 同一台机器上的进程间通信\n");
printf(" 使用文件系统路径作为地址\n");
printf(" 不经过网络协议栈\n\n");
printf("--- 类型 ---\n");
printf("SOCK_STREAM: 流式(类似 TCP)\n");
printf("SOCK_DGRAM: 数据报(类似 UDP)\n");
printf("SOCK_SEQPACKET: 有序数据报\n\n");
printf("--- 地址 ---\n");
printf("struct sockaddr_un {\n");
printf(" sun_family = AF_UNIX;\n");
printf(" sun_path = \"/tmp/my.sock\";\n");
printf("};\n\n");
printf("--- 使用方式 ---\n");
printf("服务器:\n");
printf(" socket(AF_UNIX, SOCK_STREAM, 0)\n");
printf(" bind(\"/tmp/my.sock\")\n");
printf(" listen()\n");
printf(" accept()\n\n");
printf("客户端:\n");
printf(" socket(AF_UNIX, SOCK_STREAM, 0)\n");
printf(" connect(\"/tmp/my.sock\")\n\n");
printf("--- 优势 ---\n");
printf("1. 速度: 不经过网络协议栈\n");
printf("2. 安全: 文件系统权限控制\n");
printf("3. 简单: 不需要 IP 和端口\n\n");
printf("--- Unix socket vs 管道 ---\n");
printf("管道:\n");
printf(" 单向\n");
printf(" 只能父子进程\n\n");
printf("Unix socket:\n");
printf(" 双向\n");
printf(" 任意进程\n\n");
printf("--- 用途 ---\n");
printf("MySQL: /var/run/mysqld/mysqld.sock\n");
printf("Docker: /var/run/docker.sock\n");
printf("systemd: /run/systemd/private\n");
return 0;
}道藏笔记
内核启示
Unix domain socket 是本机进程间通信的方式。
Unix domain socket 的特点:
- 使用文件系统路径作为地址
- 不经过网络协议栈
- 比网络 socket 快
类型:
- — 流式
- — 数据报
- — 有序数据报
优势:
- 速度 — 不经过协议栈
- 安全 — 文件系统权限控制
- 简单 — 不需要 IP 和端口
Unix domain socket 是"内河"——比网络更快更安全。
Unix socket 之试
Unix socket 内河里,提供面向连接字节流语义的 socket 类型是什么?