Linux socket 跨进程传递描述符实现技术

作者:袖梨 2026-07-12
Linux中跨进程传递文件描述符需通过Unix域套接字的sendmsg/recvmsg发送SCM_RIGHTS控制消息,使接收方获得指向相同内核文件表项的新fd,共享打开状态;仅AF_UNIX套接字支持,须正确设置地址族、路径权限及cmsg结构,并严格验证收发两端fd有效性。

Linux 中跨进程传递文件描述符,核心是利用 Unix Domain Socket 配合 sendmsg/recvmsg 传递 SCM_RIGHTS 类型的控制消息。这不是传一个整数,而是让内核在接收进程中创建一个新的 fd,指向与发送方相同的内核文件表项——真正实现“共享打开状态”,包括偏移量、访问模式、锁等。

关键前提:必须用 Unix 域套接字

只有 AF_UNIX 类型的 socket(流式 SOCK_STREAM 或数据报 SOCK_DGRAM)支持描述符传递。TCP/IP 套接字不支持。常见错误是误用网络 socket 或未正确设置地址族。

  • 服务端需 bind 到本地路径(如 /tmp/fdsock),并 listen/accept;客户端 connect 到该路径
  • 父子进程间可直接用 socketpair(AF_UNIX, SOCK_STREAM, 0, sv) 快速建立双向通道,省去 bind/connect 步骤
  • 路径权限需合理(如 0700),避免非授权进程连接

发送端:构造带 SCM_RIGHTS 的控制消息

发送 fd 时,不能用普通 send,必须填充 msghdr 并附带 cmsg 控制数据。重点在 msg_control 字段的组织。

  • 分配足够空间:用 CMSG_SPACE(sizeof(int)) 计算缓冲区大小,包含头部+数据
  • 设置 cmsg 头部:cmsg_level = SOL_SOCKETcmsg_type = SCM_RIGHTS
  • 把待传 fd 写入 CMSG_DATA(cmsg),注意是 int 值,不是指针
  • msg_iov 可只传 1 字节 dummy 数据(如 "x"),因为实际 payload 是控制消息,不是 iov

接收端:安全提取并验证 fd

recvmsg 后需遍历控制消息,确认类型和层级,并检查返回 fd 是否有效。忽略验证可能引发后续读写失败或资源泄漏。

  • 调用 CMSG_FIRSTHDR(&msg) 获取首个 cmsg 指针
  • 校验 cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS
  • CMSG_DATA(cmsg) 取出 int 值,赋给新变量(如 int newfd = *(int*)CMSG_DATA(cmsg)
  • 立即检查 newfd >= 0,若为 -1 表示传递失败(如对方关闭 socket 或权限不足)

注意事项与典型陷阱

这套机制看似简单,但几个细节处理不当会导致静默失败或崩溃:

  • 发送前确保目标 fd 已打开且未 close-on-exec(fcntl(fd, F_SETFD, 0) 清除 FD_CLOEXEC)
  • 接收后建议用 fstat(newfd, &st) 验证是否可读/写,避免误用无效 fd
  • 多个 fd 可一次传递:把多个 int 放进同一块 CMSG_DATA 区域,长度设为 sizeof(int) * n
  • 不要在多线程中对同一 socket 并发 sendmsg/recvmsg,除非加锁或使用独立 socket 对

相关文章

精彩推荐