进程间通信:IPC-管道

进程间通信方式:IPC(管道、共享内存、信号量) 、信号。
IPC:
  管道:分为标准流管道、无名管道、有名管道。
  共享内存:将两个进程的虚拟地址空间。映射到同一块物理内存。
  信号量:
信号:约定好了,什么信号干什么事。
----------------------------------------------------------------------------------------------------------------------------------------------------------------
管道分:
1.标准流管道
2.无名管道
3.命名管道(有名管道)

1.标准流管道(用的比较少) 
FILE* popen(const char* command, const char* open_mode);
popen 允许一个程序将另一个程序作为新进程来启动。command字符串是要运行的程序名(即,被调用程序)。open_mode必须是“r”或“w”。调用popen函数,会做两件事: 1.创建新的进程command。2.在程序和被调用程序之间建立一条标准流管道。
如果open_mode是“r”:则调用函数对管道就是读,被调用函数对管道就是写,并且把 被调用函数的标准输出重定向到了写端
如果open_mode是“w”:则调用函数对管道就是写、被调用函数对管道就是读,并且把 被调用函数的标准输入重定向到了读端
例1:
第一步:vim printf.c
int main(){
  printf("Hello world\n");               //输出到标准输出上。因为这里printf是popen新建的进程。而新创建的进程的 标准输出重定 向到了
                  写端。所以也就是往管道里输出了字符串。
  return 0;
}
第二步:gcc printf.c -o printf              //这就是要执行的程序
第三步:vim popen_r.c
int main(int argc,char *argv[]){
  if(argc!=2){
    printf("error args\n");
    return -1;  
  }
  FILE * fp= popen(argv[1],"r");      //将argv[1]作为新的进程来启动。即,新建的进程为:printf。
  if(NULL==fp){
    perror("popen");
    return -1;
  }
  char buf[128]={0};
  fread(buf,sizeof(buf),sizeof(buf),fp);   //读管道的东西到buf中。新建的进程printf向标准输出写了"Hello world\n",而新建的进程的
                        标准输出重定向到了管道。所以读管道的内容就是"Hello world\n"。
  printf("%s",buf);                                //打印出读出的内容:Hello world
  fclose(fp);                                 
  return 0;
}
第四步:gcc popen_r.c -o popen_r
第五步:./popen_r ./printf                 //将命令"./printf"作为参数传进去。

例2:
第一步:vim fgets.c
int main(){
        char buf[128]={0};
        fgets(buf,sizeof(buf),stdin);       
        printf("%s\n",buf);
        return 0;
}
第二步:gcc fgets.c fgets
第三步:vim popen_w.c
int main(int argc,char* argv[])
{
        if(argc !=3){
                printf("error args\n");
                return -1;
        }
        FILE* fp;
        fp=popen(argv[1],"w");                   //打开一个新的进程argv[1],并建立一条管道。自己是写端。被调用进程是读端。被调用进程的                                                               标准输入重定向到了管道。即,从标准输入读就是从管道读。 
        if(NULL==fp){
                perror("popen");
                return -1;
        }
        fwrite(argv[2],sizeof(char),strlen(argv[2]),fp);     //往管道fp中写argv[2].
        fclose(fp);                                      //写完之后,如果还没来得及读就关闭了管道,没有关系。但是这样管道不能正常退出。
        return 0;
}
第四步:gcc popen_w.c -o popen_w
第五步:./popen_w ./fgets hello

2.无名管道
无名管道只能在亲缘关系进程间通信(父子或兄弟)。
int fds[2];pipe(fds);后内核里面只有一条管道,父进程和子进程同时各有了一个管道的读端和写端。但是:每个进程只能用其中的一个。要么:父进程从fd[1]写,子进程从fd[0]读。要么子进程从fd[1]写,父进程就从fd[0]读。


例:
         int fds[2];
         pipe(fds);                                      //新建了一条管道,并把读端和写端打开。
        if(!fork()){                                      //子进程
                 close(fds[1]);                         //fds[0]是读,fds[1]是写。子进程关闭写端。对于新创建的
                char buf[128]={0};
                read(fds[0],buf,sizeof(buf));   //从读端读
                printf("%s\n",buf);
                 close(fds[0]);                         //关闭读端
                exit(0);
        }else{                                           //父进程
                 close(fds[0]);                         //父进程关闭读端。
                write(fds[1],"Hello",5);          //向写端写。
                wait(NULL);                         //防止子进程变成僵尸进程或孤儿进程
                 close(fds[1]);         //关闭写端
                return 0;
        }

3.命名管道(有名管道):
有名管道的使用跟前面学习的管道文件一样,只不过是用函数创建的管道。
例:创建有名管道:
int main(){
        int ret= mkfifo("1.fifo",0666);
        if(-1==ret){
                perror("mkfifo");
                return -1;
        }
        return 0;
}

猜你喜欢

转载自blog.csdn.net/pengchengliu/article/details/80501610