簡體   English   中英

使用未命名的管道發送信息

[英]Sending information with unnamed pipes

我的程序必須使用未命名的管道發送一些字節的信息。 我有一個名為“ input”的txt文件,應該由程序讀取,並且它的信息必須發送和寫入另一個名為“ output”的文件中。 我還必須使用read(),write(),open()函數。 我的代碼如下所示:

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <string.h>
#define BUFSIZE 25

int main( int argc, char *argv[] ) {

    srand(time(NULL));
    pid_t pid;
    int mypipefd[2];
    int ret;
    char buf[BUFSIZE];
    int output;
    int stream;
    int nbytes;
    ret = pipe(mypipefd);

    if( ret == -1 ) {
        perror( "pipe error");
        exit(1);
    }
    pid = fork();

    if( pid == -1 ) {
        perror( "FORK ERROR...");
        exit(2);
    }
    if( pid == 0 ) {
        /* CHILD */
        printf(" Child process...\n");
        stream = open("input.txt", O_RDONLY);
        if (close(mypipefd[0]) == -1 ) {
            perror("ERROR CLOSING PIPE");
            exit(3);
        }
        while ( (nbytes = read(stream, buf, BUFSIZE)) > 0 ) {
            sleep(rand() %2);
            write(mypipefd[1], buf, nbytes );
        }
        if ( close(stream) == -1 ) {
        perror("ERROR CLOSING STREAM");
        exit(4);
        }
    }
    else {
        /* PARENT */
        printf(" Parent process...\n");
        output = open("output.txt", O_CREAT | O_WRONLY, 00777);
        while ( (nbytes = read(mypipefd[0], buf, BUFSIZE)) > 0 ) {
            write(output, buf, nbytes);
        }
        printf("buf: %s\n", buf);
        if (close(output) == -1) {
            perror("ERROR CLOSING OUTPUT");
            exit(5);
        }
        if (close(mypipefd[1]) == -1 ) {
            perror("ERROR CLOSING PIPE");
            exit(6);
        }
    }


    return 0;

}

不幸的是,代碼無法在終端屏幕上顯示

在我嘗試while循環並立即發送所有信息之前,它可以工作,但是輸出文件看起來像這個輸出文件

而輸入文件看起來像這個輸入文件

主要錯誤是父級必須在父級讀取循環之前 (而不是之后)執行close(mypipefd[1]) )。 這樣可以防止在孩子完成書寫后,父母在管道上看到EOF。

另外,您在父級中缺少一個waitpid

父級中bufprintf在錯誤的位置[在讀取循環之后]。 在這一點上,不能保證buf擁有正確的數據或正確的零終止符。 這就是為什么stdout在末尾有一些垃圾字符。

因此,除了輸出到輸出文件之外,循環還應該輸出到stdout,但是應該使用fwrite因為buf不能保證被零終止。

我在最初的帖子中錯過了這一點,所以我已經更正了。

根據我的最高評價,孩子應該在[可能]部分寫入管道時循環執行。 我編碼了。

這是帶注解並修正錯誤的版本:

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#define BUFSIZE 25

int main( int argc, char *argv[] ) {

    srand(time(NULL));
    pid_t pid;
    int mypipefd[2];
    int ret;
    char buf[BUFSIZE];
    int output;
    int stream;
    int nbytes;

    ret = pipe(mypipefd);
    if( ret == -1 ) {
        perror( "pipe error");
        exit(1);
    }

    pid = fork();
    if( pid == -1 ) {
        perror( "FORK ERROR...");
        exit(2);
    }

    if( pid == 0 ) {
        /* CHILD */
        printf(" Child process...\n");
        stream = open("input.txt", O_RDONLY);
        if (close(mypipefd[0]) == -1 ) {
            perror("ERROR CLOSING PIPE");
            exit(3);
        }

        while ( (nbytes = read(stream, buf, BUFSIZE)) > 0 ) {
            sleep(rand() %2);

#if 0
            write(mypipefd[1], buf, nbytes );
#else
            // NOTE: this _should_ work but adds extra at the end
            int off;
            int wlen;
            for (off = 0;  nbytes > 0;  off += wlen, nbytes -= wlen) {
                wlen = write(mypipefd[1], buf + off, nbytes );
                if (wlen <= 0)
                    break;
            }
#endif
        }

        if ( close(stream) == -1 ) {
        perror("ERROR CLOSING STREAM");
        exit(4);
        }

        // NOTE/FIX: child must close it's side of the pipe
#if 1
        close(mypipefd[1]);
#endif
    }

    else {
        /* PARENT */
        printf(" Parent process...\n");

        // NOTE/FIX: this must be closed _before_ the read loop -- holding it
        // open prevents parent from seeing EOF on pipe
#if 1
        if (close(mypipefd[1]) == -1 ) {
            perror("ERROR CLOSING PIPE");
            exit(6);
        }
#endif

#if 1
        printf("buf: ");
#endif

        output = open("output.txt", O_CREAT | O_WRONLY, 00777);
        while ( (nbytes = read(mypipefd[0], buf, BUFSIZE)) > 0 ) {
            write(output, buf, nbytes);
#if 1
            fwrite(buf,1,nbytes,stdout);
#endif
        }

        // NOTE/BUG: the buffer at this point will only have the data from
        // the _last_ read and may not be null terminated
#if 0
        printf("buf: %s\n", buf);
#else
        printf("\n");
#endif

        if (close(output) == -1) {
            perror("ERROR CLOSING OUTPUT");
            exit(5);
        }

        // NOTE/BUG: this must be closed _before_ the parent's read loop
#if 0
        if (close(mypipefd[1]) == -1 ) {
            perror("ERROR CLOSING PIPE");
            exit(6);
        }
#endif

        // NOTE/FIX: this is missing (prevents orphan/zombie child process)
#if 1
        waitpid(pid,NULL,0);
#endif
    }

    return 0;
}

更新:

但我不明白“ for”循環在這里做什么

寫入管道會產生“短寫入”(例如,您想寫入20,但返回值(即實際寫入的字節數))返回15。您必須索引緩沖區並在隨后的操作中寫入其余字節寫道。

內核對單個原子寫操作中可以寫多少個字節有一個限制(例如,如果您執行了write(mypipefd[1],buf,10000000) ,則內核沒有為如此大的寫操作分配空間,因此它將返回可以附加到管道緩沖區的值(在內核中)。

另外,假設內核管道緩沖區可以容納64個字節。 然后將大小為64的緩沖區寫入其中。 讀者可能只讀取32個字節。 因此,第一次寫就可以了。 然后,讀取器讀取32個字節。 因此,下一次寫入64的管道時,只有32個字節的空間,因此寫入將返回32

程序必須顯示:“ buf:這是ra”,然后顯示“ buf:ndom文本”

好吧,我已經解決了

最后,我需要在所有地方實現錯誤處理。

我注釋了要添加錯誤和處理的地方,以及需要查找的內容。

無論如何,這是更新版本。 我留在// NOTE/*注釋中,但刪除了#if/#endif對以使其更容易閱讀。

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#define BUFSIZE 25

int
main(int argc, char *argv[])
{

    srand(time(NULL));
    pid_t pid;
    int mypipefd[2];
    int ret;
    char buf[BUFSIZE];
    int output;
    int stream;
    int nbytes;

    ret = pipe(mypipefd);
    if (ret == -1) {
        perror("pipe error");
        exit(1);
    }

    pid = fork();
    if (pid == -1) {
        perror("FORK ERROR...");
        exit(2);
    }

    if (pid == 0) {
        /* CHILD */
        printf(" Child process...\n");
        stream = open("input.txt", O_RDONLY);
        if (close(mypipefd[0]) == -1) {
            perror("ERROR CLOSING PIPE");
            exit(3);
        }

        while ((nbytes = read(stream, buf, BUFSIZE)) > 0) {
            sleep(rand() % 2);

            // NOTE/FIX: writing to pipes _can_ generate a _short_ write. that
            // is, (e.g.) if the length given to write is 20, the return value
            // may be only 15. this means that the remaining 5 bytes must be
            // sent in a second/subsequent write
            int off;
            int wlen;
            for (off = 0;  nbytes > 0;  off += wlen, nbytes -= wlen) {
                wlen = write(mypipefd[1], buf + off, nbytes);
                if (wlen < 0) {
                    perror("ERROR WRITING TO FILE");
                    exit(3);
                }
                if (wlen == 0)
                    break;
            }
        }

        if (close(stream) == -1) {
            perror("ERROR CLOSING STREAM");
            exit(4);
        }

        // NOTE/FIX: child must close it's side of the pipe
        // NOTE/ERRCODE: check error code here
        close(mypipefd[1]);
    }

    else {
        /* PARENT */
        printf(" Parent process...\n");

        // NOTE/FIX: this must be closed _before_ the read loop -- holding it
        // open prevents parent from seeing EOF on pipe
        if (close(mypipefd[1]) == -1) {
            perror("ERROR CLOSING PIPE");
            exit(6);
        }

        // NOTE/ERRCODE: this should be checked for -1 (i.e. output file
        // could not be opened for file permission, etc. or other reasons
        // similar to those for the file write below)
        output = open("output.txt", O_CREAT | O_WRONLY, 00777);

        // NOTE/FIX: we read one less than buffer size to allow for adding an
        // artificial zero byte at the end
        while ((nbytes = read(mypipefd[0], buf, BUFSIZE - 1)) > 0) {
            // NOTE/ERRCODE: error handling _could_ be added here but it would
            // be rare (e.g. filesystem has an I/O error because it's full or
            // marked R/O because of an I/O error on the underlying disk)
            write(output, buf, nbytes);

            // write partial buffer to stdout
            buf[nbytes] = 0;
            printf("buf: %s\n",buf);
        }

        if (close(output) == -1) {
            perror("ERROR CLOSING OUTPUT");
            exit(5);
        }

        // NOTE/FIX: this is missing (prevents orphan/zombie child process)
        // NOTE/ERRCODE: yes, this _can_ have an error return but here it's
        // unlikely because we _know_ that pid is valid
        // what can be done is to do:
        //   int status;
        //   waitpid(pid,&status,0)
        // then process the return code from the child using the W* macros
        // provided (e.g. WIFEXITED, WSTATUS) on status
        waitpid(pid, NULL, 0);
    }

    return 0;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM