文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>Unix和Window平台IPC: 匿名管道

Unix和Window平台IPC: 匿名管道

时间:2010-09-02  来源:kgisme170

        Unix和Windows平台上,用匿名管道进行IPC的原语几乎是一致的,但是实现非常不一样。举一个例子,如果有一个命令行程序My,从标准输入读取几个整数,把加和输出到标准输出。那么我希望写一个Main程序来调用它,从一个数据文件in.txt读入内容,通过管道给My,再把My的输出通过一个管道写入一个文件out.txt   

        那么我们需要两个管道:Main写入第一个管道的write端,第一个管道的read端和第二个管道的write端绑定到My,而第二个管道的write端给Main来做输出用。

        Unix平台用fork()函数,子进程自动继承了父进程声明的各个管道fd,把子进程的stdin/stdout关闭,用dup2把两个管道绑定到子进程的stdin/stdout,然后用execl执行My,这样就可以实现通信了。注意到,因为从文件读入的时候,一次读入就结束了(虽然是两行,但是还是一次read就结束,同理写入管道的时候也是一次结束。所以主程序的while循环里面,应该只包含从管道读出结果的过程

        Windows平台没有fork之后的流程,必须直接CreateProcess。而CreateProcess的参数里面指定了子进程的stdin/stdout是否使用特定的管道句柄。因为Windows平台如果混用Posix C函数和Windows API的话,IO的过程会出现一些不可预料的混乱(Microsoft的常用伎俩: 兼容标准,但是又不那么彻底,想要无误运行,还是依赖Windows API)。所以最好完全使用Win32API来编程。

        比较而言,Unix调用是简洁的,优雅的,代码很少。函数符合Kiss原则,用不到的功能,不用去管。但是这样的代价就是会有很多"隐含"的知识,这会导致一些隐含的问题。而Win32 API的风格是"显示"的要去指定所有的参数,没有任何隐含的知识。代价就是代码显得繁琐而且罗嗦。但是,所有的知识都体现在一个调用界面里面,阅读和分析代码,反而变得容易了。值得注意的是,Unix平台的read()是遇到内容结束的时候就退出,而在windows上必须保证写端的句柄已经关闭,否则ReadFile会一直处于阻塞状态。

        下面是源代码,先贴出Unix平台的。in.txt的内容是:20 30\n5 6\n

> cat calc.C
#include<iostream.h>
int main(void){
  int a , b;
  while(cin>>a>>b){
    cout<<a+b<<'\n';
  }
  return 0;
}
> cat wrap.C
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
int main(void){
  int p2my[2];
  int my2p[2];
  char bufin[128]="23 35\n1 4\n";
  char bufout[128]={0};
  if(pipe(p2my) || pipe(my2p)){
    printf("pipe failed\n");
    exit(1);
  }
  int pid=fork();
  if(pid<0){
    printf("fork failed\n");
    exit(1);
  }
  if(pid>0){//father

    close(p2my[0]);
    close(my2p[1]);
    int fdin=open("in.txt",O_RDONLY);
    int fdout=open("out.txt",O_RDWR|O_CREAT,O_NONBLOCK);
    int r=read(fdin,bufin,sizeof(bufin));
    write(p2my[1],bufin,sizeof(bufin));
    for(int i=0;i<128;++i){bufout[i]=0;}
    r=read( my2p[0],bufout,sizeof(bufout));
    write(/*STDOUT_FILENO*/fdout, bufout,sizeof(bufout));
    close(fdin);
    close(fdout);
  }else if(pid==0){//child

    close(p2my[1]);
    close(my2p[0]);
    close(0);
    close(1);
    dup2(p2my[0],0);
    dup2(my2p[1],1);
    execl("my",NULL);
  }
  return 0;
}

        运行结果,out.txt里面有两个50 11两个数字。

        再贴出Windows平台的。注意,如果向标准输入写内容不是Write(STD_OUTPUT_HANDLE),会有奇怪的IO问题。(使用printf或者cout的话,会导致读取结果的时候,结果不止一行)为了简化,文件IO被stdin/stdout代替。

#include<windows.h>
#include<stdio.h>
int main(){
 int a,b;
 DWORD dwWrite;
 char buf[4];
 while(scanf("%d %d",&a,&b)){
  sprintf(buf,"%d,",a+b);
  WriteFile((HANDLE)STD_OUTPUT_HANDLE,buf,strlen(buf),&dwWrite,NULL);
 }
 return 0;
}


#include<windows.h>
#define BUFSIZE 1024
HANDLE hInRead,hInWrite,hOutRead,hOutWrite;
PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo;
BOOL fork(char* sCommandLine){//用于创建MyIO进程

 ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
 ZeroMemory(&siStartInfo,sizeof(STARTUPINFO));
 siStartInfo.cb=sizeof(STARTUPINFO);
 siStartInfo.hStdInput=hInRead;
 siStartInfo.hStdOutput=hOutWrite;
 siStartInfo.dwFlags=STARTF_USESTDHANDLES;//指定管道为stdin,stdout

 return CreateProcess(NULL,sCommandLine,NULL,NULL,TRUE,0,NULL,NULL,&siStartInfo,&piProcInfo);
}
void writePipe(const char* inputFileName){
 char buf[BUFSIZE]="\n\n\n\n\n20 30\n50 60\n70 80\n3 9\n4 7\n\0";
 DWORD dwRead=strlen(buf),dwWrite=0;
 BOOL fOK=WriteFile(hInWrite,buf,dwRead,&dwWrite,NULL);
 if(!fOK)exit(1);
 CloseHandle(hInWrite);
}
void readPipe(void){//从子进程读出结果,并打印

 char buf[BUFSIZE]={0};
 DWORD dwRead=0,dwWrite;
 ZeroMemory(buf,BUFSIZE);
 BOOL fOK=ReadFile(hOutRead,buf,BUFSIZE,&dwRead,NULL);
 if(!fOK || !dwRead)exit(1);
 WriteFile((HANDLE)STD_OUTPUT_HANDLE,buf,strlen(buf),&dwWrite,NULL);
 ZeroMemory(buf,BUFSIZE);
 fOK=ReadFile(hOutRead,buf,BUFSIZE,&dwRead,NULL);
 if(!fOK || !dwRead)exit(1);
 WriteFile((HANDLE)STD_OUTPUT_HANDLE,buf,strlen(buf),&dwWrite,NULL);
}
int main(void){
 SECURITY_ATTRIBUTES saAttr;
 saAttr.nLength=sizeof(SECURITY_ATTRIBUTES);
 saAttr.bInheritHandle=TRUE;
 saAttr.lpSecurityDescriptor=NULL;
 if(!CreatePipe(&hOutRead,&hOutWrite,&saAttr,0)
  ||!CreatePipe(&hInRead ,&hInWrite ,&saAttr,0)){
  return 1;
 }
 if(!fork("testChar"))exit(1);
 writePipe("inputFile.txt");
 readPipe();
 return 0;
}


相关阅读 更多 +
排行榜 更多 +
辰域智控app

辰域智控app

系统工具 下载
网医联盟app

网医联盟app

运动健身 下载
汇丰汇选App

汇丰汇选App

金融理财 下载