简体   繁体   中英

Linux inter-processes communication

I have to implement a testing program(quiz), which besides displaying the question and reading the answer, it has to display the time left at each one minute past. After finishing the examination time, by finishing the questions or by running out of time,the program has to get back from the beginning, when before the start, we enter the name of the candidate. This implementation has to be done using processes. Below is the code that i have written so far. The problem is that i am not sure that i am making a good communication between the process and the subprocesses, especially because i am not using a pipe. Some opinions?

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

Pipes as such don't require you to provide a regular file, but they can have a unique, globally visible name, which is provided by a (unused) filename you have to specify. The contents of the file, if any, is handled by the library.

There are (simple) pipes for communication among related processes (such as a child and a parent process in the same process hierarchy) where the pipe handle can easily be passed to other processes.

The other flavor is called 'named pipes' for processes with any relation, where one can lookup the pipe handle using the global name (as explained in the answer of the question I linked). You can think of a pipe as of a directly connected speaking tube, allowing two processes to chitchat about whatever they like, using read and write functions. On Linux, a pipe is a simplex (at a time, one talks, the other one listens). One would nee two pipes for bidirectional async IO in this case ( https://unix.stackexchange.com/questions/53641/how-to-make-bidirectional-pipe-between-two-programs ). The immediate buffer for input and output is abstracted. Its just like with network sockets.

I'd suggest to compile this nice example in the accepted answer to play around with: https://stackoverflow.com/a/2789967/1175253

Edit

Example code with error handling. Treat pipe.h & pipe.c as a library (link NamedPipeReader and NamedPipeWriter against it). This code would need further testing, however, the code is able to (re)open named pipes in any order.


pipe.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_ */ 


pipe.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; } 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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