进程地址空间
,所有进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区
,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区读走数据,内核提供的这种机制就是进程间通信(IPC),管道
是最基本的IPC机制。数据传输
:一个进程需要将它的数据发送给另一个进程资源共享
:多个进程之间共享同样的资源。通知事件
:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。进程控制
:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变管道
System V IPC
POSIX IPC
管道通信原理图:
具有亲缘关系
(如父子进程)的进程间通信,因为匿名管道无法被其他进程找到
,也就无法通信,所以只能通过子进程复制父进程的方法,让子进程能够访问到相同的管道来实现通信。#include <unistd.h>
int pipe(int fd[2])
参数
fd:文件描述符
返回值
成功返回0,失败返回错误代码。
pipe函数
创建管道,得到两个文件描述符fd[0]、fd[1]
指向管道的读端和写端
。父进程关闭管道写端,子进程关闭管道读端
。父进程可以从管道中读数据,子进程将从管道中写入数据。由于管道是利用环形队列实现的,数据从写端流入管道,从读端流出,这样就实现了进程间通信。#include<stdio.h>
#include<unistd.h>
#include<string.h>
#define SIZE 128
int main()
{
int pipefd[2]={0};
pipe(pipefd);
pid_t id=fork();
if(id==0)
{
close(pipefd[0]);
const char* msg="Hello World!\n";
while(1)
{
write(pipefd[1],msg,strlen(msg));
sleep(1);
}
}
else
{
close(pipefd[1]);
char buff[SIZE];
while(1)
{
ssize_t s=read(pipefd[0],buff,sizeof(buff)-1);
if(s>0)
{
buff[s]=0;
printf("fathrt get message:%s\n",buff);
sleep(1);
}
}
}
return 0;
}
运行结果:
如果所有指向管道写端的文件描述符都关闭了(管道写端引用计数为0),而仍然有进程从管道的读端读数据,那么管道中剩余的数据都被读取后,再次read会返回0,
就像读到文件末尾一样。
如果有指向管道写端的文件描述符没关闭(管道写端引用计数大于0),而持有管道写端的进程也没有向管道中写数据,这时有进程从管道读端读数据,那么管道中剩余的数据都被读取后,再次read会阻塞
,直到管道中有数据可读了才读取数据并返回。
如果所有指向管道读端的文件描述符都关闭了
(管道读端引用计数为0),这时有进程向管道的写端write,那么该进程会收到信号SIGPIPE,通常会导致进程异常终止
。当然也可以对SIGPIPE信号实施捕捉,不终止进程。具体方法信号章节详细介绍。
如果有指向管道读端的文件描述符没关闭(管道读端引用计数大于0),而持有管道读端的进程也没有从管道中读数据,这时有进程向管道写端写数据,那么在管道被写满时再次write会阻塞
,直到管道中有空位置了才写入数据并返回。
具有共同祖先的进程(具有亲缘关系的进程)之间进行通信
;通常,一个管道由一个进程创建,然后该进程调用fork,此后父、子进程之间就可应用该管道。流式服务
进程退出,管道释放
,所以管道的生命周期随进程
同步与互斥
半双工
的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道打破了匿名管道只能在有血缘关系的进程间的通信
。命名管道之所以可以实现进程间通信在于通过同一个路径名而看到同一份资源
,这份资源以FIFO的文件形式存在于文件系统中。open()和close()函数打开和关闭命名管道
。程序里创建,相关函数:
#include <sys/tyoes.h>
#include <sys/stat.h>
int mkfifo(const char* pathname, mode_t mode);
参数:
返回值:
//server.c
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
#include<unistd.h>
#define FIFO_FILE "./fifo"
int main()
{
umask(0);
if(mkfifo(FIFO_FILE ,0666)== -1)
{
perror("mkfifo error!\n");
return 1;
}
int fd=open(FIFO_FILE,O_RDONLY);
if(fd >=0)
{
char buf[64];
while(1)
{
ssize_t s = read(fd,buf,sizeof(buf)-1);
if(s>0)
{
buf[s]=0;
printf("client message is:%s",buf);
}
else if(s==0)
{
perror("client quit!\n");
break;
}
else
{
perror("read error!");
break;
}
}
}
return 0;
}
// client.c
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#define FIFO_FILE "./fifo"
int main()
{
int fd=open(FIFO_FILE,O_WRONLY);
if(fd >=0)
{
char buf[64];
while(1)
{
printf("Please enter message:");
fflush(stdout);
ssize_t s=read(0,buf,sizeof(buf)-1);
if(s>0)
{
buf[s]=0;
write(fd,buf,s);
}
}
}
return 0;
}
运行结果:
client 和 server 之间的通信
,在client上输入的内容即在server上显示
。client和server之间是两个没有任何关系的进程
,他们的pid 和 ppid都不相同
。打破了匿名管道只有在具有亲缘关系间通信的束缚
。命名管道可以从命令行上创建:
$ mkfifo filename
mkfifo 不仅是一个函数,还是一条命令
,不仅可以在函数内创建管道,还可以在命令行上创建命名管道,实现两个命令进程之间的通信。pipe函数
创建并打开。mkfifo函数创建
,打开用open。这5个PHP编程中的不良习惯,一定要改掉 PHP世界上最好的语言! 测试循环前数组是...
首先到这里下载其源码。里面东西挺多的,我们基本上可以把它放到两个文件夹就是...
今天看到个不错的网页播放器,感觉不错,大家可以测试 我写的一个播放器网页: ...
目录 读者基础 ?微服务架构梳理 https://www.coder4.com/homs_online/ ? ? 读者...
本文实例为大家分享了javascript实现倒计时提示框的具体代码,供大家参考,具体...
本文实例为大家分享了vue实现按钮切换图片的具体代码,供大家参考,具体内容如下...
由于固态驱动器(SSD)的速度比传统的硬盘驱动器(HDD)快得多,并且价格越来越便宜...
MFC项目在vs2017编译正常无报错,但是升级vs2019后一打开项目就报如下错误。 项...
在大三的时候,一直就想搭建属于自己的一个博客,但由于各种原因,最终都不了了...
目录 1. C语言文件接口(库函数) 1.1 fopen 1.2 fclose 1.3 fread 1.4 fwrite 1.5...