文档章节

I/O重定向和管道——《Unix/Linux编程实践教程》读书笔记(第10章)

木子叶
 木子叶
发布于 2014/08/06 12:41
字数 1654
阅读 321
收藏 9

1、I/O重定向的概念与原因 及 标准输入、输出的标准错误的定义

所以的Unix I/O重定向都基于标准数据流的原理。三个数据了分别如下:

    1)标准输入——需要处理的数据流

    2)标准输出——结果数据流

    3)标准错误输出——错误消息流


概念:所以的Unix工具都使用文件描述符0、1和2。标准输入文件的描述符是0,标准输出的文件描述符是1,而标准错误输出的文件描述符则是2。Unix假设文件描述符0、1、2已经被打开,可以分别进行读写操作。

通常通过shell命令行运行Unix系统工具时,stdin、stdout、stderr连接在终端上。因此,工具从键盘读取数据并且把输出和错误消息写到屏幕。

大部分的Unix工具处理从文件或标准输入读入的数据。如果在命令行上给出了文件名,工具将从文件读取数据。若无文件名,程序则从标准输入读取数据。从另一方面说,大多数程序并不接收输出文件名;它们总是将结果写到文件描述符1,并将错误消息写到文件描述符2。如果希望将进程的输出写道文件或另一个进程的输入去,就必须重定向相应的文件描述符。

重定向I/O的是shell而不是程序

最低可用文件描述符(lowest-available-fd)原则:文件描述符是一个数组的索引号。每个进程都有其打开的一组文件。这些打开的文件被保持在一个数组中。文件描述符即为某文件在此数组中的索引。当打开文件时,为此文件安排的描述符总是此数组中最低可用位置的索引。

将文件描述符0、1、2的概念和最低可用文件描述符原则结合使用,即可理解I/O重定向的工作原理


2、重定向标准I/O到文件

(1)如何将stdin定向到文件

a、close-then-open策略

close(0);
int fd = open(file, O_RDONLY);


b、open-close-dup-close策略
int oldfd = open(file, O_RDONLY);
#ifndef DUP2
    close(0);
    int newfd = dup(oldfd);
#else
    int newfd = dup2(oldfd, 0);
#endif
close(oldfd);


man 2 dup
#include <unistd.h>

int dup(int oldfd);
int dup2(int oldfd, int newfd);

系统调用dup复制了文件描述符oldfd。而dup2将oldfd文件描述符复制给newfd。两个文件描述符都指向同一个打开的文件。这两个调用都返回新的文件描述符,若发生错误,则返回-1。


c、open-dup2-close策略


(2)重定向到文件

共有3个基本的概念,利用它们是的Unix下的程序可以轻易地将标准输入、输出和错误信息输出连接到文件:

    a、标准输入、输出以及错误输出分别对应于文件描述符0、1、2;

    b、内核总是使用最低可用文件描述符;

    c、文件描述符集合通过exec调用传递,且不会被改变。

例子:

/*
 * who_to_file.c
 * purpose: show how to redirect output for another program
 * idea: fork, then in the child, redirect output, then exec
 */

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(void)
{
    pid_t   pid;
    int     fd;

    printf("about to run who into a file\n");

    /* create a new process or quit */
    if ((pid = fork()) == -1)
    {
        perror("fork");
        exit(1);
    }

    /* child does the work */
    if (pid == 0)
    {
        close(1);
        fd = creat("userlist", 0644);
        execlp("who", "who", NULL);
        perror("execlp");
        exit(1);
    }

    /* parent waits then reports */
    else
    {
        wait(NULL);
        printf("done running who. results in userlist\n");
    }

    return 0;
}


3、管道编程

(1)创建管道

man 2 pipe
#include <unistd.h>

int pipe(int pipefd[2]);

系统调用pipe创建管道并将两端连接到两个文件描述符。pipefd[0]为读数据端的文件描述符,而pipefd[1]则为写数据端的文件描述符。


(2)技术细节:管道并非文件

a、从管道中读数据

    管道读取阻塞:当进程试图从管道中读数据时,进程被挂起直到数据被写进管道。

    管道的读取结束标志:当所以的写操作关闭了管道的写数据端时,试图从管道读取数据的调用返回0,意味着文件的结束。

    多个读操作可能会引起麻烦:管道是一个队列。当进程从管道中读取数据之后,数据已经不存在了。

b、向管道中写数据

    写入数据阻塞直到管道有空间去容纳新的数据

    写入必须保证一个最小的块大小:POSIX标准规定内涵不会拆分小于512字节的块。而Linux则保证管道中可以存在4096字节的连续缓存。如果两个进程向管道写数据,并且没一个进程都限制其消息不打由于512字节,那么这些消息都不会被内核拆分。

    若无读操作在读数据,则写操作执行失败:如果所以的读操作都已将管道的读取端关闭,那么对管道的写入调用将会执行失败。如果在这种情况下,数据还可以被接收的话,为了避免数据丢失,内核采用了两种方法来通知进程:“此时的写操作是无意义的”。首先,内核发送SIGPIPE消息给进程。若进程被终止,则无任何事情发生。否则write调用返回-1,并且将errno置为EPIPE。

例子:

/*
 * pipe.c -
 *      demonstrates how to create a pipeline from one process to another
 * takes two args, each a command, and connectes
 * argv[1]s output to input of argv[2]
 * usage: pipe command1 command2
 * effect: command1 | command2
 * limitations: commands do not take arguments
 * users execlp() since known number of args
 * note: exchange child and parent and watch fun
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#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("cannot get a pipe", 1);

    if ((pid = fork()) == -1)
        oops("cannot 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);
    }

    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;
}


© 著作权归作者所有

木子叶
粉丝 8
博文 15
码字总数 10129
作品 0
徐汇
程序员
私信 提问
UNIX网络编程卷2进程间通信读书笔记汇总

UNIX网络编程卷2进程间通信读书笔记(一)—概述 http://blog.chinaunix.net/u/22935/article_52711_2.html UNIX网络编程卷2进程间通信读书笔记(二)—管道 (1) http://blog.chinaunix.ne...

长平狐
2012/09/03
239
0
如何用思维导图学Java编程思想

摘要:如何学习Java编程思想,基本分为21个部分,看完本文你就有答案。 width="auto" src="http://ss.csdn.net/p?http://mmbiz.qpic.cn/mmbizjpg/Uic0S1r5o6Ou7kZN43vics5mNtYhTO4vhkOldOGnA......

bjweimengshu
2017/12/23
0
0
《机器学习》周志华西瓜书 笔记/习题答案 总目录

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 https://blog.csdn.net/TeFuirnever/article/details/96178919 专栏【机器学习】 【机器学习...

我是管小亮:)
07/16
0
0
免费的编程中文书籍索引

免费的编程中文书籍索引,欢迎投稿。 国外程序员在 stackoverflow 推荐的程序员必读书籍,中文版。 stackoverflow 上的程序员应该阅读的非编程类书籍有哪些? 中文版 github 上的一个流行的编...

modernizr
2014/04/08
7.4K
24
编程类开放书籍荟萃(转载)

关于开源图书有人在网络上做了大量整理,本文为大家刊载《免费的编程中文书籍索引》 国外程序员在 stackoverflow 推荐的程序员必读书籍,中文版。 stackoverflow 上的程序员应该阅读的非编程...

行者PHPer
2016/10/09
163
0

没有更多内容

加载失败,请刷新页面

加载更多

【0918】正则介绍_grep

【0918】正则介绍_grep 9.1 正则介绍_grep上 9.2 grep中 9.3 grep下 一、正则介绍 正则是一串有规律的字符串,它使用单个字符串来描述或匹配一系列符合某个语法规则的字符串。 二、grep工具 ...

飞翔的竹蜻蜓
30分钟前
4
0
为什么要在网站中应用CDN加速?

1. 网页加载速度更快 在网站中使用CDN技术最直接的一个好处就是它可以加快网页的加载速度。首先,CDN加速的内容分发是基于服务器缓存的,由于CDN中缓存了不少数据,它能够给用户提供更快的页...

云漫网络Ruan
今天
8
0
亚玛芬体育(Amer Sports)和信必优正式启动合作开发Movesense创新

亚玛芬体育和信必优正式启动合作开发Movesense创新,作为亚玛芬体育的完美技术搭档,信必优利用Movesense传感器技术为第三方开发移动应用和服务。 Movesense基于传感器技术和开放的API,测量...

symbiochina88
今天
4
0
创龙TI AM437x ARM Cortex-A9 + Xilinx Spartan-6 FPGA核心板规格书

SOM-TL437xF是一款广州创龙基于TI AM437x ARM Cortex-A9 + Xilinx Spartan-6 FPGA芯片设计的核心板,采用沉金无铅工艺的10层板设计,适用于高速数据采集和处理系统、汽车导航、工业自动化等领...

Tronlong创龙
今天
5
0
好程序员Java学习路线分享MyBatis之线程优化

  好程序员Java学习路线分享MyBatis之线程优化,我们的项目存在大量用户同时访问的情况,那么就会出现大量线程并发访问数据库,这样会带来线程同步问题,本章我们将讨论MyBatis的线程同步问...

好程序员官方
今天
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部