简体   繁体   English

在Unix C中使用管道

[英]Working with pipes in Unix C

I am having serious trouble working with pipes in C. I'm supposed to take in arguments from the command line (example: ./myprogram 123 45 67), read the arguments one character at a time into a buffer, send the character to the child process to be counted, and then return the total number of characters read to the parent process. 我在使用C管道时遇到了很大的麻烦。我应该从命令行接受参数(例如:./myprogram 123 45 67),一次将一个字符的参数读入缓冲区,将字符发送到要计算的子进程,然后返回读取到父进程的字符总数。 My code is as follows(note: the comments are what I'm supposed to be doing): 我的代码如下(注意:评论是我应该做的):

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

The child process seems to hang even though I've closed both ends of both pipes. 即使我关闭了两个管道的两端,子进程似乎仍然挂起。

For starters, this is wrong. 对于初学者来说,这是错误的。

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

It will eventually contribute to your problem. 它最终将导致您的问题。 count is a int , not char or unsigned char . count是一个int ,而不是charunsigned char You need to send sizeof(count) . 你需要发送sizeof(count) Also, the read-function upon hitting an error will return EOF, which is non-zero, so your child exit condition is not appropriate. 此外,按下错误时的读取功能将返回EOF,这是非零,因此您的子退出条件不合适。 it should look something like this: 它应该看起来像这样:

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

Finally, your parent process should cycle through each argument in argv[] sending each as a strlen sized buffer. 最后,您的父进程应循环遍历argv[]每个参数,并将每个参数作为strlen大小的缓冲区发送。

I'm nearly certain this is what you're trying to do. 我几乎可以肯定这就是你想要做的。 Note that in order to maintain sanity in knowing which descriptor is used for a specific purpose, I prefer using a #define to note what each process uses for reading and writing. 请注意,为了在知道哪个描述符用于特定目的时保持健全,我更喜欢使用#define来记录每个进程用于读写的内容。 This can be extended to any number of processes, btw, which I'm sure is not too far down the line for your next assignment: 这可以扩展到任何数量的进程,顺便说一下,我确信你的下一个任务不会太过分:

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

Input 输入

./progname argOne argTwo

Output 产量

Child: count = 12
Parent: count = 12

Edit: Single Pipe with Child Return Status 编辑:具有子返回状态的单个管道

It seems from the comments of the original question your assignment may call for reaping the return status of the child process as the result count rather than returning it in a pipe. 从原始问题的评论看来,您的任务可能会要求将进程的返回状态作为结果计数而不是将其返回到管道中。 In doing so, you can do this with a single pipe-descriptor pair. 这样做,您可以使用单个管道描述符对完成此操作。 I prefer the first method, but this works as well: 我更喜欢第一种方法,但这也适用:

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

The results are the same, but note this is a biach to to debug as most debuggers will signal-trip on your child process and the real exit status is lost. 结果是一样的,但请注意这是一个调试的biach,因为大多数调试器将在您的子进程上发出信号跳闸并且实际退出状态丢失。 On my Mac, for example, running this under Xcode trips: 例如,在我的Mac上,在Xcode游戏下运行:

Failed to wait for child process: Interrupted system call

while running from the command line gives: 从命令行运行时给出:

Child: count = 12
Parent: count = 12

One of the many reasons I prefer the two-pipe methodology. 我更喜欢双管方法的众多原因之一。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM