当前位置: 首页 > news >正文

进程、线程、进程间通信Unix Domain Sockets (UDS)

进程、线程、UDS

  • 进程和线程
  • 进程间通信
  • Unix Domain Sockets (UDS)
    • UDS的核心适用场景和用途
    • 配置UDS的几种主要方式
    • socketpair() 基本配置流程
    • socketpair() 进阶——传递文件描述符
  • 补充
    • socketpair() 函数
    • struct msghdr 结构体
    • struct iovec
    • struct cmsghdr
    • struct iovec 、struct msghdr 、struct cmsghdr三者关系
    • sendmsg() 函数
    • recvmsg() 函数

博客园——进程和线程_1
博客园——进程和线程_2
博客园——浅析Unix domain socket是什么、Java如何使用UnixSocket调用Docker API对容器进行操作(jnr-unixsocket的使用)

进程和线程

每个进程中的内容每个线程中的内容
地址空间程序计数器
全局变量寄存器
打开文件堆栈
子进程状态
即将发生的定时器
信号与信号处理程序
账号信息
  1. 进程模型
    在进程模型中,计算机上所有可运行的软件,通常也包括操作系统本身,被组织成若干顺序进程,一个进程就是一个正在执行程序的实例,包括程序计算器、寄存器、变量当前值。

  2. 进程的状态
    进程运行,占用CPU资源执行程序
    就绪态,等待调度程序给进程分配CPU资源
    阻塞态,等待某种外部事件发生,比如用户输入

运行——》阻塞 :进程因为等待输入而被阻塞
运行——》就绪 :调度程序选择另外的进程执行
就绪——》运行 :调度程序选择了这个进程
阻塞——》就绪 :出现有效输入

  1. 进程的实现
    为了实现进程模型,操作系统维护了一张表格(结构数组)——进程表,每个进程占用一个表项——进程控制块,其包含了进程状态的重要信息,进程在从运行态切换到其它状态时,必须将一些必要的当前信息保存到进程控制块中。

进程控制块中的字段(部分)

进程管理存储管理文件管理
寄存器正文段指针根目录
程序计数器数据段指针工作目录
程序状态字堆栈段指针文件描述符
堆栈状态用户ID
进程状态组ID
优先级
调度参数
进程ID
父进程
进程组
信号
进程开始时间
使用的CPU时间
子进程的CPU时间
下次定时器时间
  1. 中断向量
    中断向量本质上是一个指针,指向中断服务程序(ISR, Interrupt Service Routine)的地址。中断向量存储了中断处理程序的内存地址,CPU通过该指针跳转到正确的代码位置执行来执行ISR。

所有的中断都是从保存寄存器开始,对于当前进程而言,通常是保存在进程控制块中,随后,会从堆栈中,删除由中断硬件机制存入堆栈的那部分信息,并将堆栈指针指向一个由进程处理程序所使用的临时堆栈。

一些诸如保存寄存器值和设置堆栈指针等操作,通常由一个短小的汇编语言例程来完成,当该例程结束后,它会调用一个C过程处理某个特定的终端类型剩下的工作。

在完成有关工作后,大概就会使相关的进程就绪,接着调用调度程序,决定下一个该运行的进程。决定好以后,会将控制转给一段汇编语言代码,为将要执行的进程装入寄存器值以及内存映射并启动该进程运行。

中断发生后操作系统最底层的工作步骤
1.硬件过程:压入堆栈程序计算器等
2.硬件从中断向量装入新的程序计数器
3.汇编语言过程:保存寄存器值
4.汇编语言过程:设置新的堆栈
5.C中断服务例程运行(读、缓冲写入)
6.调度程序决定下一个将要运行的进程
7.C过程:返回至汇编代码
8.汇编语言过程:开始运行新的当前进程

  1. POSIX线程
    IEEE在IEEE标准1003.1c中定义了线程的标准。它定义的线程包叫作pthread。
    大部分UNIX系统都支持该标准。

进程间通信

竞争条件:两个或多个进程读写某些共享数据,而最后的结果取决于进程运行的精确时序
互斥:以某种手段确保当一个进程在使用一个共享数据时,其他进程不能做同样的操作
临界区:对共享内存进行访问的程序片段

互斥实现方案:
屏蔽终端、锁变量、严格轮转法、Peterson解法、TSL指令

Unix/Linux 系统中的进程间通信(IPC, Inter-Process Communication)机制:

  1. 管道(Pipe)
    匿名管道(单向通信):
    通过 pipe() 系统调用创建,用于父子进程或兄弟进程间的通信。
    数据单向流动(一个写端,一个读端)。
    示例:ls | grep “file”(Shell 中的竖线 | 就是匿名管道)。
    命名管道(FIFO):
    通过 mkfifo 命令或 mkfifo() 函数创建,存在于文件系统中。
    允许无亲缘关系的进程通信。

  2. 信号(Signal)
    用于通知进程某事件的发生(如 SIGKILL、SIGTERM)。
    通过 kill()、raise() 或键盘组合(如 Ctrl+C 发送 SIGINT)触发。
    异步通信,处理函数通过 signal() 或 sigaction() 注册。

  3. 共享内存(Shared Memory)
    最高效的 IPC 方式,多个进程直接访问同一块内存。
    通过 shmget() 创建共享内存段,shmat() 附加到进程地址空间。
    需配合同步机制(如信号量)避免竞争条件。

  4. 消息队列(Message Queue)
    进程通过 msgget() 创建队列,msgsnd() 和 msgrcv() 发送/接收消息。
    消息按类型存储,支持优先级。
    与管道不同,消息队列是持久的(除非显式删除)。

  5. 信号量(Semaphore)
    用于进程间同步,避免资源竞争。
    通过 semget()、semop() 等操作信号量集。
    分为 System V 信号量和 POSIX 信号量(后者更轻量)。

  6. 套接字(Socket)
    支持跨网络通信,也可用于本地进程间通信(Unix Domain Socket)。
    本地套接字通过文件系统路径标识(如 /tmp/mysocket)。
    示例:AF_UNIX 地址族的套接字。

  7. 内存映射文件(Memory-Mapped File)
    通过 mmap() 将文件映射到进程内存空间,多个进程可共享修改。
    适用于大文件读写或进程间共享数据。

  8. 文件锁(File Locking)
    通过 fcntl() 或 flock() 对文件加锁,实现进程间同步。
    分为建议性锁(需进程主动检查)和强制性锁(内核强制限制)。

  9. 远程过程调用(RPC, Remote Procedure Call)
    允许进程调用另一台机器上的函数(如 Sun RPC)。
    本地通信也可通过 RPC 框架实现。

Unix Domain Sockets (UDS)

Unix Domain Sockets (UDS) 是一种进程间通信(IPC)机制,允许同一台主机上的进程相互通信。与网络套接字不同,UDS不通过网络协议栈,而是通过文件系统进行通信,因此效率更高。

  1. 使用场景
    同一主机上的客户端/服务器通信
    数据库系统与本地客户端通信
    X Window系统
    Docker守护进程通信
    系统服务内部通信

  2. 使用方法

步骤服务器端客户端
1.创建Socket socket(AF_UNIX, type, 0)socket(AF_UNIX, type, 0)
2.绑定地址bind() + listen()connect()
3.通信accept() → read/writeread/write
4.关闭close() + unlink()close()
  1. 与普通套接字的差异
    Unix Domain Sockets (UDS) 和普通网络套接字(如 TCP/UDP)在编程接口上非常相似,下面进行对比:

(1)地址结构不同
UDS 使用 sockaddr_un(文件系统路径或抽象名)
普通套接字使用 sockaddr_in(IP + 端口)

(2)通信范围不同

特性Unix Domain Sockets (UDS)普通套接字(TCP/UDP)
通信范围仅限同一台主机上的进程可以跨网络通信(本地或远程主机)
依赖网络协议栈不依赖,直接走内核 IPC 机制依赖 TCP/IP 或 UDP/IP 协议栈
性能更高(无需序列化、校验和、路由等)较低(有协议栈开销)

(3)绑定流程差异
UDS 需要处理文件系统路径
普通套接字绑定到 IP + 端口

(4)权限控制
UDS 受文件系统权限控制
普通套接字依赖网络权限

(5)数据传递能力
UDS 支持传递文件描述符和进程凭证
普通套接字只能传输原始数据

UDS的核心适用场景和用途

  1. 本地高性能进程间通信(IPC)
  2. 文件描述符与进程间状态传递
  3. 系统服务与守护进程通信
  4. 容器化环境(Container)内部通信
  5. 特权分离与安全沙盒
  6. 特殊场景:抽象命名空间(Linux 特有)

配置UDS的几种主要方式

具体取决于通信模型和需求

