Unix Domain Socket(简称 UDS)是类 Unix 系统(Linux、macOS 等)中一种进程间通信(IPC, Inter-Process Communication) 的核心机制。它允许同一台机器上的两个进程通过文件系统路径进行高效、安全的数据交换。

虽然名字中有 “Socket”,但它不经过网络协议栈,性能远高于 TCP/IP loopback(localhost),广泛用于数据库(如 PostgreSQL、MySQL)、Web 服务(PHP-FPM、Supervisor)、容器通信等场景。


一、知识体系构成(共 7 大部分)

1. 基本概念与作用

  • 定义
    Unix Domain Socket 是一种本地进程间通信机制,使用文件系统路径作为地址(如 /tmp/mysql.sock)。
  • 核心作用
    • 同主机进程通信
    • 替代本地 TCP 连接(127.0.0.1:3306/tmp/mysql.sock
    • 提高性能、增强安全性
  • 类比
    • TCP Socket:IP + Port → 网络通信
    • Unix Socket:Path → 本地通信

2. 类型与通信模式

类型 对应常量 特点 应用场景
流式套接字 SOCK_STREAM 面向连接,可靠,字节流(类似 TCP) MySQL、Supervisor、HTTP Server
数据报套接字 SOCK_DGRAM 无连接,消息边界(类似 UDP) 简单消息传递
顺序包套接字 SOCK_SEQPACKET 保留消息边界 + 可靠传输(较少用) 特定协议

✅ 最常用的是 SOCK_STREAM


3. 地址结构:struct sockaddr_un

用于绑定和连接的地址结构:

struct sockaddr_un {
    sa_family_t sun_family;     // AF_UNIX
    char        sun_path[108];  // 路径,如 "/tmp/app.sock"
};
  • sun_family = AF_UNIX:表示使用 Unix Domain Socket
  • sun_path:最大 108 字节(Linux),路径过长会失败

4. 创建与通信流程(C 语言层面)

服务端(Server):
int sock = socket(AF_UNIX, SOCK_STREAM, 0);
struct sockaddr_un addr = { .sun_family = AF_UNIX };
strcpy(addr.sun_path, "/tmp/my.sock");

bind(sock, (struct sockaddr*)&addr, sizeof(addr));
listen(sock, 5);
int client_fd = accept(sock, NULL, NULL);
// read/write
客户端(Client):
int sock = socket(AF_UNIX, SOCK_STREAM, 0);
struct sockaddr_un addr = { .sun_family = AF_UNIX };
strcpy(addr.sun_path, "/tmp/my.sock");

connect(sock, (struct sockaddr*)&addr, sizeof(addr));
// send/recv

类似 TCP,但使用 AF_UNIX 和路径


5. 文件系统与生命周期

  • Socket 文件
    • 是一个特殊类型的文件(ls -l 显示为 s
    • 示例:srwxr-xr-x 1 user user 0 Apr 5 10:00 /tmp/app.sock
    • s 表示 socket 文件
  • 生命周期
    • 服务端 bind() 时创建
    • 服务端关闭时自动删除(或需手动)
    • 若未清理,下次启动报错 Address already in use

⚠️ 常见错误:bind: Address already in use → 手动删除残留 .sock 文件


6. 权限与安全机制

  • 文件权限控制
    • 可设置 chmodchown
    • 控制哪些用户/组可以连接
  • 优势
    • 不暴露网络端口
    • 防止外部扫描和攻击
    • 适合容器、本地服务隔离
  • 典型应用
    • Docker:/var/run/docker.sock(仅 root 可访问)
    • Supervisor:/var/run/supervisor.sock
    • PHP-FPM:/run/php/php8.1-fpm.sock

7. 与 TCP Socket 的对比

特性 Unix Domain Socket TCP Socket(localhost)
通信范围 仅本机 本机或远程
性能 ⭐ 极高(无网络协议栈) 较低(经过 TCP/IP 栈)
安全性 ⭐ 高(文件权限控制) 中(需防火墙、密码)
配置复杂度 中(路径管理) 简单(IP+Port)
调试工具 ncsocatlsof telnetcurlnetstat
跨主机 ❌ 不支持 ✅ 支持
文件系统依赖 ✅ 依赖路径 ❌ 无

结论:本地通信优先使用 Unix Socket


二、底层原理详解

1. 内核实现机制

  • Unix Socket 由操作系统内核实现,位于 传输层之下
  • 数据在内核空间直接拷贝,不经过网卡、IP、TCP/UDP
  • 使用 消息队列 在进程间传递数据
  • 支持 文件描述符传递SCM_RIGHTS)——这是 TCP 不具备的强大功能
文件描述符传递示例:
// 服务端可以将打开的文件 fd 发送给客户端
sendmsg(sockfd, &msg, 0);

用于进程间共享资源(如日志文件、数据库连接)


2. 两种路径类型

类型 说明
路径名 socket 普通路径,如 /tmp/app.sock,会创建文件
抽象 socket(Linux 特有) 路径以 \0 开头,不创建文件系统条目,如 \0hidden

抽象 socket 不占用文件系统,但仅 Linux 支持


3. 性能优势原理

TCP Loopback 流程:
应用 → Socket → TCP → IP → 网络驱动 → 回环接口 → IP → TCP → Socket → 应用

Unix Socket 流程:
应用 → VFS → 内核消息队列 → 应用
  • 减少 6 层协议处理
  • 零拷贝优化(copy_to_user 次数少)
  • 延迟更低,吞吐更高

实测性能:Unix Socket 比 TCP localhost 快 30%~50%


4. 系统调用与内核接口

系统调用 作用
socket(AF_UNIX, SOCK_STREAM, 0) 创建 socket
bind() 绑定到路径
listen() / accept() 监听和接受连接
connect() 客户端连接
send() / recv() 数据传输
close() 关闭连接(自动删除 socket 文件)

所有调用由内核 af_unix.c 模块处理(Linux 源码)


5. 文件描述符传递(SCM_RIGHTS)

这是 Unix Socket 的高级特性,允许进程间传递“打开的文件”或“socket”:

struct msghdr msg = {0};
struct cmsghdr *cmsg;
char cmsg_buf[CMSG_SPACE(sizeof(int))];

msg.msg_control = cmsg_buf;
msg.msg_controllen = sizeof(cmsg_buf);

cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
*(int*)CMSG_DATA(cmsg) = fd_to_send;

sendmsg(sockfd, &msg, 0);

用途:Nginx 将连接传递给 PHP-FPM worker


三、典型应用场景

应用 使用方式
MySQL / PostgreSQL /tmp/mysql.sock 替代 127.0.0.1:3306
Supervisor /var/run/supervisor.sock 管理进程
Docker /var/run/docker.sock 控制 Docker 引擎
PHP-FPM Nginx 通过 /run/php/php-fpm.sock 转发请求
Laravel Horizon 内部通信
Systemd 服务间通信(如 journald)

四、常见命令与调试工具

命令 作用
lsof /tmp/app.sock 查看谁在使用 socket
netstat -x 列出所有 Unix Socket
ss -x 更现代的替代(推荐)
socat - UNIX-CONNECT:/tmp/app.sock 手动发送数据
strace -p <pid> 跟踪 socket 系统调用
file /tmp/app.sock 查看文件类型

五、总结:知识体系图谱

Unix Domain Socket 知识体系
├── 核心概念
│   ├── 本地 IPC 机制
│   └── 文件路径作为地址
├── 通信类型
│   ├── SOCK_STREAM(流)
│   ├── SOCK_DGRAM(数据报)
│   └── SOCK_SEQPACKET
├── 地址结构
│   └── struct sockaddr_un
├── 创建流程
│   ├── socket() → bind() → listen() → accept()
│   └── connect()
├── 底层原理
│   ├── 内核消息队列
│   ├── 零网络开销
│   ├── 文件描述符传递(SCM_RIGHTS)
│   └── 抽象 socket
├── 安全与权限
│   ├── chmod / chown
│   └── 防外部访问
├── 对比 TCP
│   ├── 性能更高
│   ├── 更安全
│   └── 仅本地
└── 典型应用
    ├── 数据库
    ├── Web 服务
    ├── 容器
    └── 进程管理(Supervisor)

六、最佳实践建议

  1. ✅ 本地服务优先使用 Unix Socket
  2. ✅ 设置合理权限:chmod 600, chown www-data:www-data
  3. ✅ 使用 /var/run//tmp/ 标准路径
  4. ✅ 程序退出时自动清理 socket 文件
  5. ✅ 在 Docker 中通过 volume 共享 socket(如 /var/run/docker.sock
  6. ✅ 避免路径过长(< 108 字符)
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