进程通信的几种方式之间通信都有哪些方式

Hi,欢迎来到中国嵌入式培训第一品牌 - 华清远见嵌入式学院,专注嵌入式工程师培养13年!
全国免费报名电话:400-706-1880
当前位置: >
> 进程间通信方式汇总
进程间通信方式汇总
时间:作者:华清远见
对于进程来说,进程的本质就是程序的执行过程,进程是独立运行的单位,所以不同的程序的执行就产生不同的进程,进程和进程之间,运行空间时相互独立的,以平常的方法无法实现两者之间的通信。
这里给出几种进程之间通信的方法可供参考学习: 管道、信号、IPC通信共享内存、消息队列、信号量)、套接口(socket)。
刚刚接触进程的学者,我们可以给以上的方法分类,同时分析每种通信方式的实现过程:
第一类:原始通信方式:最早的通信方式,理解起来简单、易懂。
(1)管道通信:实质是管道文件操作,分为有名管道和 无名管道两种。
无名管道 : 用在有亲缘关系进程之间通信,例如父子进程之间。通信方向单一,有固定的读端口fd[0](只能read()),固定的写端口fd[1](只能write()),如下图所示,构成一个半双工通道。
图1.无名管道
创建无名管道的方法:pipe()
包含头文件:#include
函数原型 : int pipe(int fd[2]);
fd: 整型的数组名数组长度2
返回值 : 成功 0 : fd[0] 读文件描述符 fd[1] 写文件描述符
int main()
int fd[2] = {0};
if(pipe(fd) == -1) // 注意1, pipe() 执行次数1次 放在 fork()前面
perror(&pipe&);
return -1;
id = fork(); // 创建一个子进程
if(id == 0) // 子进程
write(fd[1],&hello world!&,13); // 写管道
printf(&send OK\n&);
else if(id & 0) // 父进程
char buf[100] = {0};
read(fd[0], buf, 100); // 读管道
printf(&rcv : %s\n&, buf);
perror(&fork&);
有名管道: 用在任意两个进程之间通信,实质是两个进程同时访问一个管道文件,所有操作都属于文件IO。
创建有名管道方法:mkfifo()
头文件 :#include #include #include
函数原型 : int mkfifo(const char *filename, mode_t mode);
filename :管道文件的路径 相对路径 绝对路径
mode : 文件的访问权限
返回值 :成功 0
失败 -1 errno perror()
///////////////读进程
#define FIFO &MYFIFO&
int main()
char buf[100] = {0};
r = access(FIFO, F_OK); // 判断文件是否存在
if(r == -1) // 文件不存在
r = mkfifo(FIFO, 0666); // 创建管道文件
if(r == 0)
printf(&create FIFO\n&);
else //创建失败
perror(&mkfifo MYFIFO&);
else //文件已存在
printf(&%s 文件存在\n&, FIFO);
fd = open(FIFO, O_RDONLY); //打开文件
if(fd != -1)
r = read(fd, buf, 100); //阻塞读取信息
printf(&recv %d : %s\n&, r, buf);
close(fd); // 关闭文件
////////////////////////////////////写进程
#define FIFO &MYFIFO&
int main()
fd = open(FIFO, O_WRONLY); //打开文件
if(fd != -1)
{ //写入信息
write(fd, &hello&, strlen(&hello&)+1);
close(fd);
注意事项:管道通信, 两端(两个进程)通信, 要求 只有一端read() 方法, 另一端write()方法才有意义;read() 阻塞读取管道信息,write() 阻塞写入信息。
(2)信号:信号通信是唯一的异步通信,所有进程 默认接收所有信号
处理信号 : signal() 忽略信号 默认处理 捕捉信号(自定义处理方法)
发送信号 : 发送给目标进程 kill() 给自己 raise()
常见信号:
信号编号 信号名 默认意义 目的
2 SIGINT 按键 Ctrl+c 给终端上正在的进程 终止进程
3 SIGQUIT 按键 Ctrl+\ 给终端上正在的进程 终止进程
9 SIGKILL 杀死一个进程 (不能被忽略) 杀死进程
20 SIGTSTP 按键 Ctrl+z 终止进程
处理信号的函数:signal()
函数原型: #include
typedef void (*sighandler_t)(int); // 函数指针类型的重命名
函数原型 : sighandler_t signal(int signo, sighandler_t handler);
signo : 信号的编号
handler : SIG_IGN 忽略信号
SIG_DFL 默认处理
fun 自定义处理函数的首地址
返回值: 成功 首地址
发送信号的函数:kill() raise()
函数原型 : #include
int kill(pid_t id, int signo);
id : 进程号 & 0 向PID== id 进程发送信号
0 向同组的进程发送信号
-1 向进程表中所有进程(除去 PID最大 )发送信号
& -1 向进程组号 == |id|发送信号
signo : 信号的编号 或信号名
返回值: 成功 0
int raise(int signo);
signo : 信号编号
返回值: 成功 0
////////////////////接收信号的进程
void handler(int signo) // 自定义信号处理函数
if(signo == SIGQUIT) // 判断信号 是不是 SIGQUIT
printf(&input ctrl+\\ \n&);
else if(signo == SIGINT) // 判断信号 是不是 ctrl+c发送的
printf(&Ctrl +c\n&);
exit(0); //进程退出
int main()
signal(SIGINT, handler); // 设置信号处理方法
signal(SIGQUIT, handler);
printf(&PID = %d\n&, getpid());
////////发送信号的进程
int main(int argc, char *argv[])
if(argc & 3)
printf(&input: %s PID signo\n&, argv[0]);
pid_t id = atoi(argv[1]); // 获取参数 ---& 转成整型
int signo = atoi(argv[2]); // 信号编号 ---& 转成整型
kill(id, signo);
第二类:IPC通信方式, 通信的特点, 通过一个IPC对象,获取对应的ID从而实现通信
图2. IPC通信
获取IPC对象: 有亲缘进程之间, 例如 父子进程, IPC对象key 值 为IPC_PRIVATE
无关进程之间, key值 通过ftok() 获取:
方法: 函数原型: key_t ftok(const char *pathname, int idno);
pathname : 目录的路径 相对路径
idno : 取值 0 --- 255之间的数值
返回值:成功 键值 失败 -1
(1)共享内存
创建/打开共享内存-----& 映射------& 通信------& 解除映射 -----&删除共享内存
头文件:#include
创建/打开共享内存:
函数原型: int shmget(key_t key, int size, int shmflg);
key : 有亲缘关系进程 IPC_PRIVATE
无关进程 ftok() 获取键值
size : 共享内存的大小 字节数
shmflg : 共享内存的权限 和open() 的参数位相同
例子: key PIC_PRIVATE 0666 或者 0777 ....
ftok() IPC_CREAT|0666
返回值:成功 共享内存的段标识符
映射函数原型:
void *shmat(int shmid, const void *shmaddr, int shmflg);
shmid: 共享内存的段标志符
shmaddr : 计划映射的位置 == 首地址
NULL == 0 系统自动映射,找首地址
shmflg : 映射后空间的访问权限
SHM_RDONLY 只读
0 可读可写
返回值 :成功 映射区域的首地址
失败 (void *)-1
共享内存通信方法: 已知映射的首地址p 不阻塞读写------&效率高
读共享内存: printf(&%s\n&, p);
char buf[100]; strcpy(buf, p);
写共享内存: scanf(&%s&, p);
char buf[] = &jxkdadjlaj&; strcpy(p, buf);
解除映射 函数原型:
int shmdt(void *shmaddr);
shmaddr: 映射区域首地址
返回值: 成功 0
删除共享: int shmctl(int shmid, int cmd, struct shmid_ds *buf);
shmid : 共享内存的段标志符
cmd: IPC_RMID 删除对象 第3个参数 NULL
IPC_SET 设置共享内存的属性 第3个参数 新属性存放位置
IPC_STAT 获取共享内存的属性 第3个参数 属性存放位置
buf : 属性存放的首地址
返回值: 成功 0
int main()
shmid = shmget(IPC_PRIVATE, 100, 0666); // 创建/打开共享内存
if(shmid == -1)
perror(&shmget&);
printf(&shmget OK\n&);
system(&ipcs -m&); // 查看共享内存信息
id = fork(); // 创建 子进程
if(id == 0) // 在 子进程中 写入信息
p = shmat(shmid, 0, 0); // 映射
if(p == (char *)-1)
perror(&c: shmat&);
printf(&c: shmat OK\n&);
strcpy(p, &hello&); // 写共享内存
shmdt(p); // 解除映射
else if(id & 0) // 在 父进程 中 读信息
p = shmat(shmid, 0, 0); // 映射
if(p == (char *)-1)
perror(&f: shmat&);
printf(&f: shmat OK\n&);
sleep(1); // 先 写入 再读取信息
printf(&f : recv %s\n&, p); // 读共享内存
shmdt(p); // 解除映射
shmctl(shmid, IPC_RMID, NULL); // 删除共享内存
system(&ipcs -m&);
(2)消息队列
创建/打开消息队列 ---& 添加消息/取出消息 ----& 删除消息队列
头文件:#include
函数原型: int msgget(key_t key, int flag);
key : 键值 有亲缘关系进程 IPC_PRIVATE
无关 ftok()
flag : open()权限位 相同
IPC_CREAT | 0666
返回值:成功 消息队列ID
向消息队列中添加消息:msgsnd()
函数原型 : int smgsnd(int msgid, const void *buf, size_t size, int flag);
msgid : 消息队列ID
buf : 添加消息的存放位置
自定义结构体----&存放消息类型 + 正文
struct msgbuf
// 消息的类型, 使用者给定的含义
正文数据类型 text[N]; // 根据具体情况改变的
size : 正文的字节数
flag : 0 阻塞形式添加消息
IPC_NOWAIT 不阻塞添加
返回值: 成功 0
从消息队列中取出消息:msgrcv() 过滤
函数原型 int msgrcv(int smgid, void *buf, size_t size, long msgtype, int flag);
msgid : 消息队列ID
buf : 接收到消息的存放位置
size : 正文字节数
msgtype : 接收的消息类型 过滤条件
0 没有过滤,直接接收队列的第一个消息
& 0 只接收 消息队列中,第一个消息类型 == msgtype 的消息
& 0 只接收 消息类型 不大于 |msgtype| 并且最小的
flag : 0 阻塞接收
IPC_NOWAIT 不阻塞接收
返回值: 成功 接收信息的实际字节数
删除消息队列: msgctl
int msgctl(int msgid, int cmd, struct msgid_ds *buf);
msgid : 消息队列ID
cmd : IPC_RMID 删除对象 第3个参数 NULL
IPC_SET 设置属性 第3个 buf 属性存放位置
IPC_STAT 获取属性 第3个 buf 属性存放位置
buf : 属性存放首地址
返回值: 成功 0 ; 失败 -1
typedef struct msgbuf // 自定义消息的结构体类型
// 消息类型
char text[100]; // 正文
int main()
int msgid,r;
msgid = msgget(IPC_PRIVATE, 0666); // 创建/打开消息队列
if(msgid == -1)
perror(&msgget&);
return -1;
printf(&msgget OK %d\n&,msgid);
id = fork(); // 创建子进程
if(id == 0) // 子进程中 添加消息
a.type = 1; // 消息的具体内容
strcpy(a.text, &hello world!&);
r = msgsnd(msgid, &a, sizeof(a.text), 0); // 阻塞形式添加到消息队列中
if(r == -1)
perror(&msgsnd &);
printf(&msgsnd OK r = %d\n&,r);
else if(id & 0) // 父进程
r = msgrcv(msgid, &b, sizeof(b.text),1,0); // 取出第一个 消息类型== 1的消息
if(r == -1)
perror(&msgrcv&);
printf(&r = %d,type = %ld %s\n&, r, b.type,b.text); // 打印 消息信息
msgctl(msgid, IPC_RMID, NULL); // 删除消息队列
(3)信号量 :保护临界资源----& 进程之间实现互斥, 信号量常常修饰共享内存
创建/打开信号量---& 初始化信号量(执行 1 次)----& P操作 ----& V操作 ---&删除
头文件:#include
创建一个信号量: semget()
函数原型: int semget(key_t key, int num, int semflg);
key : 键值 亲缘关系进程 IPC_PRIVATE
无关进程 ftok()
num : 信号量的个数 一般 1
semflg : 权限 亲缘进程 0666 ....
无关进程 IPC_CREAT
IPC_EXCL 唯一的信号量, 如果已经存在,则返回错误
返回值: 成功 信号量ID
函数原型: semctl()
int semctl(int semid, int semnum, int cmd, union semun arg);
semid : 信号量ID
semnum : 信号量的编号 一般 0 : 第一个信号量
cmd : IPC_STAT 获取这个semnum编号信号量的结构
IPC_RMID 删除信号量对象
IPC_SETVAL 设置信号量的值 val , 初始化 SETVAL
IPC_GETVAL 获取信号量的值
arg : 信号量相关结构 变量 , union 复用
自定义复用类型: union semun
struct semid_ds *
unsigned short *
返回值: 成功 0; 失败 -1
函数原型:semop()
int semop(int semid, struct sembuf *sp, size_t ns);
semid : 信号量ID
sp : 结构体变量的地址
系统定义好的结构体
struct sembuf
short sem_ 信号量的编号 第一个信号量 0
short sem_ -1 P操作 ; 1 V操作
short sem_flg: 一般 SEM_UNDO
ns : 操作个数 一般 1
返回值: 成功 0 ; 失败 -1
一般开发者会自定义信号量的相关函数,如下:
a) 初始化信号量 : 自定义一个初始化函数 init_sem()
int init_sem(int semid, int no, int value)
// 复用变量
a.val = // 初值
if(semctl(semid, no, SETVAL, a) == -1) // 设置信号量的值
return -1;
调用: init_sem(semid, 0, 1);
b) P操作: 自定义一个P操作函数
int p_sem(int semid, int num)
mybuf.sem_num =
mybuf.sem_op = -1;
mybuf.sem_flg = SEM_UNDO;
if(semop(semid, &mybuf, 1) & 0)
perror(&semop&);
c) V操作: 自定义一个V操作函数
int v_sem(int semid, int num)
mybuf.sem_num =
mybuf.sem_op = 1;
mybuf.sem_flg = SEM_UNDO;
if(semop(semid, &mybuf, 1) & 0)
perror(&semop&);
d) 删除信号量: 自定义一个删除信号量函数
int delete_sem(int semid,int no)
// 复用变量
if(semctl(semid, no, IPC_RMID, a) == -1)
return -1;
#include&mysem.h&
int main()
semid = semget(IPC_PRIVATE, 1, 0666); // 创建信号量
if(semid != -1)
printf(&semget OK\n&);
if(init_sem(semid, 0, 1) != -1) // 初始化信号量
printf(&init_sem OK\n&);
id = fork(); // 创建子进程
if(id == 0)
p_sem(semid, 0); // P操作
printf(&child running\n&);
v_sem(semid, 0); // V操作
else if(id & 0)
p_sem(semid, 0); //P操作
printf(&father running\n&);
v_sem(semid, 0); // V操作
delete_sem(semid, 0); // 删除信号量
第三类:socket套接口通信方式, 对于socket常用来实现网络中不同主机之间的进程间通信。这是另一个知识点,对于初学者只要掌握前5中通信方式,就能实现本机进程之间的通信。
评论列表(网友评论仅供网友表达个人看法,并不表明本站同意其观点或证实其描述)
学院最新动态几种进程间的通信方式&比较
# 管道( pipe
):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
# 有名管道 (named pipe) :
有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
# 信号量( semophore ) :
信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
# 消息队列( message queue ) :
消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
# 信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
# 共享内存( shared memory )
:共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC
方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。
# 套接字( socket ) :
套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。
1,管道:根植于文件系统,管道一端读,一端写,只可以在父子进程间共享。父子进程有同一个文件标号(内核中file数组的下标),但是指向的file结构是不同的(file结构和具体的进程相关)。而file结构又指向一个dentry结构,这个dentry结构是同一个,dentry结构又指向一个inode结构(以上都是在内核中)。父进程发送消息,系统从用户态转到内核态,内核调用inode结构中的函数操作指针(具体的函数实现由虚拟文件系统下面的具体的文件系统实现),数据写入inode结构的缓存。内核在找到子进程的task_struct结构,添加标志告诉子进程有数据发送过来了,子进程由阻塞进度就绪。此时父进程进入就绪阶段。内核根据调度算法找到一个进程执行。不久,子进程被选择执行,同样的流程找到inode结构,读取缓存数据。这样,一个完整的进程通信完成了。
2,命名管道:匿名管道是根据相同的文件fd找到对方,而命名管道是根据相同的文件名字。因此只要预定了文件名就可以通信,从找到file结构开始,过程都类似。都是根植于虚拟文件系统。不同于块文件和字符文件,命名管道是一种特殊的文件系统FIFO队列,因此不可以定位指针。
3,信号:信号时一种异步的通信方式。一个进程向另一个进程发送信号,系统从用户态进入内核态,内核根据接收进程的id找到其task_struct,把发送的信号放入接收进程队列。若干时刻过后,接收进程运行,运行一段时间后,接收进程需要用到内核服务,所以通过系统调用(异常或者中断)进入内核态,内核首先找到进程的信号队列,进入用户态,处理信号处理程序。处理完之后,再次进入内核态,找到信号处理前的指令,一切恢复正常,就好像没有信号处理一样。信号处理程序和主程序就好像是两个不同线程,共享进程的用户空间,却没有方法同步。信号处理程序不可能中断执行阻塞然后又转到主程序执行。信号处理程序的优先级高,但是指向条件苛刻。
4,共享内存:共享内存其实是不同进程的同一个物理页面,虽然虚拟内存地址不同。甲进程往共享内存写数据,乙进程也可以访问。共享内存可以不涉及内核操作,但是需要同步和通知控制。比如可以用信号来实现通知。
5,报文传递:不同于管道通信,每个进程都有一个报文队列,而每个报文都是一个有结构的数据。相对来说,管道只是一个字符流。管道通信缓冲区满之后,发送进程就会阻塞,报文不会。报文不是根植于文件系统。
6,信号量:很简单的通信方式,再访问一段内存的时候查看某一个信号是否可以访问,访问,关闭,保证同一时刻只有一个进程访问这段内存。如此......
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。&>&&>&windows进程间通信几种方式demo源码集合
进程间通信的几种方式:
共享内存,匿名管道,命名管道,邮槽,剪贴板
vs2010 实现
所需积分:14
下载次数:78
资源大小:984B
上传时间:
上传者:qiulanzhu所需积分:6
下载次数:46
资源大小:30.08MB
上传时间:
上传者:qq_所需积分:2
下载次数:44
资源大小:45.23MB
上传时间:
上传者:qq_所需积分:2
下载次数:55
资源大小:45.25MB
上传时间:
上传者:qq_所需积分:2
下载次数:67
资源大小:13.17MB
上传时间:
上传者:qq_所需积分:2
扫描微信二维码精彩活动、课程更新抢先知
$album->title ?>
所需积分:14
剩余积分:
您当前C币:0
可兑换下载积分: 0
兑换下载分:
兑换失败,您当前C币不够,请先充值C币
消耗C币:0
你当前的下载分为 。前去下载资源
VIP会员免积分下载
会员到期时间:
剩余下载次数:
你还不是VIP会员
开通VIP会员权限,免积分下载
所需积分:14
剩余积分:
你下载资源过于频繁,请输入验证码
您因违反CSDN下载频道规则而被锁定帐户,如有疑问,请联络:!
积分不够下载该资源
如何快速获得积分?

我要回帖

更多关于 进程通信的几种方式 的文章

 

随机推荐