簡體   English   中英

在Unix C中使用管道

[英]Working with pipes in Unix C

我在使用C管道時遇到了很大的麻煩。我應該從命令行接受參數(例如:./myprogram 123 45 67),一次將一個字符的參數讀入緩沖區,將字符發送到要計算的子進程,然后返回讀取到父進程的字符總數。 我的代碼如下(注意:評論是我應該做的):

// Characters from command line arguments are sent to child process
// from parent process one at a time through pipe.
// Child process counts number of characters sent through pipe.
// Child process returns number of characters counted to parent process.
// Parent process prints number of characters counted by child process.

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

static int toChild[2];
static int fromChild[2];
static char buffer;

int main(int argc, char **argv)
{
    int status;
    int nChars = 0;
    pid_t   pid;

    pipe(toChild);
    pipe(fromChild);

    if ((pid = fork()) == -1) {
        printf("fork error %d\n", pid);
        return -1;
    }
    else if (pid == 0) {
        close(toChild[1]);
        close(fromChild[0]);
        // Receive characters from parent process via pipe
        // one at a time, and count them.

        int count = 0;
        printf("child about to read\n");
        while(read(toChild[0], &buffer, 1)){
            count++;
        }
        // Return number of characters counted to parent process.

        write(fromChild[1], &count, sizeof(count));
        close(toChild[0]);
        close(fromChild[1]);
        printf("child exits\n");
    }
    else {
        close(toChild[0]);
        close(fromChild[1]);
        // -- running in parent process --
        printf("CS201 - Assignment 3 - Chris Gavette\n");

        write(toChild[1], &argv[1], 1); 

        // Send characters from command line arguments starting with
        // argv[1] one at a time through pipe to child process.

        read(fromChild[0], &nChars, 1);

        // Wait for child process to return. Reap child process.
        // Receive number of characters counted via the value
        // returned when the child process is reaped.
        close(toChild[1]);
        close(fromChild[0]);
        waitpid(pid, &status, 0);

        printf("child counted %d chars\n", nChars);
        printf("parent exits\n");
        return 0;
    }
}

即使我關閉了兩個管道的兩端,子進程似乎仍然掛起。

對於初學者來說,這是錯誤的。

write(toChild[1], &count, 1) 

它最終將導致您的問題。 count是一個int ,而不是charunsigned char 你需要發送sizeof(count) 此外,按下錯誤時的讀取功能將返回EOF,這是非零,因此您的子退出條件不合適。 它應該看起來像這樣:

while(read(toChild[0], &buffer, 1) == 1)

最后,您的父進程應循環遍歷argv[]每個參數,並將每個參數作為strlen大小的緩沖區發送。

我幾乎可以肯定這就是你想要做的。 請注意,為了在知道哪個描述符用於特定目的時保持健全,我更喜歡使用#define來記錄每個進程用於讀寫的內容。 這可以擴展到任何數量的進程,順便說一下,我確信你的下一個任務不會太過分:

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

// P0_READ   - parent read source
// P0_WRITE  - parent write target
// P1_READ   - child read source
// P1_WRITE  - child write target

#define P0_READ     0
#define P1_WRITE    1
#define P1_READ     2
#define P0_WRITE    3
#define N_PIPES     4

int main(int argc, char **argv)
{
    int fd[N_PIPES], count = 0, i;
    pid_t pid;
    char c;

    if (pipe(fd) || pipe(fd+2))
    {
        perror("Failed to open pipe(s)");
        return EXIT_FAILURE;
    }

    // fork child process
    if ((pid = fork()) == -1)
    {
        perror("Failed to fork child process");
        return EXIT_FAILURE;
    }

    // child process
    if (pid == 0)
    {
        // close non P1 descriptors
        close(fd[P0_READ]);
        close(fd[P0_WRITE]);

        // get chars from input pipe, counting each one.
        while(read(fd[P1_READ], &c, 1) == 1)
            count++;

        printf("Child: count = %d\n", count);
        write(fd[P1_WRITE], &count, sizeof(count));

        // close remaining descriptors
        close(fd[P1_READ]);
        close(fd[P1_WRITE]);
        return EXIT_SUCCESS;
    }

    // parent process. start by closing unused descriptors
    close(fd[P1_READ]);
    close(fd[P1_WRITE]);

    // send each arg
    for (i=1; i<argc; ++i)
        write(fd[P0_WRITE], argv[i], strlen(argv[i]));

    // finished sending args
    close(fd[P0_WRITE]);

    // Wait for child process to return.
    wait(NULL);

    // wait for total count
    if (read(fd[P0_READ], &count, sizeof(count)) == sizeof(count))
        printf("Parent: count = %d\n", count);

    // close last descriptor
    close(fd[P0_READ]);

    return 0;
}

輸入

./progname argOne argTwo

產量

Child: count = 12
Parent: count = 12

編輯:具有子返回狀態的單個管道

從原始問題的評論看來,您的任務可能會要求將進程的返回狀態作為結果計數而不是將其返回到管道中。 這樣做,您可以使用單個管道描述符對完成此操作。 我更喜歡第一種方法,但這也適用:

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

// P0_WRITE  - parent write target
// P1_READ   - child read source

#define P1_READ     0
#define P0_WRITE    1
#define N_PIPES     2

int main(int argc, char **argv)
{
    int fd[N_PIPES], count = 0;
    pid_t pid;
    char c;

    if (pipe(fd))
    {
        perror("Failed to open pipe(s)");
        return EXIT_FAILURE;
    }

    // fork child process
    pid = fork();
    if (pid == -1)
    {
        perror("Failed to fork child process");
        return EXIT_FAILURE;
    }

    if (pid == 0)
    {
        // close non P1 descriptors
        close(fd[P0_WRITE]);

        // Return number of characters counted to parent process.
        while(read(fd[P1_READ], &c, 1) == 1)
            ++count;

        close(fd[P1_READ]);
        printf("Child: count = %d\n", count);
        return count;
    }

    // parent process. start by closing unused descriptors
    close(fd[P1_READ]);

    // eacn each arg entirely
    for (int i=1; i<argc; ++i)
        write(fd[P0_WRITE], argv[i], strlen(argv[i]));

    // finished sending args
    close(fd[P0_WRITE]);

    // Wait for child process to return.
    if (wait(&count) == -1)
    {
        perror("Failed to wait for child process");
        return EXIT_FAILURE;
    }

    printf("Parent: count = %d\n", WEXITSTATUS(count));

    return 0;
}

結果是一樣的,但請注意這是一個調試的biach,因為大多數調試器將在您的子進程上發出信號跳閘並且實際退出狀態丟失。 例如,在我的Mac上,在Xcode游戲下運行:

Failed to wait for child process: Interrupted system call

從命令行運行時給出:

Child: count = 12
Parent: count = 12

我更喜歡雙管方法的眾多原因之一。

暫無
暫無

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

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