#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <iostream>
#include <sys/types.h>
#include <sys/wait.h>
using namespace std ;
int count;
int main (void )
{
int fd[2];
if (pipe(fd)==-1)
{
perror("open pipe err");
exit(1);
}
pid_t pid=fork();
if (pid == 0)
{
char buf[10];
printf("i am child process =%d \n",getpid());
while (1)
{
read(fd[0],buf,10);
printf("recv message to parent =%s \n",buf);
if (++count ==10)
break;
}
//return 10;
exit(10);
}else
{
printf("i am parent process =%d \n",getpid());
while (1)
{
sleep(2);
count+=1;
printf("count = %d \n",count);
write(fd[1],"hello c",8);
if (count == 10)
break;
}
int status;
int pid;
while(1){
pid=waitpid(-1,&status,WNOHANG);//回收子进程用
//printf("pid =%d \n",pid);
if (pid > 0)
break;
}
if (WIFEXITED(status))
printf("child return =%d \n",WEXITSTATUS(status));
}
close(fd[0]);
close(fd[1]);
return 0;
}
pipe一般应用于父子(血缘悬系)进程之间的通信,不能进行跨进程通信。管道是一种最基本的IPC机制,其本质是一个伪文件(实为内核缓冲区),通过fd[1]write 写数据,fd[0] read读数据。需要注意,数据只要通过管道写进去了,不管哪个进程都可以在读端取数据。为了避免混乱一般需要创建两个管道进行互相通信。
管道数据只能读取一次不能反复读取,就像队列一样。
一,匿名管道PIPE局限性
管道的主要局限性正体现在它的特点上:
- 只支持单向数据流;
- 只能用于具有亲缘关系的进程之间;
- 管道的缓冲区是有限的(管道制存在于内存中,在管道创建时,为缓冲区分配一个页面大小);
- 管道所传送的是无格式字节流,这就要求管道的读出方和写入方必须事先约定好数据的格式,比如多少字节算作一个消息(或命令、或记录)等等;
如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道,是一种特殊类型的文件。
//FIFO 写端
static char *PIPE_NAME;
#define BUF_S 1024
int main (int args , char *argv[])
{
if (args != 2)
{
fputs("please set fifo name \n",stderr);
exit(1);
}
PIPE_NAME =argv[1];
printf("crete a fifo name = %s \n ",PIPE_NAME);
//umask(0);
if (mkfifo(PIPE_NAME,0644)==-1)//create a fifo )
{
perror("mkfifo err");
exit(1);
}
printf("next open this pipe \n");
int fd = open(PIPE_NAME,O_WRONLY);
if (fd==-1)
{
perror("open err");
exit(1);
}
printf(" open sucess!! \n");
char buf[BUF_S];
int str_len ;
while(1)
{
fputs("press q to quit",stdout);
fgets(buf,BUF_S,stdin);//终端输入数据
//strcmp check each character
//检查每个字符,如果字符一样返回0,如果不一样逐个字符对比大小
if (strcmp(buf,"q\n") && strcmp(buf,"Q\n"))
{
printf("stdin = %s \n",buf);
write(fd,buf,sizeof(buf));
}else
break ;
}
close(fd);
unlink(PIPE_NAME); //文件用完后即删除。将文件的引用计数减1,当为0的时候回自动删除。
printf("process end \n");
return 0;
}
读端
int main (int args , char *argv[])
{
if (args != 2)
{
fputs("please set fifo name \n",stderr);
exit(1);
}
PIPE_NAME =argv[1];
printf("next open fifo name = %s \n ",PIPE_NAME);
int fd = open(PIPE_NAME,O_RDONLY);//如果写端还没有打开将会阻塞在这。
if (fd==-1)
{
perror("open err");
exit(1);
}
printf("open pipe %s suscess",PIPE_NAME);
char buf[BUF_S];
int str_len ;
while(1)
{
str_len=read(fd,buf,BUF_S);
if (str_len == 0)//写端调用close
{
printf("recev length 0 \n ");
break;
}
printf("receve message =%s ",buf);
}
printf("process end \n");
return 0;
}
FIFO(命名管道)与pipe(匿名管道)之间唯一的区别在它们创建与打开的方式不同,一量这些工作完成之后,它们具有相同的语义。man帮助说明:The only difference between pipes and FIFOs is the manner in which they are created and opened. Once these tasks have been accomplished, I/O on pipes and FIFOs has exactly the same semantics。
有名管道比管道多了一个打开操作:open。
FIFO的打开规则:如果以读的方式打开fifo时 ,fifo写端还没有创建,会因为没有那个文件导致No such file or director错误。如果写端打开mkfifo,此时读端还没有打开,则会阻塞直到读端打开fifo。文中unlink 一定要用,如果不用文件会残留(进程通信完后这个文件没用了,变成垃圾),注意unlink的位置,一但文件的引用为0系统就会删除这个文件,如果进程open这个文件是就会报No such file or directory;
//写端
int main(int args ,char *argv[])
{
bool no_name=false;
if (args != 2)
no_name=true;
;
size_t length_t=sizeof(struct people);
printf("length_t=%d \n",length_t);
people *p_cache ;
if (no_name)
{//匿名的只有父子进程这样的才行(MAP_SHARED)。其他进程不能读
printf("no file mmap \n");
//匿名,不依赖文件 看man只能 supported on Linux only since kernel 2.4
p_cache=(people *)mmap(NULL,length_t,PROT_WRITE|PROT_READ,MAP_SHARED|MAP_ANONYMOUS,-1,0);
//exit(0);
}else
{
int fd = open (argv[1],O_CREAT|O_RDWR|O_TRUNC,0777);
if (fd == -1)
{
perror("open err");
exit(1);
}
//lseek(fd,length_t);
if(ftruncate(fd,length_t)==-1)
{
perror("ftruncate err");
}
//p_cache=(people *)mmap(NULL,length_t,PROT_WRITE|PROT_READ,MAP_SHARED,fd,0);
p_cache=(people *)mmap(NULL,length_t,PROT_WRITE|PROT_READ,MAP_PRIVATE,fd,0);
close(fd);//创建了映射区后这个文件的描述符已经没用。
//unlink(argv[1]);//创建后这个文件就没有了,读端找不到这个文件导致错误
}
//验证是否映射成功
if (MAP_FAILED==p_cache||errno!=0)
{
perror("mmap err");
printf("errorno = %d \n",errno);
exit(1);
}
//printf("p_cache=%d",p_cache);
memset(p_cache,0,length_t);
strcpy(p_cache->name,"ahahah");
int count;
while(1)
{
sleep(2);
p_cache->age+=1;
printf("name =%s,age=%d \n",p_cache->name,p_cache->age);
if (++count==20)
break;
}
//unlink(argv[1]);//用过之后如果没有文件进程使用该文件了则会自动清除
munmap(p_cache,length_t);//记得释放映射空间
}
读端
int main (int args , char *argv[])
{
if (args != 2)
{
fputs("please set fifo name \n",stderr);
exit(1);
}
PIPE_NAME =argv[1];
printf("next open fifo name = %s \n ",PIPE_NAME);
int fd = open(PIPE_NAME,O_RDONLY);
if (fd==-1)
{
perror("open err");
exit(1);
}
printf("open pipe %s suscess \n",PIPE_NAME);
char buf[BUF_S];
int str_len ;
while(1)
{
str_len=read(fd,buf,BUF_S);
if (str_len == 0)
{
printf("recev length 0 \n ");
break;
}
printf("receve message =%s ",buf);
}
printf("process end \n");
return 0;
}
mmap: (mmap原理请看别人博客)
使用mmap是无比注意一下事项:
1.创建映射区的过程中,隐含着一次对映射文件的读操作
2.当MAP_SHARED时,要求映射区的权限<=文件打开的权限(处于对映射区的保护)。而MAP_PRIVATE则无所谓,因为mmap的权限是对内存限制。
3.映射区的释放与文件关闭无关,只要映射建立成功,文件可以立即关闭
4.特别注意,当映射文件大小为0时,不能创建映射区。所以:用于映射的文件必须要有实际大小!如ftruncate(fd,20) mmap使用时常常会出现总线错误,通常是由于共享文件存储空间大小引起的。
5.munmap传入的地址一定是mmap的返回地址。坚决杜绝指针++操作(指向映射区以外)
6,.文件的偏移量必须是4k(页的大小)的整数倍
7.mmap创建映射区出错概率非常高,检查返回值,建立成功后再进行后续工作
mmap父子进程通信
父子等有血缘关系的进程之间也可以通过mmap建立映射区来完成数据通信。但是相应的要在创建映射区的时候指定对应的标志位参数flags:
MAP_PRIVATE :(私有映射)父子进程各自独占映射区
MAP_SHARED :(共享映射)父子进程共享映射区
进程之间通信优劣:https://www.cnblogs.com/lincappu/p/8536431.html