进程线程与 IPC 工控实践:多线程采集、共享内存、管道数据交互
进程线程与 IPC 工控实践多线程采集、共享内存、管道数据交互一个进程干活太慢多线程协作像流水线数据怎么在进程间流转IPC就是工厂里的传送带。一、进程与线程从独门独户到合租室友进程是操作系统分配资源的基本单位每个进程有自己的内存空间、文件描述符——就像独门独户的住户互不打扰。线程是进程内的执行单元多个线程共享同一个进程的地址空间——就像合租室友共享客厅厨房但各有各的卧室栈空间。对比进程线程地址空间独立隔离共享同进程内创建开销大fork复制小轻量创建通信方式需IPC机制直接读写共享变量崩溃影响不影响其他进程可能拖垮整个进程调度OS内核调度OS内核调度(pthread)工控场景怎么选多线程采集多个传感器、并行处理数据——共享地址空间方便数据交换多进程核心采集和Web服务分离——隔离性强一个崩了另一个不受影响实际项目往往是混合架构主进程多线程采集子进程做网络/显示服务二、多线程采集架构设计一个典型的工控多线程采集架构┌─────────────────────────────────────────────┐ │ 主线程 │ │ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │ │ │串口1│ │串口2│ │ CAN │ │ ADC │ ← 采集线程池 │ │ └─────┘ └─────┘ └─────┘ └─────┘ │ │ ↓ ↓ ↓ ↓ │ │ ┌──────────────────────────────┐ │ │ │ 数据缓冲区环形队列 │ ← mutex保护│ │ └──────────────────────────────┘ │ │ ↓ │ │ ┌──────┐ ┌──────┐ │ │ │处理线程│ │上报线程│ ← 工作线程 │ │ └──────┘ └──────┘ │ └─────────────────────────────────────────────┘核心思路采集线程只管收数据往缓冲区塞处理线程从缓冲区取数据加工上报线程把结果发出去。各线程通过互斥锁和条件变量协调。三、pthread编程基础3.1 创建与等待线程#includepthread.hvoid*thread_func(void*arg){intid*(int*)arg;printf(线程%d 开始工作\n,id);/* ... 干活 ... */returnNULL;}intmain(void){pthread_ttid;intarg1;pthread_create(tid,NULL,thread_func,arg);pthread_join(tid,NULL);/* 等待线程结束 */return0;}pthread_create创建线程传入函数指针和参数。pthread_join阻塞等待线程结束类似进程的wait。3.2 互斥锁Mutex多个线程同时写同一个缓冲区不加锁就是灾难——数据会被搅成一锅粥。pthread_mutex_tmtxPTHREAD_MUTEX_INITIALIZER;/* 加锁访问共享资源 */pthread_mutex_lock(mtx);/* ... 操作共享缓冲区 ... */pthread_mutex_unlock(mtx);锁的原则持锁时间尽量短只保护必要的数据操作别在锁里做sleep或IO。3.3 条件变量Condition Variable互斥锁只能防止并发冲突但线程间怎么通知对方条件变量就是信号灯——一个线程等信号另一个线程发信号。pthread_mutex_tmtxPTHREAD_MUTEX_INITIALIZER;pthread_cond_tcondPTHREAD_COND_INITIALIZER;/* 消费者等数据 */pthread_mutex_lock(mtx);while(buf_count0){pthread_cond_wait(cond,mtx);/* 等待自动解锁被唤醒后重新加锁 */}/* 取数据 ... */pthread_mutex_unlock(mtx);/* 生产者放数据后通知 */pthread_mutex_lock(mtx);/* 放数据到缓冲区 ... */buf_count;pthread_cond_signal(cond);/* 通知一个等待者 */pthread_mutex_unlock(mtx);为什么pthread_cond_wait要用while而非if因为可能被虚假唤醒——没有真正信号也被唤醒了所以要再检查一遍条件。四、IPC机制总览进程间通信Inter-Process Communication是多进程协作的桥梁IPC方式特点工控适用场景管道(pipe/fifo)半双工、字节流简单命令传递命名管道(mkfifo)有文件名、无关进程可用进程间命令/状态传递共享内存(shm)最快、零拷贝高速数据交换视频帧等消息队列(msg)有格式、按类型读取结构化命令分发信号(signal)异步通知、信息量少事件通知紧急停止等Socket网络或本地、最通用跨机器或本地进程通信工控最常用的三件套共享内存高速数据、命名管道命令传递、信号紧急通知。五、共享内存实战共享内存是最快的IPC方式——两个进程直接读写同一块内存不需要内核中转真正零拷贝。#includesys/shm.h#includestring.h#defineSHM_KEY0x1234#defineSHM_SIZE4096/* 进程A写入共享内存 */intwriter(void){intshmidshmget(SHM_KEY,SHM_SIZE,IPC_CREAT|0666);if(shmid0){perror(shmget);return-1;}void*addrshmat(shmid,NULL,0);/* 写入传感器数据 */sensor_data_t*data(sensor_data_t*)addr;data-temperature25.3f;data-timestamptime(NULL);shmdt(addr);return0;}/* 进程B读取共享内存 */intreader(void){intshmidshmget(SHM_KEY,SHM_SIZE,0666);if(shmid0){perror(shmget);return-1;}void*addrshmat(shmid,NULL,SHM_RDONLY);/* 只读映射 */sensor_data_t*data(sensor_data_t*)addr;printf(温度: %.1f, 时间: %ld\n,data-temperature,data-timestamp);shmdt(addr);return0;}共享内存本身没有同步机制必须配合互斥锁或信号量/* 共享内存中嵌入锁 */typedefstruct{pthread_mutex_tmtx;/* 进程间互斥锁 */intdata_ready;floattemperature;floathumidity;time_ttimestamp;}shm_buffer_t;/* 注意进程间mutex需要设置为PROCESS_SHARED属性 */pthread_mutexattr_tattr;pthread_mutexattr_init(attr);pthread_mutexattr_setpshared(attr,PTHREAD_PROCESS_SHARED);pthread_mutex_init(shm_buf-mtx,attr);六、管道实战命名管道命令传递命名管道FIFO在文件系统中有路径名任何进程都能打开它读写。适合进程间的命令传递——一个进程发命令另一个接收执行。#includesys/stat.h#includefcntl.h#defineFIFO_PATH/tmp/ic_cmd_fifo/* 创建命名管道 */mkfifo(FIFO_PATH,0666);/* 命令发送进程 */intsend_cmd(constchar*cmd){intfdopen(FIFO_PATH,O_WRONLY);write(fd,cmd,strlen(cmd)1);close(fd);return0;}/* 命令接收进程 */intrecv_cmd(void){intfdopen(FIFO_PATH,O_RDONLY);charbuf[128];read(fd,buf,sizeof(buf));printf(收到命令: %s\n,buf);close(fd);return0;}管道的特性默认阻塞——写端打开时读端阻塞等数据读端打开时写端阻塞等读者。这天然实现了等命令再执行。七、信号实战自定义信号通知信号是进程间最轻量的通知方式适合紧急事件通知——比如紧急停止或数据更新了。#includesignal.h/* 注册信号处理函数 */voidsigusr1_handler(intsig){printf(收到SIGUSR1: 数据已更新!\n);/* 触发重新读取共享内存 */}voidsigusr2_handler(intsig){printf(收到SIGUSR2: 紧急停止!\n);/* 立即停止采集关闭设备 */}intmain(void){signal(SIGUSR1,sigusr1_handler);signal(SIGUSR2,sigusr2_handler);while(1){pause();/* 等待信号 */}}/* 另一个进程发送信号 *//* kill -SIGUSR1 pid 通知数据更新 *//* kill -SIGUSR2 pid 紧急停止 */编程发送信号kill(target_pid,SIGUSR1);/* 通知目标进程 */信号局限携带信息量极少只有信号编号不能传数据。适合做触发器数据本身走共享内存或管道。八、完整代码示例多线程串口采集共享内存转发这是一个完整的工控采集架构多线程从多个串口采集数据通过共享内存转发给另一个进程处理。#includepthread.h#includesys/shm.h#includestdio.h#includestdlib.h#includestring.h#includeunistd.h#includetime.h#defineSHM_KEY0x5678#defineMAX_PORTS3#defineBUF_SIZE64typedefstruct{pthread_mutex_tmtx;pthread_cond_tcond;intcount;struct{intport_id;floatvalue;time_tts;}items[BUF_SIZE];}shm_ring_t;shm_ring_t*shm_buf;/* 采集线程 */void*collect_thread(void*arg){intport_id*(int*)arg;while(1){floatvalread_serial_port(port_id);/* 串口读取 */time_tnowtime(NULL);pthread_mutex_lock(shm_buf-mtx);if(shm_buf-countBUF_SIZE){shm_buf-items[shm_buf-count].port_idport_id;shm_buf-items[shm_buf-count].valueval;shm_buf-items[shm_buf-count].tsnow;shm_buf-count;pthread_cond_signal(shm_buf-cond);}pthread_mutex_unlock(shm_buf-mtx);usleep(100000);/* 100ms采集间隔 */}returnNULL;}/* 数据处理线程 */void*process_thread(void*arg){while(1){pthread_mutex_lock(shm_buf-mtx);while(shm_buf-count0){pthread_cond_wait(shm_buf-cond,shm_buf-mtx);}/* 处理所有数据 */for(inti0;ishm_buf-count;i){printf(端口%d: %.2f %ld\n,shm_buf-items[i].port_id,shm_buf-items[i].value,shm_buf-items[i].ts);}shm_buf-count0;pthread_mutex_unlock(shm_buf-mtx);}returnNULL;}intmain(void){/* 创建共享内存 */intshmidshmget(SHM_KEY,sizeof(shm_ring_t),IPC_CREAT|0666);shm_bufshmat(shmid,NULL,0);memset(shm_buf,0,sizeof(shm_ring_t));/* 初始化进程间互斥锁 */pthread_mutexattr_tmattr;pthread_mutexattr_init(mattr);pthread_mutexattr_setpshared(mattr,PTHREAD_PROCESS_SHARED);pthread_mutex_init(shm_buf-mtx,mattr);pthread_condattr_tcattr;pthread_condattr_init(cattr);pthread_condattr_setpshared(cattr,PTHREAD_PROCESS_SHARED);pthread_cond_init(shm_buf-cond,cattr);/* 启动采集线程 */pthread_ttids[MAX_PORTS];intport_ids[MAX_PORTS]{0,1,2};for(inti0;iMAX_PORTS;i){pthread_create(tids[i],NULL,collect_thread,port_ids[i]);}/* 启动处理线程 */pthread_tproc_tid;pthread_create(proc_tid,NULL,process_thread,NULL);/* 主线程等待 */for(inti0;iMAX_PORTS;i){pthread_join(tids[i],NULL);}pthread_join(proc_tid,NULL);shmdt(shm_buf);shmctl(shmid,IPC_RMID,NULL);return0;}九、工控IPC方案选择对比表场景推荐IPC原因多线程内部数据交换全局变量mutex同地址空间最简单最快高速数据流视频帧/ADC波形共享内存零拷贝最高带宽命令/指令传递命名管道有序可靠自带阻塞同步紧急事件通知信号(SIGUSR)最快响应异步触发结构化消息分发消息队列按类型读取灵活筛选跨网络通信Socket通用性强可跨机器选择原则内部用线程共享外部用IPC隔离。数据量大用共享内存命令传递用管道紧急事件用信号——三者搭配就是工控IPC的最佳组合。

相关新闻

【Bug已解决】FSDP + torch.nn.Parameter (MoE layer) lora fine-tuning doesn‘t work 解决方案

【Bug已解决】FSDP + torch.nn.Parameter (MoE layer) lora fine-tuning doesn‘t work 解决方案

【Bug已解决】FSDP torch.nn.Parameter (MoE layer) lora fine-tuning doesnt work 解决方案 一、现象长什么样 在做一个 MoE(混合专家)模型微调时,很多人会把多个专家存成一个 nn.ParameterList 或一个大的 torch.nn.Parameter 张量&#x…

2026/10/23 17:03:16 阅读更多 →
线性代数实战:齐次方程组 Ax=0、基础解系与 Python 零空间求解详解

线性代数实战:齐次方程组 Ax=0、基础解系与 Python 零空间求解详解

前言 在大学的线性代数课堂上,我们第一次接触到齐次线性方程组 A x = 0 Ax=0 Ax=0 时,也许只是为了应付考试而去反复做行初等变换。但如果你从事算法、AI、机器人控制或数据分析,你会发现这短短的一个公式,其实是现代工程中**寻找系统“隐藏自由度”**的核心利器。 今天我…

2026/10/27 3:00:33 阅读更多 →
LLM技术实战:从原理到企业级应用指南

LLM技术实战:从原理到企业级应用指南

1. 项目概述:为什么现在学习LLM正当时?过去一年里,大型语言模型(LLM)已经从实验室走向大众视野。从智能客服到代码生成,从创意写作到数据分析,这些拥有千亿级参数的"数字大脑"正在重塑…

2026/10/24 18:25:27 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/10/26 12:00:21 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/10/25 22:36:10 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/10/26 4:52:12 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/10/26 12:00:21 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/10/26 5:00:37 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/10/26 3:16:46 阅读更多 →

日新闻

周新闻

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/26 12:00:19 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/25 13:21:54 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/26 8:34:37 阅读更多 →