15 管道——pipe

1、创建管道

使用管道可以用来连接一个进程的输出和另一个进程的输入,管道是内核中的一个单向的数据通道,管道有一个读取端和一个写入端。

pipe系统调用:

  • pipe():创建管道,result=pipe(int array[2]),调用pipe来创建管道并将两端连接到两个文件描述符,array[1]为写数据端的文件描述符,array[0]为读数据端的文件描述符,管道的内部实现隐藏在内核中,进程只能看见两个文件描述符。pipe调用也使用最低可用文件描述符。

2、创建管道并向自己发送数据

首先进程通过标准输入将数据从终端送到进程位于进程缓冲区,然后进程向刚刚创建的管道写入端写入数据位于内核缓冲区,再然后进程从管道的读取端读取数据到进程位于进程缓冲区,最后通过标准输出从进程送回终端显示出来。通过标准输出从进程送回终端显示数据的过程,进程应该先是将数据送到内核,然后内核通过系统调用才将数据送到了终端。

3、创建管道并向其他进程发送数据

当进程创建一个管道后,该进程就有了连向管道两端的连接,当这个进程调用fork的时候,它的子进程也得到了这两个连向管道的连接相当于从父进程复制连接管道端点的文件描述符指针数组,且父进程和子进程连向相同的管道,共享管道,也就是父进程的管道写入端和子进程的写入端连接同一管道的写入端,父进程的管道读取端和子进程的读取端连接同一管道的读取端,这样,两个进程都可以读写管道,当一个进程读,另一个进程写的时候,管道的使用效率是最高的。

4、管道总结

(1)从管道中读数据

  • 从管道读取阻塞:当进程试图从管道读数据时,进程被挂起直到数据被写进管道
  • 管道的读取结束标志:当所有的写者关闭了管道的写数据端时,试图从管道读取数据的调用返回0,意味着文件的结束
  • 多个读者可能会引起麻烦:管道是一个队列,当进程从管道中读取数据之后,数据就不存在了。如果两个进程都试图对同一个管道进行读操作,在一个进程读取一些之后,另一个进程读到的将是后面的内容,它们读取的数据是不完整的。

(2)向管道写入数据

  • 写入数据阻塞直到管道有空间去容纳新的数据:管道容纳数据的能力比磁盘文件差的多,当进程试图对管道进行写操作的时候,次调用将挂起进程直到管道中有足够的空间去容纳新的数据
  • 写入必须保证一个最小的块大小:POSIX标准规定内核不会拆分小于512字节的块,LInux则保证管道中可以存在4096字节的连续缓存。
  • 若无读者在读取数据,则写操作执行失败:如果所有的读者都已将管道的读取端关闭,那么对管道的写入调用将会执行失败。这种情况内核发送SIGPIPE信号给进程,若进程被终止,则无任何事情发生,否则write调用返回-1并将errno置为EPIPE。

5、具体实现程序

pipedemo.c

创建管道并向自己发送数据

#include<stdio.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
int main()
{
    int len,i,apipe[2];
    char buf[BUFSIZ];
    if(pipe(apipe)==-1)
    {
        perror("Could not make pipe");
        exit(1);
    }
    printf("Got a pipe.It is file descriptors:{%d,%d}\n",apipe[0],apipe[1]);
    while(fgets(buf,BUFSIZ,stdin))
    {
        len=strlen(buf);
        if(write(apipe[1],buf,len)!=len)
        {
            perror("writing to pipe");
            break;
        }
        for(i=0;i<len;i++)
            buf[i]='X';
        len=read(apipe[0],buf,BUFSIZ);
        if(len==-1)
        {
            perror("reading from pipe");
            break;
        }
        if(write(1,buf,len)!=len)
        {
            perror("writing to stdout");
            break;
        }
    }
    return 0;
}

pipedemo2.c

创建管道并向其他进程发送数据

#include<stdio.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#define CHILD_MSG "I want a cookie\n"
#define PAR_MSG "testing...\n"
#define oops(m,x) {perror(m);exit(x);}
int main()
{
    int len,pipefd[2];
    char buf[BUFSIZ];
    int read_len;
    if(pipe(pipefd)==-1)
        oops("Could not make pipe",1);
    printf("Got a pipe.It is file descriptors:{%d,%d}\n",pipefd[0],pipefd[1]);
    switch(fork())
    {
        case -1:
            oops("cannot fork",2);
        case 0:
            len=strlen(CHILD_MSG);
            while(1)
            {
                if(write(pipefd[1],CHILD_MSG,len)!=len)
                    oops("wirte",3);
                sleep(5);
            }
        default:
            len=strlen(PAR_MSG);
            while(1)
            {
                if(write(pipefd[1],PAR_MSG,len)!=len)
                    oops("write",4);
                sleep(1);
                read_len=read(pipefd[0],buf,BUFSIZ);
                if(read_len<=0)
                    break;
                write(1,buf,read_len);
            }
    }
    return 0;
}

pipe.c

创建管道当前进程的标准输出连接到管道写入端,另一个进程的标准输入连接到管道读取端,这样通过管道一个进程向其他进程发送数据

#include<stdio.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#define CHILD_MSG "I want a cookie\n"
#define PAR_MSG "testing...\n"
#define oops(m,x) {perror(m);exit(x);}
int main(int argc,char **argv)
{
    int thepipe[2],newfd,pid;
    if(argc!=3)
    {
        fprintf(stderr,"usage:pipe cmd1 cmd2\n");
        exit(1);
    }
    if(pipe(thepipe)==-1)
        oops("Could not make pipe",1);
    if((pid=fork())==-1)
        oops("Could not fork",2);
    if(pid>0)
    {
        close(thepipe[1]);
        if(dup2(thepipe[0],0)==-1)
            oops("could not redirect stdin",3);
        close(thepipe[0]);
        execlp(argv[2],argv[2],NULL);
        oops(argv[2],4);
    }
    else if(pid==0)
    {
        close(thepipe[0]);
        if(dup2(thepipe[1],1)==-1)
            oops("could not redirect stdout",4);
        close(thepipe[1]);
        execlp(argv[1],argv[1],NULL);
        oops(argv[1],5);
    }
    return 0;
}

 


版权声明:本文为Revendell原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。