方法适用场景是否需要文件路径特点
socketpair()父子进程/线程间快速通信否(匿名)简单高效,但限于相关进程
常规 UDS客户端-服务器模型支持多客户端,需管理 socket 文件
抽象命名空间Linux 匿名通信否(但需特殊命名)无文件残留,不可移植
SOCK_DGRAM无连接消息传递可选类似 UDP,无可靠性保证
SCM_RIGHTS传递文件描述符依赖基础 UDS跨进程资源共享的唯一标准方式
I/O 多路复用高并发事件驱动依赖基础 UDS高性能,但实现复杂

socketpair() 基本配置流程

  1. 创建套接字对
  2. 创建子进程
  3. 关闭不需要的文件描述符
  4. 使用套接字进行通信
    下面是代码示例:
//创建套接字对
int sockets[2];
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) == -1) {perror("socketpair");exit(EXIT_FAILURE);
}

参数说明:

AF_UNIX 或 AF_LOCAL:表示本地通信(同一主机)

SOCK_STREAM:提供面向连接的可靠通信(类似TCP)

0:默认协议

sockets:用于存储创建的套接字描述符的数组

//创建子进程
pid_t pid = fork();
if (pid == -1) {perror("fork");exit(EXIT_FAILURE);
}
关闭不需要的文件描述符在子进程中
if (pid == 0) { // 子进程close(sockets[0]); // 关闭父进程使用的套接字// 现在可以使用 sockets[1] 与父进程通信// ... 通信代码 ...close(sockets[1]); // 通信结束后关闭exit(EXIT_SUCCESS);
}在父进程中
else { // 父进程close(sockets[1]); // 关闭子进程使用的套接字// 现在可以使用 sockets[0] 与子进程通信// ... 通信代码 ...close(sockets[0]); // 通信结束后关闭wait(NULL); // 等待子进程结束
}
可以使用 read()/write()send()/recv() 进行通信:父进程发送数据到子进程:char message[] = "Hello from parent";
write(sockets[0], message, sizeof(message));子进程接收数据:char buffer[256];
ssize_t n = read(sockets[1], buffer, sizeof(buffer));
if (n > 0) {printf("Child received: %s\n", buffer);
}

socketpair() 进阶——传递文件描述符

socketpair() 创建的套接字还可以用于传递文件描述符(如示例代码中的 SendFD 和 RecvFD 方法):

  1. 使用 sendmsg() 和 recvmsg() 系统调用
  2. 通过辅助数据 (cmsghdr) 传递文件描述符
  3. 设置 msg_control 和 msg_controllen 字段
  4. 使用 SCM_RIGHTS 作为控制消息类型

补充

socketpair() 函数

//创建一对相互连接的匿名套接字,适用于父子进程或线程间通信。
//与 pipe() 不同,socketpair 创建的套接字是全双工的(双向通信)。
//文件描述符通过 fork() 继承,无需暴露文件系统路径(匿名性)。
int socketpair(int domain, int type, int protocol, int sv[2]);
参数取值/类型作用
domainAF_LOCAL/AF_UNIX指定协议族(Unix Domain Socket,本地通信)
typeSOCK_STREAM面向连接的可靠通信(类似 TCP,保证数据顺序)
SOCK_DGRAM无连接的数据报(类似 UDP,不保证顺序)
protocol0通常为 0,表示默认协议
svint[2]用于返回两个关联的套接字描述符(sv[0] 和 sv[1])

struct msghdr 结构体

struct msghdr {void         *msg_name;       // 目标地址(用于网络通信,UDS 中为 NULL)socklen_t     msg_namelen;    // 地址长度struct iovec *msg_iov;        // 数据块数组(分散/聚集 I/O)int           msg_iovlen;     // 数据块数量void         *msg_control;    // 控制信息(如文件描述符、凭证)socklen_t     msg_controllen; // 控制信息长度int           msg_flags;      // 接收时的标志位(通常忽略)
};

关键成员

成员用途
msg_iov指向 struct iovec 数组,支持分散读写(Scatter/Gather I/O)
msg_control指向控制信息缓冲区(如传递文件描述符的 cmsghdr 结构)
msg_controllen控制信息的实际长度(需对齐到 CMSG_SPACE)

struct iovec

struct iovec {void  *iov_base;  // 数据缓冲区地址size_t iov_len;   // 数据长度
};

struct cmsghdr

struct cmsghdr {socklen_t cmsg_len;   // 控制信息总长度(含头部)int       cmsg_level; // 协议层级(如 SOL_SOCKET)int       cmsg_type;  // 信息类型(如 SCM_RIGHTS)// 随后是实际的控制数据(如 int fd)
};

struct iovec 、struct msghdr 、struct cmsghdr三者关系

  1. 这三个结构体共同用于 高级 I/O 操作(如 sendmsg/recvmsg),分工明确:
    iovec:管理普通数据(如字符串、二进制块)。
    msghdr:整合数据+控制信息,作为 I/O 操作的顶层容器。
    cmsghdr:专门处理控制信息(如文件描述符、进程凭证)。
  2. 它们的关系类似于 快递包裹:
    iovec = 包裹中的物品(数据内容)
    cmsghdr = 快递单上的备注(特殊指令)
    msghdr = 整个包裹(物品+快递单)

三者的内存布局

  struct msghdr+---------------------+| msg_iov  ---------->|--> struct iovec[0] {iov_base, iov_len}| msg_iovlen = 2      |    struct iovec[1] {iov_base, iov_len}|                     || msg_control ------->|--> struct cmsghdr| msg_controllen      |    |-- cmsg_len+---------------------+    |-- cmsg_level (SOL_SOCKET)|-- cmsg_type (SCM_RIGHTS)|-- CMSG_DATA() -> 实际数据(如 int fd)

总结:

结构体存储内容所属层级典型用途
iovec普通数据(用户态缓冲区)数据层分散/聚集 I/O
cmsghdr控制信息(内核级元数据)控制层传递文件描述符、进程凭证
msghdr整合 iovec + cmsghdr操作聚合层sendmsg/recvmsg 的参数

sendmsg() 函数

//发送数据和辅助数据(如文件描述符)。
//通过 msg_control 传递控制信息(需按 CMSG_SPACE 对齐)。
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
参数类型作用
sockfdint套接字描述符(如 socketpair 返回的 pipes[1])
msgconst msghdr*包含数据和控制信息的消息结构体
flagsint发送标志(如 MSG_DONTWAIT 非阻塞,通常设为 0)

recvmsg() 函数

//接收数据和辅助数据(如文件描述符)。
//通过 msg_control 提取控制信息(需检查 cmsg_type)。
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
参数类型作用
sockfdint套接字描述符(如 socketpair 返回的 pipes[0])
msgstruct msghdr*接收数据的消息结构体(需预分配缓冲区)
flagsint接收标志(如 MSG_WAITALL 阻塞等待全部数据,通常设为 0)

相关文章:

  • 【自然语言处理与大模型】大模型意图识别实操
  • 一文详解 Linux下的开源打印系统CUPS(Common UNIX Printing System)
  • 回收铼树脂RCX-5143
  • 航电系统之网络控制运动技术篇
  • 2025年4月通信科技领域周报(4.21-4.27):6G标准加速推进 空天地一体化网络进入实测阶段
  • 极光PDF编辑器:高效编辑,轻松管理PDF文档
  • (Go Gin)Gin学习笔记(五)会话控制与参数验证:Cookie使用、Sessions使用、结构体验证参数、自定义验证参数
  • 合并多个Excel文件到一个文件,并保留格式
  • 区块链+医疗:破解数据共享困局,筑牢隐私安全防线
  • Copilot 祝你走在AI前沿:2025 年 4 月动态
  • HTML5好看的水果蔬菜在线商城网站源码系列模板8
  • Copilot总结Word长文档功能更新升级
  • PCB设计工艺规范(一)概述
  • MCP Server 的 Stdio 与 SSE:两种通信方式的本质差异与技术选型指南
  • Linux Nginx网站服务【完整版】
  • 宝塔面板运行docker的jenkins
  • leetcode76
  • a-upload组件实现文件的上传——.pdf,.ppt,.pptx,.doc,.docx,.xls,.xlsx,.txt
  • [计算机科学#6]:从锁存器到内存,计算机存储的构建与原理
  • 安装kubernetes 1.33版本
  • 阿斯利康中国区一季度收入增5%,或面临最高800万美元新罚单
  • 特朗普执政百日集会吹嘘政绩,美国消费者信心指数跌至疫情以来最低
  • 马上评丨准入壁垒越少,市场活力越足
  • 专访|首夺天元头衔创生涯历史,王星昊打算一步一步慢慢来
  • TAE联手加州大学开发出新型核聚变装置:功率提升百倍,成本减半
  • 千亿市值光储龙头董事长向母校合肥工业大学捐赠1亿元