目录
目录
简介
不同进程之间资源是独立的,不能在一个进程里直接访问另一个进程的资源。他们的信息交互和状态传递需要进程间通信(IPC)。
1.1通信目的:
数据传输:传输数据
通知事件:如子进程终止时要通知父进程
资源共享:需要内核提供互斥和同步机制
进程控制:如Debug进程需要控制拦截另一个进程,并能够及时知道它的状态改变
1.2通信方式
1.3管道
(匿名)管道:所有UNIX都支持这种通信机制,
如统计一个目录里文件的数目:ls | wc -l 中间的| 是管道符,有两个进程分别执行ls wc,将ls得到的信息传给wc
(有/无名)管道特点:
1)内核内存里维护的缓冲器,存储能力有限
2)拥有文件特质:读,写,匿名管道没有文件实体,有名管道有但不存储数据。 可以执行文件操作
3)一个管道是一个字节流,所以可以不管写入数据块大小,读取任意大小的数据块。
4)管道传递数据是顺序的。传入啥样传出就是啥顺序。(先入先出)
5)管道是单向的,半双工通信。
6)从管道读数据是一次性操作,数据一旦被读就从管道中抛弃,释放空间以写数据。无法使用lseek()随机访问数据。
7)匿名管道用于有亲缘关系的进程。(因为如父子进程fd指向同一个匿名管道)。
8)默认是阻塞的,如果没有数据,等着写,满了,等着收。
逻辑上,管道是一个环形结构。
匿名管道使用 <unistd.h>
int pipe(int pipefd[2]) 创建一个匿名管道 成功返回0,失败-1
pipefd[2]是一个传出参数 pipedfd[0]对应读端,pipefd[1]对应写。
ulimit -a 查看pipe size 默认 8*512bytes =4k=4096
先管道,再fork()。
父子进程读写案例
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
using namespace std;
int main()
{
//先管道,再fork
int pipefd[2];
int ret=pipe(pipefd);
if(ret==-1)
{
perror("pipe:");
exit(0);
}
pid_t pid=fork();
if(pid>0)
{ //父进程
char readbuf[1024]={0};
close(pipefd[1]);//关闭写端
while (1)
{
read(pipefd[0],readbuf,sizeof(readbuf));
cout<<"parent "<<getpid()<<" receive : "<<readbuf<<endl;
bzero(readbuf,1024);//清除buf上次读到的内容
}
}
else if(pid==0)
{ //子进程
char readbuf[1024]={0};
close(pipefd[0]);//关闭读端
while(1)
{
cout<<"i am "<<getpid()<<endl;
char wrbuf[1024]="i am child";
write(pipefd[1],wrbuf,strlen(wrbuf));
sleep(1);
}
}
return 0;
}
读写时注意:
读管道:
管道中有数据,read返回实际读到字节数
管道无数据,
写端计数=0,读完后再读read返回0,类似文件末尾
写端计数>0,读完后再读阻塞等待。
写管道:
管道没满,write返回实际写入字节数。
管道满了
读端计数=0,写满后进程收到SIGPIPE信号,进程异常终止
读端计数>0,wr写满后阻塞等待。
子/进程或父进程在另一方不写/写满时会处于阻塞状态,即read/write之后的内容不再继续运行。而设置非阻塞后,read write没成功也可以继续向下运行。
设置管道非阻塞
int flags=fcntl(fd[0],F_GETFL);
flags|=O_NONBLOCK;
fcntl(fd[0],F_SETFL,flags);
#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
using namespace std;
int main()
{
//先管道,再fork
int pipefd[2];
int ret=pipe(pipefd);
if(ret==-1)
{
perror("pipe:");
exit(0);
}
pid_t pid=fork();
if(pid>0)
{ //父进程
char readbuf[1024]={0};
close(pipefd[1]);//关闭写端
//设置非阻塞
int flags=fcntl(pipefd[0],F_GETFL);
flags|=O_NONBLOCK;
fcntl(pipefd[0],F_SETFL,flags);
while (1)
{
read(pipefd[0],readbuf,sizeof(readbuf));
cout<<"parent "<<getpid()<<" receive : "<<readbuf<<endl;
bzero(readbuf,1024);//清除buf上次读到的内容
sleep(1) ;
}
}
else if(pid==0)
{ //子进程
char readbuf[1024]={0};
close(pipefd[0]);//关闭读端
while(1)
{
cout<<"i am "<<getpid()<<endl;
char wrbuf[1024]="i am child";
write(pipefd[1],wrbuf,strlen(wrbuf));
sleep(2);
}
}
return 0;
}
有名管道 FIFO
提供了一个路径名,以FIFO的文件形式存在于文件系统中,所以无亲缘关系的进程只要能够访问该路径,就可以彼此通过FIFO通信。 也有一个写入端,读取端,先入先出。被读后数据被抛弃,不支持lseek等文件定位操作。
和pipe区别:
FiFO是文件,但内容存在内存里
使用FIFO的进程退出后,FIFO文件将继续保存在文件系统里便以后使用
FIFO有名字,可以使不相关进程通信。
FIFO使用
1)命令行mkfifo
2)int mkfifo(const char* pathname,mode_t mode)创建
mode和open的一样8进制数, 都是文件权限
创建后就可以用操作文件的open close read等操作
fifo通信
写fifo
#include <sys/types.h>
#include <cstring>
#include <sys/stat.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string>
using namespace std;
int main()
{
int ret=access("fifo2",F_OK);//判断文件是否存在
if(ret==-1)
{
printf("create fifo\n");
ret=mkfifo("fifo2",0664);//不存在 创建
if(ret==-1)
{
perror("mkfifo");
exit(0);
}
}
int fd= open("fifo2",O_WRONLY);
if(fd==-1)
{
perror("write open");
exit(0);
}
for(int i=0;i<100;i++)
{
char buf[1024];
sprintf(buf,"i am %d",i);
write(fd,buf,strlen(buf));
sleep(1);
}
close(fd);
return 0;
}
读fifo
#include <sys/types.h>
#include <cstring>
#include <sys/stat.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string>
using namespace std;
int main()
{
int ret=access("fifo2",F_OK);//判断文件是否存在
if(ret==-1)
{
perror("mkfifo");
exit(0);
}
int fd= open("fifo2",O_RDONLY);
if(fd==-1)
{
perror("read open");
exit(0);
}
char buf[1024];
for(int i=0;i<100;i++)
{
int len=read(fd,buf,sizeof(buf));
if(len==0)
{
printf("write close\n");
exit(0);
}
printf("%s\n",buf);
bzero(buf,1024);
}
close(fd);
return 0;
}
fifo实现简单的聊天 只能读一句写一句。
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <iostream>
int main()
{
//判断存在
int ret = access("fifo1",F_OK);
if(ret==-1)
{
printf("fifo1文件不存在\n");
ret=mkfifo("fifo1",0664);
if(ret==-1)
{
perror("mkfifo1");
exit(0);
}
}
ret=access("fifo2",F_OK);
if(ret==-1)
{
printf("fifo2文件不存在\n");
ret=mkfifo("fifo2",0664);
if(ret==-1)
{
perror("mkfifo2");
exit(0);
}
}
//只写 只读分别打开fifo1,2
int fdw = open("fifo1",O_WRONLY);
if(fdw==-1)
{
perror("open");
exit(0);
}
printf("fifo1 打开,等待写入\n");
int fdr = open("fifo2",O_RDONLY);
if(fdr==-1)
{
perror("open");
exit(0);
}
printf("fifo2 打开,等待读取\n");
const int N=128;
char buf[N];
//循环读写
while(1)
{
memset(buf,0,N);
fgets(buf,N,stdin);//获取标准输入的数据
ret=write(fdw,buf,strlen(buf));
if(ret==-1)
{
perror("wirte");
exit(0);
}
memset(buf,0,N);
ret=read(fdr,buf,N);
if(ret<=0)
{
perror("read");
exit(0);
}
std::cout<<getpid()<<"read: "<<buf<<std::endl;
}
close(fdw);
close(fdr);
return 0;
}
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <iostream>
int main()
{
//判断存在
int ret = access("fifo1",F_OK);
if(ret==-1)
{
printf("fifo1文件不存在\n");
ret=mkfifo("fifo1",0664);
if(ret==-1)
{
perror("mkfifo1");
exit(0);
}
}
ret=access("fifo2",F_OK);
if(ret==-1)
{
printf("fifo2文件不存在\n");
ret=mkfifo("fifo2",0664);
if(ret==-1)
{
perror("mkfifo2");
exit(0);
}
}
//只写 只读分别打开fifo2,1
int fdr = open("fifo1",O_RDONLY);
if(fdr==-1)
{
perror("open");
exit(0);
}
printf("fifo1 打开,等待读取\n");
int fdw = open("fifo2",O_WRONLY);
if(fdw==-1)
{
perror("open");
exit(0);
}
printf("fifo2 打开,等待写入\n");
const int N=128;
char buf[N];
//循环读写
while(1)
{
memset(buf,0,N);
ret=read(fdr,buf,N);
if(ret<=0)
{
perror("read");
exit(0);
}
std::cout<<getpid()<<"read: "<<buf<<std::endl;
memset(buf,0,N);
fgets(buf,N,stdin);//获取标准输入的数据
ret=write(fdw,buf,strlen(buf));
if(ret==-1)
{
perror("wirte");
exit(0);
}
}
close(fdw);
close(fdr);
return 0;
}
注意两个文件里再打开fifo时 ,顺序要一致,实际要等FIFO1,fifo2都打开,才能继续往下运行。