news 2026/8/29 12:27:39

进程间的通信

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
进程间的通信

一、概念

进程间通信(Inter-Process Communication,IPC)是指在不同进程之间进行信息交换和同步的机制。

二、实现方式

1、管道

(1)无名管道

原理:它是一种半双工的通信方式,数据只能单向流动,并且只能在具有亲缘关系(如父子进程)的进程之间使用。无名管道在内核中通过一个环形缓冲区来实现,管道的两端分别是读端和写端。
特点:创建简单,使用方便,但功能相对有限,数据传输的方向性强,适用于亲缘关系进程间的简单数据传递

进程间的通信方式——pipe(管道)-CSDN博客

man 2 pipe

int pipe(int pipefd[2]);

功能:

创建操作管道的两个文件描述符

参数:

pipefd: 存放文件描述符的数组

pipefd[0]:读文件描述符,用于从管道中读取数据。

pipefd[1]:写文件描述符,用于向管道中写入数据。

返回值:

成功返回0

失败返回-1

#include <stdio.h> #include "public.h" char tmpbuff[4096] = {0}; int main(int argc, const char **argv) { pid_t pid; int pipefd[2]; int ret = 0; ret = pipe(pipefd); if (-1 == ret) { ERR_MSG("fail to pipe"); return -1; } pid = fork(); if (-1 == pid) { ERR_MSG("fail to fork"); return -1; } if (0 == pid) { close(pipefd[0]); //用不到读,所以先关掉(如上图3) strcpy(tmpbuff, "hello world"); write(pipefd[1], tmpbuff, strlen(tmpbuff)); //将 tmpbuff 中的数据写入管道。 close(pipefd[1]); //写用完了,关掉 } else if (pid > 0) { close(pipefd[1]); //用不到写,所以先关掉(如上图3) read(pipefd[0], tmpbuff, sizeof(tmpbuff)); //将管道中的数据读到 tmpbuff 中。 printf("tmpbuff = %s\n", tmpbuff); close(pipefd[0]); //读完了,关掉 } return 0; }

(2)有名管道

原理:它是一种特殊的文件,在文件系统中以文件名的形式存在,突破了无名管道只能在亲缘关系进程间通信的限制,不同进程可以通过该文件名来访问管道进行通信。
特点:有自己的文件路径,可以在不相关的进程间使用,实现了不同进程在文件系统层面的通信接口。

man 3 mkfifo

int mkfifo(const char *pathname, mode_t mode);

功能:

创建管道文件

参数:

pathname:管道文件名

mode:权限

返回值:

成功返回0

失败返回-1

用有名管道实现单工通信

send.c文件

#include <stdio.h> #include "public.h" int main(int argc, const char **argv) { int fd = 0; char tmpbuff[1024] = {0}; int ret = mkfifo("/tmp/myfifo", 0777); if(-1 == ret && errno != EEXIST) //EEXIST:它表示在尝试创建文件(或目录)时,目标文件(或目录)已经存在。 //errno != EEXIST表示错误原因不是因为,文件已经存在了 { perror("mkfifo error"); return -1; } fd = open("/tmp/myfifo", O_RDWR); if (-1 == fd) { ERR_MSG("fail to open"); return -1; } gets(tmpbuff); write(fd, tmpbuff, strlen(tmpbuff)); close(fd); return 0; }

recv.c文件

#include <stdio.h> #include "public.h" int main(int argc, const char **argv) { int fd = 0; char tmpbuff[1024] = {0}; int ret = mkfifo("/tmp/myfifo", 0777); if(-1 == ret && errno != EEXIST) //EEXIST:它表示在尝试创建文件(或目录)时,目标文件(或目录)已经存在。 //errno != EEXIST表示错误原因不是因为,文件已经存在了 { perror("mkfifo error"); return -1; } fd = open("/tmp/myfifo", O_RDWR); if (-1 == fd) { ERR_MSG("fail to open"); return -1; } read(fd, tmpbuff, sizeof(tmpbuff)); printf("RECV:%s\n", tmpbuff); close(fd); remove("myfifo"); //读完删除管道文件 return 0; }

编译send.c文件生成可执行文件send

编译recv.c文件生成可执行文件recv

执行send文件向管道中输入hello

与此同时在执行当中的recv文件会从管道中接收到hello并打印出来

(3)管道的特点

管道本质是内核中的一段缓冲区,默认64K字节,遵循队列存储方式(FFO)。
1.写阻塞:当管道的读写端都存在时,管道写满数据时,再写会发生写阻塞。
2.读阻塞:当管道的读写端都存在时,管道为空时,再读管道会发生读阻塞。
3.读返回0:当管道没有写端,只有读端时,管道中有数据则读出数据,无数据时,不再读阻塞,读管道(read),函数的返回值返回0。
4.管道破裂:当管道没有读端,向管道中写数据,发生管道破裂异常

用有名管道实现半双工通信

send.c文件

#include <stdio.h> #include "public.h" int main(int argc, const char **argv) { int fatob = 0; int fbtoa = 0; char tmpbuff[4096] = {0}; mkfifo("/tmp/ATOB", 0777); mkfifo("/tmp/BTOA", 0777); fatob = open("/tmp/ATOB", O_WRONLY); //send.c文件中打开写端,等待recv.c文件打开读端口,管道才会打开成功 if (-1 == fatob) { ERR_MSG("fail to open"); return -1; } fbtoa = open("/tmp/BTOA", O_RDONLY); //同理 if (-1 == fbtoa) { ERR_MSG("fail to open"); return -1; } printf("管道打开成功!\n"); while (1) { gets(tmpbuff); write(fatob, tmpbuff, strlen(tmpbuff)); memset(tmpbuff, 0, sizeof(tmpbuff)); read(fbtoa, tmpbuff, sizeof(tmpbuff)); printf("RECV:%s\n", tmpbuff); } close(fatob); close(fbtoa); return 0; }

recv.c文件

#include <stdio.h> #include "public.h" int main(int argc, const char **argv) { int fatob = 0; int fbtoa = 0; char tmpbuff[4096] = {0}; mkfifo("/tmp/ATOB", 0777); mkfifo("/tmp/BTOA", 0777); fatob = open("/tmp/ATOB", O_RDONLY); if (-1 == fatob) { ERR_MSG("fail to open"); return -1; } fbtoa = open("/tmp/BTOA", O_WRONLY); if (-1 == fbtoa) { ERR_MSG("fail to open"); return -1; } printf("管道打开成功!\n"); while (1) { memset(tmpbuff, 0, sizeof(tmpbuff)); read(fatob, tmpbuff, sizeof(tmpbuff)); printf("RECV:%s\n", tmpbuff); gets(tmpbuff); write(fbtoa, tmpbuff, strlen(tmpbuff)); } close(fatob); close(fbtoa); return 0; }

用有名管道实现全双工通信

老王.c

#include <stdio.h> #include "public.h" int fatob = 0; int fbtoa = 0; void *sendfun(void *arg) { char tmpbuff[4096] = {0}; while (1) { gets(tmpbuff); if (0 == strcmp(tmpbuff, ".quit")) { break; } write(fbtoa, tmpbuff, strlen(tmpbuff)); } close(fbtoa); return NULL; } void *recvfun(void *arg) { char tmpbuff[4096] = {0}; ssize_t nret = 0; while (1) { memset(tmpbuff, 0, sizeof(tmpbuff)); nret = read(fatob, tmpbuff, sizeof(tmpbuff)); if (0 == nret) { break; } printf("RECV:%s\n", tmpbuff); } return NULL; } int main(int argc, const char **argv) { pthread_t tid_send; pthread_t tid_recv; char tmpbuff[4096] = {0}; mkfifo("/tmp/ATOB", 0777); mkfifo("/tmp/BTOA", 0777); fatob = open("/tmp/ATOB", O_RDONLY); if (-1 == fatob) { ERR_MSG("fail to open"); return -1; } fbtoa = open("/tmp/BTOA", O_WRONLY); if (-1 == fbtoa) { ERR_MSG("fail to open"); return -1; } printf("管道打开成功!\n"); pthread_create(&tid_send, NULL, sendfun, NULL); pthread_create(&tid_recv, NULL, recvfun, NULL); pthread_join(tid_send, NULL); pthread_join(tid_recv, NULL); #if 0 while (1) { gets(tmpbuff); write(fatob, tmpbuff, strlen(tmpbuff)); memset(tmpbuff, 0, sizeof(tmpbuff)); read(fbtoa, tmpbuff, sizeof(tmpbuff)); printf("RECV:%s\n", tmpbuff); } #endif close(fatob); return 0; }

老李.c

#include <stdio.h> #include "public.h" int fatob = 0; int fbtoa = 0; void *sendfun(void *arg) { char tmpbuff[4096] = {0}; while (1) { gets(tmpbuff); if (0 == strcmp(tmpbuff, ".quit")) { break; } write(fatob, tmpbuff, strlen(tmpbuff)); } close(fatob); return NULL; } void *recvfun(void *arg) { char tmpbuff[4096] = {0}; ssize_t nret = 0; while (1) { memset(tmpbuff, 0, sizeof(tmpbuff)); nret = read(fbtoa, tmpbuff, sizeof(tmpbuff)); if (0 == nret) { break; } printf("RECV:%s\n", tmpbuff); } return NULL; } int main(int argc, const char **argv) { pthread_t tid_send; pthread_t tid_recv; char tmpbuff[4096] = {0}; mkfifo("/tmp/ATOB", 0777); mkfifo("/tmp/BTOA", 0777); fatob = open("/tmp/ATOB", O_WRONLY); if (-1 == fatob) { ERR_MSG("fail to open"); return -1; } fbtoa = open("/tmp/BTOA", O_RDONLY); if (-1 == fbtoa) { ERR_MSG("fail to open"); return -1; } printf("管道打开成功!\n"); pthread_create(&tid_send, NULL, sendfun, NULL); pthread_create(&tid_recv, NULL, recvfun, NULL); pthread_join(tid_send, NULL); pthread_join(tid_recv, NULL); #if 0 while (1) { gets(tmpbuff); write(fatob, tmpbuff, strlen(tmpbuff)); memset(tmpbuff, 0, sizeof(tmpbuff)); read(fbtoa, tmpbuff, sizeof(tmpbuff)); printf("RECV:%s\n", tmpbuff); } #endif close(fbtoa); return 0; }

2、信号

(1)概念

进程间通知机制(不携带数据)

信号可以看作是一种轻量级的软件中断,它能够在进程运行过程中异步地通知进程某一特定事件已经发生。当信号发送给进程时,进程会暂停当前正在执行的任务,转而去处理该信号对应的事件。处理完毕后,进程可能会继续执行之前暂停的任务,也可能根据信号的处理结果做出其他操作。例如,在 Linux 系统中,用户按下Ctrl + C组合键,会产生一个SIGINT信号发送给当前正在运行的前台进程,通知该进程要终止运行。

1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP 21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR 31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3 38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8 43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2 63) SIGRTMAX-1 64) SIGRTMAX 2) SIGINT:ctrl + c 让一个进程被打断 3) SIGQUIT:ctrl + \ 让一个进程结束 9) SIGKILL: 强制让一个进程结束 11)SIGSEGV: 让一个进程结束(段错误) 13)SIGPIPE: 让一个进程结束(管道破裂) 14)SIGALRM: 让一个进程结束(定时时间到达) 17)SIGCHLD: 子进程结束时发送给父进程 18)SIGCONT: 让停止态的进程继续执行 19)SIGSTOP: 让运行态的进程进入停止态(暂停)强制停止 20)SIGTSTP: ctrl + z 让进程进入暂停态,后台进程 来自终端的停止信号 10 SIGUSR1 12 SIGUSR2 专门预留给程序员使用的未定义信号。

(2)注册信号

sighandler_t signal(int signum, sighandler_t handler);
功能:注册一个信号
参数:
signum :要注册的信号的编号
handler:对信号的处理方式
SIG_IGN : 按忽略方式处理
SIG_DFL :按照缺省方式处理
自定义的函数:按照捕获方式处理
返回值:
成功:handler
失败:SIG_ERR

信号处理方式:
1. 忽略 :不处理
2. 缺省 :按照操作系统默认方式处理
3. 捕获 :按照自定义方式处理

#include <stdio.h> #include <unistd.h> #include <signal.h> void handler(int signum) { printf("%d signal comming\n", signum); } int main(void) { // signal(SIGINT, SIG_IGN); //信号的处理方式一: 不处理,即不对SIGINT信号做处理,终端一直打印hello world,按下ctrl+c进程结束不了。 // signal(SIGINT, SIG_DFL); //方式二:按照操作系统默认方式处理,终端一直打印hello world,按下ctrl+c进程结束。 signal(SIGINT, handler); //方式三:按照自定义方式处理,自定义方式遵循handler函数执行,终端一直打印hello world,按下ctrl+c进程不结束,打印一句signal comming后继续打印hello world。 while (1) { printf("hello world\n"); sleep(1); } return 0; }

3、消息队列

4、共享内存

5、信号灯

6、套接字

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/29 12:26:59

REDRIVER2:用C语言重写《Driver 2》的开源逆向项目

还记得 PlayStation 时代那些用光盘才能玩到的经典作品吗&#xff1f;《Driver 2》就是其中一款让无数老玩家念念不忘的赛车动作游戏——它允许你中途下车、劫车、在城市里自由穿梭&#xff0c;这种“开放式驾驶”设计在 2000 年可以说相当超前。但 20 多年过去&#xff0c;你想…

作者头像 李华
网站建设 2026/8/29 12:26:45

10行代码接入世界模型:QQWorld双查询机制解析与实战

看到世界模型&#xff08;World Model&#xff09;相关的工作越来越多&#xff0c;很多同学在复现时都被环境搭建、状态空间设计、模型训练开销劝退了。近期西安交通大学团队开源的 QQWorld 引起了不少关注&#xff0c;它的宣传点非常直接&#xff1a;仅需 10 行代码&#xff0…

作者头像 李华
网站建设 2026/8/29 12:19:28

美赛数学建模实战指南:从组队到论文的96小时极限挑战

1. 从旁观到入局&#xff1a;我眼中的美赛到底是什么&#xff1f; 第一次听说“美赛”&#xff08;美国大学生数学建模竞赛&#xff0c;MCM/ICM&#xff09;的时候&#xff0c;我还在大二&#xff0c;看着学长学姐们熬了四天三夜后蓬头垢面的样子&#xff0c;心里既好奇又有点发…

作者头像 李华
网站建设 2026/8/29 12:19:21

分治算法精讲:从归并排序到快速幂,掌握高效问题解决范式

1. 项目概述&#xff1a;分治策略的核心思想 “分治”这个词听起来挺玄乎&#xff0c;但它的核心思想其实非常朴素&#xff0c;就像我们处理生活中的复杂问题一样。想象一下&#xff0c;你要整理一个杂乱无章、堆满各种物品的大仓库。如果试图一次性把所有东西都归位&#xff0…

作者头像 李华
网站建设 2026/8/29 12:17:48

生成式AI应用入门完整教程:21节课从提示词写到AI代理

生成式AI应用入门完整教程&#xff1a;21节课从提示词写到AI代理 【免费下载链接】generative-ai-for-beginners 21 Lessons, Get Started Building with Generative AI 项目地址: https://gitcode.com/GitHub_Trending/ge/generative-ai-for-beginners 对着聊天框改了…

作者头像 李华
网站建设 2026/8/29 12:14:11

AI项目的“返航系统”:从目标定义到可观测性,让系统不偏航

去年在调试一个多智能体协作系统时&#xff0c;我盯着满屏的日志&#xff0c;发现系统在某个节点突然偏离了用户意图&#xff0c;不再回答问题&#xff0c;而是开始自己编造任务。那一瞬间&#xff0c;我脑子里冒出来的不是某个技术文档&#xff0c;而是《奥德赛》&#xff1a;…

作者头像 李华