4

我必须实现一个测试程序(测验),除了显示问题和阅读答案之外,它还必须显示过去一分钟的剩余时间。考试时间结束后,通过做完题目或时间用完,程序必须从头返回,在开始之前,我们输入考生的姓名。这个实现必须使用进程来完成。以下是我到目前为止编写的代码。问题是我不确定我是否在进程和子进程之间进行了良好的通信,特别是因为我没有使用管道。一些意见?

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

#define T 180

void firstChildAction(){
    static const char filename[] = "/home/osystems01/laura/text";
    char question[100];
    char answer[100];

    FILE *file = fopen(filename,"r");
    if(file != NULL){
        while(fgets(question,sizeof question,file) != NULL){
            fputs(question, stdout);
            scanf("%s",&answer);
        }
        fclose(file);
    }
    else{
        perror(filename);
    }
}

void secondChildAction(){
    int i;
    for(i = T; i >= 0; i-=60){
        if( i/60 != 0){
            printf("You have %d %s left.\n", i/60,(i/60 >   1)?"minutes":"minute");
            sleep(60);
        }
        else{
            printf("The time is over\n");
            break;
        }
    }
}

int main() {
    pid_t pidA;
    pid_t pidB;
    pid_t wPid;
    char name[20];
    while(1){
        printf("Enter the candidate name or Quit to exit: \n");
        scanf("%s",&name);
        if(strcmp(name,"Quit") == 0 || strcmp(name,"quit") == 0){
            printf("The program is terminating.....\n");
            break;
        }
        else{
            pidA = fork();
            if(pidA == 0){
                firstChildAction();
                exit(0);
            }
            else{
                pidB = fork();
                if(pidB == 0){
                    secondChildAction();
                    exit(0);
                }
            }
            int status;
            while(wPid = wait(&status)) > 0 ){
                if(WIFEXITED(status)){
                    int result = WEXITSTATUS(status);
                    printf("Exit status of %d is %d\n", wPid, result);
                    if(wPid == pidA){
                        kill(pidB,SIGTERM);
                        kill(pidA,SIGTERM);
                    }
                    else if(wPid == pidB){
                        kill(pidA,SIGTERM);
                        kill(pidB,SIGTERM);
                    }
                }
            }
        }
    }
    return 0;
}
4

1 回答 1

1

这样的管道不需要您提供常规文件,但它们可以具有唯一的、全局可见的名称,该名称由您必须指定的(未使用的)文件名提供。文件的内容(如果有)由库处理。

有(简单的)管道用于相关进程(例如同一进程层次结构中的子进程和父进程)之间的通信,其中管道句柄可以很容易地传递给其他进程。

对于具有任何关系的进程,另一种风格称为“命名管道”,其中可以使用全局名称查找管道句柄(如我链接的问题的答案中所述)。您可以将管道视为直接连接的说话管,允许两个进程使用读取和写入功能闲聊他们喜欢的任何内容。在 Linux 上,管道是一个单工(一次,一个说话,另一个听)。在这种情况下,需要两个用于双向异步 IO 的管道(https://unix.stackexchange.com/questions/53641/how-to-make-bidirectional-pipe-between-two-programs)。输入和输出的即时缓冲区是抽象的。就像网络套接字一样。

我建议在接受的答案中编译这个很好的例子来玩:https ://stackoverflow.com/a/2789967/1175253

编辑

带有错误处理的示例代码。将 pipe.h 和 pipe.c 视为一个库(针对它链接 NamedPipeReader 和 NamedPipeWriter)。此代码需要进一步测试,但是,该代码能够以任何顺序(重新)打开命名管道。


管道.h

#ifndef PIPE_H_
#define PIPE_H_

//C headers
#include <errno.h>
#include <assert.h>

//Linux headers
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#ifdef __cplusplus
extern "C"
{
#endif

int open_named_pipe(const char* const name, const int permissions, const int mode, int* pipe_created);

#ifdef __cplusplus
}
#endif

#endif /* PIPE_H_ */


管道.c

#include "pipe.h"

#include <stdio.h>

int open_named_pipe(const char* const name, const int permissions, const int mode, int* pipe_created)
{
    int fd;

    assert(name);
    assert(permissions);
    assert(pipe_created);

    //Create or use an existing pipe special file
    if (0 == mkfifo(name, permissions))
    {
        *pipe_created = 1;
        printf("Successfully created named pipe '%s'\n", name);
    }
    else
    {
        switch (errno)
        {
        case EEXIST:
            //this is OK, as the other process might already has created the special file
            printf("Opened existing named pipe '%s'\n", name);
            break;
        default:
            fprintf(stderr, "Failed to create or access named pipe '%s'\n", name);
            perror("    ");
            return -1;
        };
    }

    fd = open(name, mode);
    if (fd < 0)
    {
        perror("Could not open pipe for writing");
        if (*pipe_created)
        {
            if (0 == unlink(name))
            {
                *pipe_created = 0;
            }
            else
            {
                perror("Failed to unlink named pipe");
            }
        }
    }

    return fd;
}

NamedPipeReader.c

#include <stdlib.h>
#include <stdio.h>
#include <signal.h>

#include "pipe.h"

//Globals
const char* const pipe_name = "/tmp/myfifo";
const int pipe_permissions = 0600;
const size_t read_buffer_size = 1024; //[bytes]
const size_t read_retry_delay = 25000; //[us]

int fd = -1;
int pipe_created = 0;
char* read_buffer = NULL;

//Handles EPIPE signal
void signal_handler(int signal)
{
    fprintf(stderr, "cought signal %d\n", signal);
}

//Handles cleanup on exit
void exit_handler(void)
{
    if (read_buffer)
        free(read_buffer);

    if (fd >= 0)
        close(fd);

    //if this process created the FIFO, we unlink it
    if (pipe_created == 0)
        unlink(pipe_name);
}

int main()
{
    //Locals
    int run = 1;
    int received = 0;

    //Install the exit handler
    atexit(&exit_handler);
    signal(EPIPE, signal_handler);
    signal(EACCES, signal_handler);

    //Allocate the buffer
    read_buffer = (char*) malloc(read_buffer_size);
    if (!read_buffer)
    {
        perror("Failed to allocate buffer");
        return EXIT_FAILURE;
    }

    restart: ;

    //Close if already open
    if(fd >= 0)
        close(fd);
    //Create or use an existing pipe special file
    fd = open_named_pipe(pipe_name, pipe_permissions, O_RDONLY, &pipe_created);
    if (fd < 0)
    {
        return EXIT_FAILURE;
    }

    while (run)
    {
        assert(fd >= 0);
        assert(read_buffer_size > 1);

        received = read(fd, read_buffer, read_buffer_size - 1);

        if (received > 0)
        {
            //add a NUL char for string termination
            read_buffer[received] = '0';
            printf("local process %llu received: %s\n", (unsigned long long) getpid(), read_buffer);
        }
        else if (received == 0)
        {
            //EOF reached, this happens in case the writer has closed its handle.
            //Perform a delayed restart and recreate the named pipe
            usleep(read_retry_delay);
            printf("Restarting...\n");
            goto restart;
        }
        else
        {
            switch (errno)
            {
            case EAGAIN:
                //Wait, if the pipe is empty,
                //happens when opened with the O_NONBLOCK flag
                usleep(read_retry_delay);
                break;
            case EPIPE:
            case EBADF:
            case EBADFD:
                perror("Pipe error");
                printf("Restarting...\n");
                goto restart;
            default:
                perror("Pipe error");
                return EXIT_FAILURE;
            };
        }
    }

    return EXIT_SUCCESS;
}

NamedPipeWriter.c

#include <stdlib.h>
#include <stdio.h>
#include <signal.h>

#include "pipe.h"

//Globals
const char* const pipe_name = "/tmp/myfifo";
const int pipe_permissions = 0600;
const size_t write_buffer_size = 1024; //[bytes]
const size_t write_retry_delay = 25000; //[us]
const size_t write_interval = 1000000;

int fd = -1;
int pipe_created = 0;
char* write_buffer = NULL;

//Handles EPIPE signal
void signal_handler(int signal)
{
    fprintf(stderr, "cought signal %d\n", signal);
}

//Handles cleanup on exit
void exit_handler(void)
{
    if (write_buffer)
        free(write_buffer);

    if (fd >= 0)
        close(fd);

    //if this process created the FIFO, we unlink it
    if (pipe_created == 0)
        unlink(pipe_name);
}

//Main Function
int main()
{
    //Locals
    int run = 1;
    int sent = 0;
    int msg_len = 0;

    //Install the exit handler
    atexit(&exit_handler);
    signal(EPIPE, signal_handler);
    signal(EACCES, signal_handler);

    //Allocate the buffer
    write_buffer = (char*) malloc(write_buffer_size);
    if (!write_buffer)
    {
        perror("Failed to allocate buffer");
        return EXIT_FAILURE;
    }

    restart: ;

    //Close if already open
    if(fd >= 0)
        close(fd);
    //Create or use an existing pipe special file
    fd = open_named_pipe(pipe_name, pipe_permissions, O_WRONLY, &pipe_created);
    if (fd < 0)
    {
        return EXIT_FAILURE;
    }

    while (run)
    {
        //Print message into the buffer
        msg_len = snprintf(write_buffer, write_buffer_size, "Greetings from process %llu\n", (unsigned long long) getpid());

        {
            char* msg_ptr = write_buffer;
            char* msg_end = write_buffer + msg_len;
            while (msg_ptr != msg_end)
            {
                assert(fd >= 0);
                assert(msg_ptr < msg_end);
                sent = write(fd, msg_ptr, msg_end - msg_ptr);
                if (sent > 0)
                {
                    msg_ptr += sent;
                }
                else if (sent == 0)
                {
                    //retry delay for nonblocking writes
                    usleep(write_retry_delay);
                }
                else
                {
                    switch (errno)
                    {
                    case EAGAIN:
                        //Wait, if the pipe is full,
                        //happens when opened with the O_NONBLOCK flag
                        usleep(write_retry_delay);
                        break;
                    case EPIPE:
                    case EBADF:
                    case EBADFD:
                        perror("Pipe error");
                        printf("Restarting...\n");
                        goto restart;
                    default:
                        perror("Pipe error");
                        return EXIT_FAILURE;
                    };
                }
            }

            printf("Written: %s\n", write_buffer);
            usleep(write_interval);
        }
    }

    return EXIT_SUCCESS;
}
于 2013-11-02T22:42:47.803 回答