我们知道,进程的用户空间是互相独立的,一般而言是不能互相访问的,唯一的例外是共享内存区。但是,系统空间却是“公共场所”,所以内核显然可以提供这样的条件。所以进程之间交换数据时在内核进行,通俗的说A进程把数据拷贝到内核,B进程从内核中把数据读走,我们把内核提供的这种机制叫做进程间通信,(IPC)。
一、管道(pipe)
管道是Unix中最古老的进程间通信的形式。 我们把从一个进程连接到另一个进程的一个数据流称为一个“管道”。
管道是一种基本的ipc机制,由pipe函数创建,如下:
调用pipe函数时在内核中开辟一块缓冲区(管道)用于通信,它有一个读端和一个写端。
fd:文件描述符数组,其中fd[0]表示读端, fd[1]表示写端
pipe函数调用成功返回0,失败返回-1。
二、如何实现进程间通信
(1)首先父进程调用pipe开通管道,得到两个文件描述符指向管道的两端
(2)父进程通过fork创建子进程,那么同样的子进程也有两个文件描述符指向管道的两端
(3)父进程关闭fd[0],子进程关闭管道fd[1],这样父进程向管道写数据,子进程向管道读数据,这就实现了进程的通信.
注:图中0、1、2分别指的是标准输入,标准输出,标准出错
三、匿名管道和命名管道
1、匿名管道
值得说明的是,匿名管道只能实现亲缘关系进程之间通信,而不能实现任意两个本地进程之间的通信
使用,正如上图所示:
在父进程中,首先创建这个匿名管道,然后fork创建子进程,关闭子进程的读端,子进程只能进行写,子进程向这个匿名管道里面写数据,父进程向这个匿名管道里面读数据,以此来实现进程间的通信。
实现:
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<unistd.h>
#include<string.h>
#include<sys/wait.h>
int main()
{
int fds[2] ={0};
if(pipe(fds)<0)
{
perror("pipe");
return 1;
}
pid_t id =fork();
if(id==0)
{
//子进程写
close(fds[0]);
char *msg ="hello pipe,i am a child!";
while(1)
{
write(fds[1],msg,strlen(msg));
sleep(1);
}
exit(0);//退出
}
else{
//父进程读
close(fds[1]);
char buf[1024];
while(1)
{
ssize_t ret = read(fds[0],buf,sizeof(buf)-1);
if(ret>0)
{
buf[ret-1]=0;
printf("client->father:%s\n",buf);
}
}
pid_t tmp =wait(NULL);
}
return 0;
}
2、命名管道
如果想在不相关的进程之间通信,可以使用FIFO文件来做这项工作,它经常被称为命名管道 。
FIFO总是按照先进先出的原则工作,也就是第一个被写入的数据将首先从管道中读出 。
那么如何创建呢?
方法一:命名管道可以从命令行创建,方法是使用命令:
$ mkfifo filename
方法二:从在程序使用系统函数创建,函数有:
int mkfifo(const char *filename,mode_t mode);
以mkfifo为例:
int main(int argc, char *argv[])
{
mkfifo("p2", 0644);
return 0;
}
eg-用命名管道实现server&client通信 :
(1)、编写Makefife文件:
(2)、server.c用来读数据(读端)
(3)、client.c向管道写数据(写端):
结果: