I/O重定向和管道——《Unix/Linux編程實踐教程》讀書筆記(第10章)

一、I/O重定向的概念與緣由 及 標準輸入、輸出的標準錯誤的定義 shell

因此的Unix I/O重定向都基於標準數據流的原理。三個數據了分別以下: 編程

    1)標準輸入——須要處理的數據流 數組

    2)標準輸出——結果數據流 緩存

    3)標準錯誤輸出——錯誤消息流 ide


概念:因此的Unix工具都使用文件描述符0、1和2。標準輸入文件的描述符是0,標準輸出的文件描述符是1,而標準錯誤輸出的文件描述符則是2。Unix假設文件描述符0、一、2已經被打開,能夠分別進行讀寫操做。 工具

一般經過shell命令行運行Unix系統工具時,stdin、stdout、stderr鏈接在終端上。所以,工具從鍵盤讀取數據而且把輸出和錯誤消息寫到屏幕。 oop

大部分的Unix工具處理從文件或標準輸入讀入的數據。若是在命令行上給出了文件名,工具將從文件讀取數據。若無文件名,程序則從標準輸入讀取數據。從另外一方面說,大多數程序並不接收輸出文件名;它們老是將結果寫到文件描述符1,並將錯誤消息寫到文件描述符2。若是但願將進程的輸出寫道文件或另外一個進程的輸入去,就必須重定向相應的文件描述符。 ui

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

最低可用文件描述符(lowest-available-fd)原則:文件描述符是一個數組的索引號。每一個進程都有其打開的一組文件。這些打開的文件被保持在一個數組中。文件描述符即爲某文件在此數組中的索引。當打開文件時,爲此文件安排的描述符老是此數組中最低可用位置的索引。 spa

將文件描述符0、一、2的概念和最低可用文件描述符原則結合使用,便可理解I/O重定向的工做原理


二、重定向標準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、一、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;
}


三、管道編程

(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;
}
相關文章
相關標籤/搜索