要想进程间通信,数据交换,必须通过内核;
一个进程将数据写到内核,然后另一个进程从内核读走数据。
IPC:进程间通信(interprocess communication)
通信方式:
- 管道
- 信号
- 共享映射区(无血缘关系)
- 本地套接字
管道:
概念:
管道是一种最基本的IPC机制,也称匿名管道,应用于有血缘关系的进程之间,完成数据传递。
特质:
- 管道的本质是一块内核缓冲区
- 由两个文件描述符引用,一个表示读端,一个表示写段
- 规定数据从管道的写端流入管道,从读端流出
- 当两个进程都终结的时候,管道也自动消失
- 管道的读端和写端默认都是阻塞的(写端写满写不进阻塞,读端读不到数据阻塞)
原理:
- 管道的实质是内核缓冲区,内部使用环形队列实现
- 默认缓冲区大小是4K,可以使用ulimit -a命令获取大小
- 实际操作时缓冲区会根据数据压力做适当调整
局限性:
- 数据一旦被读走,便不在管道中存在,不可反复读取
- 数据只能在一个方向上流动,若要实现双向流动,必须使用两个管道
- 只能在有血缘关系的进程间使用管道
创建管道-pipe函数:
- 作用:创建一个管道
- 函数原型: int pipe(int pipefd[2]);也可以写int pipe(int *pipefd)写成pipefd[2]是为了说明传两个fd。
- 参数:若调用成功,f[0]放读端,f[1]放写端
- 返回值:成功返回0 ;失败返回-1,并设置error值
步骤:
- 父进程调用pipe函数创建管道,得到两个文件描述符fd[0]和fd[1]
- 父进程调用fork函数创建子进程,那么子进程也有两个文件描述符指向同一个管道
- 父进程关闭管道读端,子进程关闭通道写端。父进程写,子进程读
结果:
父子进程完成管道命令:
如:ps -aux | grep bash
文件描述符表:
标准输出是写到终端,标准输入是从终端读
思路:我们可以使用:dup2(fd[1],STDOUT_FILENO)函数,写到管道的写端
dup2(fd[0],STDIN_FILENO)函数,从管道的读端读
结果:
兄弟进程完成管道命令:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<unistd.h>
#include <sys/wait.h>
int main()
{
int i=0;
int fd[2];
int ret=pipe(fd);
if(ret<0)
{
perror("pipe error");
return -1;
}
for(;i<2;i++)
{
pid_t pid=fork();
if(pid<0)
{
perror("fork error");
return -1;
}
else if(pid>0)
{
printf("father:pid=[%d]\n",getpid());
sleep(1);
}
else if(pid==0)
{
printf("child:pid=[%d],fpid=[%d]\n",getpid(),getppid());
break;
}
}
if(i==2)
{
close(fd[0]);
close(fd[1]);
int wstatus;
while(1)
{
pid_t wpid=waitpid(-1,&wstatus,WNOHANG);
if(wpid==0)
{
continue;
}
else if(wpid>0)
{
if( WIFEXITED(wstatus))
{
printf("child[%d],return=[%d]\n",wpid,WEXITSTATUS(wstatus));
}
}
else if(wpid==-1)
{
printf("no child is living\n");
break;
}
}
}
else if(i==0)
{
close(fd[0]);
dup2(fd[1],STDOUT_FILENO);
// close(fd[1]);
execlp("ps","ps","-aux",NULL);
perror("execlp error");
exit(1);
}
else if(i==1)
{
close(fd[1]);
dup2(fd[0],STDIN_FILENO);
// close(fd[0]);
execlp("grep","grep","bash",NULL);
perror("execlp error");
exit(1);
}
}
注意点:要关闭父进程的管道读写端口,不然会一直阻塞。
结果: