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

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

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

时间: 2024-12-15 01:45:07

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

进程和程序:编写shell——《Unix/Linux编程实践教程》读书笔记(第8章)

1.Unix shell的功能 shell是一个管理进程和运行程序的程序.所有常用的shell都有3个主要功能: (1)运行程序: (2)管理输入和输出 (3)可编程 shell同时也是带有变量和流程控制的编程语言. 2.Unix的进程模型 一个程序是存储在文件中的机器指令序列,一般它是由编译器将源代码编译成二进制格式的代码.运行一个程序意味着将这些机器指令序列载入内存然后让处理器(CPU)逐条执行.在Unix术语中,一个可执行程序是一些机器指令机器数据的序列.一个进程是程序运行时的内存空间和设

系统调用操作文件——《Unix/Linux编程实践教程》读书笔记

1.who命令通过读系统日志的内容显示当前已经登录的用户. 2.Unix系统把数据存放在文件中,可以通过以下系统调用操作文件: open(filename, how) creat(filename, mode) read(fd, buffer, amt) write(fd, buffer, amt) lseek(fd, distance, base) close(fd) 3.进程对文件的读/写都要通过文件描述符,文件描述符表示文件和进程之间的连接. 4.每次系统调用都会导致用户模式和内核模式的切

终端控制和和信号——《Unix/Linux编程实践教程》读书笔记(第6章)

1.有些程序处理从特定设备来的数据.这些与特定设备相关的程序必须控制与设备的连接.Unix系统中最常见的设备是终端. 2.终端驱动程序有很多设置.各个设置的特定值决定了终端驱动程序的模式.为用户编写的程序通常需要设置终端驱动程序为特定的模式. 3.键盘输入分为3类,终端驱动程序对这些输入做不同的处理.大多数建代表常规数据,它们从驱动程序传输到程序.有些键调用驱动程序中的编辑函数.如果按下删除键,驱动程序将前一个字符从它的行缓冲中删除,并将命令发送到终端屏幕,使之从显示器中删除字符.最后,有些键调

目录与文件属性——《Unix/Linux编程实践教程》读书笔记

1.ls产生一个文件名的列表,它大致是这样工作的: open directory +-> read entry - end of dir? -+ |__ display file info | close directory <--------------+ 2.目录是一种特殊的文件,它的内容是文件和目录的名字.与普通文件不同的是,目录文件永远不会空,每个目录至少包含2个特殊的项,即 "."和"..",其中 "."不是当前目录,&qu

事件驱动编程——《Unix/Linux编程实践教程》读书笔记(第7章)

1.curses库 /* 基本curses函数 */ initscr(); // 初始化curses库和tty endwin(); // 关闭curses并重置tty refresh(); // 使屏幕按照你的意图显示 move(r, c); // 移动光标到屏幕的(r, c)位置 addstr(s); // 在当前位置画字符串s addch(c); // 在当前位置画字符c clear(); // 清屏 standout(); // 启动standout模式(一般使屏幕反色) standend

Unix/Linux编程实践教程

execvp在程序中启动新程序: 用fork创建新进程: forkdemo2代码: 测试fork的时候参考<Linux权威指南>阅读笔记(3)  使用了patch: [[email protected] programming]# diff -c forkdemo2.c forkdemo2_new.c > forkdemo2.patch [[email protected] programming]# patch < forkdemo2.patch patching file fo

Unix/Linux编程实践教程(0:)

本来只打算读这本书socket等相关内容,但书写得实在好,还是决定把其余的内容都读一下. 阅读联机帮助的一个示例: open系统调用: read系统调用: Unix的time: 上面的printf可以看到,一个临时的char* 指针也可以+4,希望查看ctime函数里面是否有malloc,如果有的话由谁来释放内存???没有的话为什么可以指针操作. 为解决上述疑惑,通过查看http://www.cplusplus.com/reference/ctime/ctime/以及及http://www.cp

Unix/Linux编程实践教程(三:代码、测试)

测试logfilec.c的时候,有个sendto(sock,msg,strlen(msg),0,&addr,addrlen),编译时提示: logfilec.c:30: warning: passing argument 5 of ‘sendto’ from incompatible pointer type 但是书上是这样写的,在stackoverflow搜了一下,原来是: 需要进行一个转换.

《Linux/Unix系统编程手册》读书笔记7 (/proc文件的简介和运用)

<Linux/Unix系统编程手册>读书笔记 目录 第11章 这章主要讲了关于Linux和UNIX的系统资源的限制. 关于限制都存在一个最小值,这些最小值为<limits.h>文件中的常量. 通过cat 命令查看: [email protected]:~/Code/tlpi$ cat /usr/include/limits.h /* Copyright (C) 1991, 1992, 1996, 1997, 1998, 1999, 2000, 2005 Free Software