命名管道,两个孩子之间的通信

时间:2018-12-25 22:11:58

标签: linux

我的代码有问题。我想在两个孩子之间进行沟通。其中一个是服务器,它打开一个文件并将每个字母发送到第二个进程。第二个过程是计算字母,它应该制作一个新文件并保存结果。我对最后一步有问题,因为第一个过程比第二个过程完成得更快,这导致程序结束。我不知道如何解决。寻找一些技巧:)。

您在这里得到结果。

enter image description here

我的代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>

//stale
#define FIFO "my_fifo"
#define SIZE 26

//zmienne globalne
int desk; //deskryptor pliku
int tab[SIZE];

//prototypy funkcji
void parentKillAll();
void server(FILE * file);
void client();
void cleanUp(FILE * file);
int checkEntryData(int argc, char *argv);
void replaceTabWithZero(int * tab);
void countLetters(int * tab, char ch);
void saveResults(int * tab, char *title);
void showTab(int * tab);

int main(int argc, char *argv[]) {
    if (!checkEntryData(argc, argv[1]))
        return 1;

    replaceTabWithZero(tab);
    FILE *file = fopen(argv[1], "r");
    umask(0);
    mkfifo(FIFO, 0666);

    if (file) {
        if (fork() == 0) {
            server(file);   
            exit(0);
        } else if (fork() == 0) {
            client();
            saveResults(tab, strcat(argv[1], "Result"));
            showTab(tab);
            exit(0);
        } else {
            cleanUp(file);
            parentKillAll();
        }
    } else {
        perror("Error");
    }

    return 0;
}

void parentKillAll() {
    sleep(1);
    kill(0, SIGKILL);
    exit(0);
}

void server(FILE * file) {
    char ch;
    while ((ch = fgetc(file)) != EOF) {
        desk = open(FIFO, O_WRONLY);
        write(desk, &ch, 1);
    }
}

void client() {
    char ch;
    while (1) {
        desk = open(FIFO, O_RDONLY);
        read(desk, &ch, 1);
        countLetters(tab, ch);
        printf("%c", ch);
    }
}

void cleanUp(FILE *file) {
    wait(0);
    fclose(file);
    close(desk);
}

int checkEntryData(int argc, char *argv) {
    if (argc < 2) {
        fprintf(stderr, "Nie poprawna ilosc argumentow\n");
        return 0;
    }

    if (access(argv, F_OK)) {
        fprintf(stderr, "Podany plik \'%s\' nie istnieje\n", argv);
        return 0;
    }

    if (access(argv, R_OK)) {
        fprintf(stderr, "Brak uprawnien do odczytu pliku \'%s\'\n", argv);
        return 0;
    }

    return 1;
}

void replaceTabWithZero(int * tab) {
    for (int i = 0; i < SIZE; i++)
        tab[i] = 0;
}

void countLetters(int *tab, char ch) {
    int chVal = ch;

    if (chVal > 92)
        chVal -= 32;

    if (chVal > 64 && chVal < 91)
        tab[chVal-65] += 1;               
}

void saveResults(int *tab, char * title) {
    FILE *plik = fopen(title, "w");
    if (plik) {
        for (int i = 0; i < SIZE; i++)
            fprintf(plik, "%c - %d\n", (i+97), tab[i]);
    } else {
        perror("Error");
    }

    fclose(plik);
}

void showTab(int * tab) {
    for (int i = 0; i < SIZE; i++)
        printf("\n%d", tab[i]);
}

1 个答案:

答案 0 :(得分:0)

真正的问题是客户端进程永远无法完成,因为它运行了无限的while(1)循环而没有任何退出条件。

您应该重写它,以使其在读取所有可用数据后退出:

void client() {
        char ch;
        // Open the fifo only once, instead of once per character
        desk = open(FIFO, O_RDONLY);

        // Loop until there is no more data to read
        while(read(desk, &ch, 1) > 0) {
                countLetters(tab, ch);
                printf("%c", ch);                       
        }       
}

从技术上讲,这足以使其起作用,但您还应该研究一系列其他问题:

  • 您应该有两个wait(0)调用,以便您等待两个进程,并且不应尝试杀死任何东西。
  • 服务器进程只能打开一次fifo,而不是每个字符一次。
  • 您应将fgetc的输出与EOF 比较,然后再将值强制为char。由于这样做之后,在ISO-8859-1终端上运行程序将导致其混淆EOF和字母ÿ
  • 即使您不知道该数组有多少空间,也要在strcat上使用argv[1]。您应该使用自己的已知长度的缓冲区。
  • 您应该检查所有系统调用的返回值,以确保它们成功。用access进行检查,然后假设一切正常,因为通话可能由于其他原因而失败,因此效果不佳。
  • 正常的Unix行为是成功退出为0,错误退出为> = 1。
  • 当直接使用read / write时,最好使用较大的缓冲区(例如65536字节而不是1)。 fgetc之类的stdio函数已经在后台使用了更大的缓冲区。
  • 使用命名管道显然是可行的,但是由于同时生成了两个进程,因此使用未命名的管道会更自然。