Linux程序设计——管道

来源:互联网 发布:李贞贤阿里阿里百度云 编辑:程序博客网 时间:2024/05/17 09:05

进程间通信:管道

  1. 什么是管道
    • 当从一个进程连接数据流到另一个进程时,我们使用术语管道。我们通常是把一个进程的输出通过管道连接到另一个进程的输入。
  2. 进程管道
    • popen函数
      • 函数原型:FILE * popen(cosnt char * command,const char * open_mode);
      • popen函数允许一个程序将另一个程序作为新进程来启动,并可以传递数据给它或者通过它接收数据。command字符串是要运行的程序名和对应的参数。open_mode必须是“r”或者“w”,此函数在失败时返回一个空指针。
    • pclose函数
      • 函数原型:int pclose(FILE * stream_to_close);
      • pclose调用只在popen启动的进程结束后才返回。如果调用pclose时它仍在运行,pclose调用将等待该进程的结束。
  3. pipe调用
    • 上面介绍的是高级的popen函数,现在看一下底层的pipe函数。这个函数不同与上面的popen/pclose函数,该函数不需要启动一个shell来解释请求的命令。它同时还提供了对读写数据的更多控制。
    • 原型:int pipe(int file_descriptor[2]);
    • pipe函数的参数是一个由两个整数类型的文件描述符组成的数值的指针。该函数在数组上填写两个新的文件描述符后返回0,如果失败返回-1并设置errno来表明失败的原因。
    • 两个返回的文件描述符以一种特殊的方式连接起来。写到file_descriptor[1]的所有数据都可以从file_descriptor[0]中读回来。数据基于先进先出的原则,进行处理。
    • 要注意的是,这里使用的是文件描述符而不是文件流,所以必须使用read和write系统调用来访问数据,而不是用fwrite和fread的文件流库函数。
    • 如果你想把文件描述符的0和1互换,那你可以试试,其后果文档中没有定义,或许和当前的系统环境有关.在我系统上,不管使用write或read是,出错,错误信息:坏的文件描述符( Bad file descriptor)。
  4. 父进程和子进程
    • 如何在父子进程之间使用管道通信。
      1. 在父进程中使用fork创建一个子进程,在子进程中,使用execl来启动另一个程序。
      2. 然后两个进程使用管道通信:
        • 在父进程中使用write写入数据
        • 在子进程中读取数据,子进程read中的文件描述符由execl的参数给出。
  5. 管道关闭之后的读操作
    对于管道,通常不知道有多少数据可以读,往往采用轮询的方式。当没有数据可以读时,read调用将被堵塞。如果管道的另一端关闭时,read调用返回的是0而不是堵塞。fork产生的子进程也会用户和父进程相同的一对管道描述符,所以在父子进程使用管道通信时,要提前关闭对应的一端管道描述符。

  6. 使用管道重定向标准输入

    • 使用fork创建子进程,然后子进程使用close(0)关闭标准输入,然后使用dup(file_pipes[0])复制管道的输出端的描述符。接着关闭子进程中的管道描述符的输入输出端。此时子进程中程序的标准输入就是管道的标准输出间接的就是父进程中的标准输入端。
  7. 命名管道
    • 命名管道是一种特殊类型的文件,它在文件系统中以文件名的形式存在,但它的行为却和我们已经见过的没有名字的管道类型。
  8. 访问FIFO文件
    • 因为FIFO中没有数据,所以当调用cat和echo单独读或写这个管道时,都会被堵塞,cat等待数据的到来,而echo等待其它进程来读取数据。原因:可以与命名管道的大小有关,如果使用ls -l /tmp/my_fifo,会发现这个管道的大小为0。
    • 与通过pipe调用创建管道不同,FIFO是以命名文件的形式存在,而不是打开的文件描述符,所以在对它进行读写操作之前必须先打开它,FIFO也用open和close函数打开和关闭。
      1. 使用open打开FIFO文件
        • 打开FIFO的限制是,程序不能以O_RDWR模式打开FIFO文件进行读写操作,因为我们通常使用FIFO只是为了单向传递数据,所以没有必要这么做。
        • 如果需要在程序之间双向传递数据,最后使用一对FIFO或管道,或者先关闭在重新打开FIFO的方法来明确地改变数据流的方向。
        • 打开FIFO文件和打开普通文件的另一个区别是,对open_falg(open函数的第二个参数)的O_NONBLOCK选项的用法。使用这个选项不仅改变open调用的处理方式,还会改变对这次open调用返回的文件描述符进行读写请求的处理方式。
        • O_RDNOLY、O_WRONLY和O_NONBLOCK标志共有四种合法的组合方式。
          • open(const char * path,O_RDONLY)在这种情况下,open调用将阻塞,除非有一个进程以写方式打开同一个FIFO,否则它不会返回。
          • open(const char* path,O_RDONLY |O_NONBLOCK)):即使没有其他进程以写方式打开FIFO,这个open调用已将成功并立刻返回。
          • open(const char * path,O_WRONLY):这种情况下open调用也会堵塞,直到有一个进程以读方式打开同一个FIFO为止。
          • open(const char * path,O_WRONLY|O_NONBLOCK):这个函数调用总是立刻返回,但如果没有进程以只读的方式打开FIFO文件,open调用将返回一个错误-1并且FIFO也不会被打开。如果确实有一个进程以只读方式打开FIFO文件,那么我们就可以通过它返回的文件描述符对这个FIFO文件进行写操作。
          • 如果没有进程以读方式打开管道,非阻塞写方式的open调用将失败,但非阻塞读方式的open调用总是成功,close调用的行为并不受O_NONBLOCK标志的影响。
      2. 对FIFO进行读写操作
        对于一个空的、堵塞的FIFO的read调用将等待,直到有数据可以读时才进行执行。与此相反,对一个空的、非堵塞的FIFO的read调用将立刻返回0字节。
        对于一个完全堵塞FIFO的write调用将等待,直到数据可以被写入(即管道的另一端被打开时)才可以继续执行。
        提供的程序两个程序使用的都是阻塞模式的FIFO。我们首先启动写进程(以写堵塞的方式打开管道),它将堵塞以等待读进程打开这个FIFO。读进程启动后,写进程解除堵塞并开始向管道写数据。同时,读进程也开始从管道中读取数据。
  9. 小结
    本章中,我们介绍了如何使用管道在进程之间传递数据。首先,介绍了通过popen或pipe调用创建的未命名管道,并且讨论了如何使用管道和dup调用把管道作为标准输入。接下来,我们介绍了命名管道以及如何在不相关的程序之间传递数据。最后,实现了一个简单的客户/服务器例子,FIFO的使用不仅提供了进程间的同步(只要保证所有的写请求是发往一个阻塞的FIFO的,并且每个写请求的数据长度小于等于PIPE_BUF字节,系统就可确保数据不会交错在一起),还提供了双向的数据流。
0 0
原创粉丝点击