Nano Hash - криптовалюты, майнинг, программирование

Почему эта программа читает повторяющиеся строки в зависимости от того, следует ли ждать() или нет для дочернего элемента?

Я пишу небольшую программу с системными вызовами, которая анализирует входной файл, содержащий команды и их параметры в каждой строке, сравнивает их, выводя истекшее время, и ждет, пока они запустятся один за другим, или выполняет их параллельно (в основном, независимо от того, ждать() порожденных детей или не в цикле)

Проблема в том, что я должен использовать gets(), который, как я понимаю, устарел и чрезвычайно опасен, но все же будет обязательным требованием для моих экзаменов CS (вздох).

Я делаю это, анализируя каждую строку с gets() и strtok() с файлом, перенаправленным на stdin.

Кажется, это работает нормально, если последним параметром является p, то есть если дочерние элементы должны запускаться параллельно, не выполняя wait() в каждом цикле анализа новой строки и создания дочерних элементов.

НО, если последним параметром является s (или любой другой), gets() кажется странным образом считывает повторяющиеся строки из файла и порождает в два раза больше дочерних элементов. Я совершенно не понимаю, почему и как это происходит.

Например, если входной файл содержит данные ниже, а последний параметр равен p:

Input file:

ls -l
ps
ps -lu user

Output file:
Comm 1: ls
Elapsed time: 0000.001824
Comm 2: ps
Elapsed time: 0000.014160
Comm 3: ps
Elapsed time: 0000.015795

Но, если входной файл содержит то же самое и последним параметром является s:

Input file:

ls -l
ps
ps -lu user

Output file:
Comm 1: ls
Elapsed time: 0000.001612
Comm 2: ps
Elapsed time: 0000.008564
Comm 3: ps
Elapsed time: 0000.009006
Comm 4: ps
Elapsed time: 0000.007379
Comm 5: ps
Elapsed time: 0000.008407
Comm 6: ps
Elapsed time: 0000.008975
// ./prog inputFile logFile p
// inputFile: file with a series of lines of max. 200 chars which consist of:
//  command p1 p2 ... pn (between 0 and 30 parameters)

// logFile: where the times are going to be logged
//  For each command parsed and executed:
//      command <i>: <name of the program executed>
//      Elapsed time: xxxx.yyyyyy

// p is either "s" or "p"
//  If it's "s", the commands must be executed in series
//  If it's "p", the commands must be executed in parallel

#include <unistd.h> // exec, read
#include <fcntl.h>  // open
#include <stdio.h>  // printf
#include <stdlib.h> // exit
#include <string.h> // strtok
#include <time.h>   // timeval
#include <sys/wait.h> 

int main(int argc, char *argv[]) {
    if (argc < 4) {
        printf("Usage: ./prog inputFile logFile p\n"); exit(0);
    }
    
    int fdCom = open(argv[1], O_RDONLY); // opens the input file with the instructions in it
    
    int fdLog = creat(argv[2], 0600);   // creates the logFile
    
    int fd[2]; pipe(fd); // pipe son->father
    
    close(0);dup(fdCom); // place the inputFile into stdin in order to use gets() on it
    close(fdCom);
    int lineNumber = 0;
    char buf[200];      // max length of a line
    char *com[21];      // command + max number of parameter
    
    while (gets(buf) != NULL) { // While there's still lines left in the input file...
        lineNumber++;
        int i = 0;
        com[i++] = strtok(buf, " "); // Parse the command
        while ((com[i] = strtok(NULL, " ")) != NULL) { // And its parameters
            i++;
        }
        
        printf("Leftover buf is: %s\n", buf);                          
        printf("The first three parsed parts are:%s %s %s\n", com[0], com[1], com[2]);
        
        if (fork() == 0) {  // child
            if (fork() == 0) { // create another child in charge of running the current command
                close(fd[0]);close(fd[1]);
                execvp(com[0], com);
            } else {    // wait for it to finish
                close(1); dup(fd[1]); // stdout to pipe output
                close(fd[0]);close(fd[1]);
                
                struct timeval initialTime, elapsedTime;
                gettimeofday(&initialTime, NULL);
                wait(NULL);
                gettimeofday(&elapsedTime, NULL);
                elapsedTime.tv_sec = elapsedTime.tv_sec - initialTime.tv_sec;
                elapsedTime.tv_usec = elapsedTime.tv_usec - initialTime.tv_usec ;
                
                if(elapsedTime.tv_usec < 0){
                    elapsedTime.tv_usec = elapsedTime.tv_usec + 100000;
                    elapsedTime.tv_sec--;
                }
                
                printf("Comm %d: %s\nElapsed time: %04d.%06d\n",lineNumber, com[0], elapsedTime.tv_sec, elapsedTime.tv_usec); // Va por pipe a padre
                fprintf(stderr, "Comm %d: %s\nElapsed time: %04d.%06d\n",lineNumber, com[0], elapsedTime.tv_sec, elapsedTime.tv_usec); // Va por pipe a padre
                exit(0);
            }
        } else {    // Father
            if (argv[3][0] == 's') { // If they indicated "s" as the last parameter, we have to wait for the current program until we execute another command
                printf("Waiting...\n");
                wait(NULL);
            } // Else, continue launching new ones
        }  
    }
    
    close(fd[1]); // Close the pipe's output 
    
    int readBytes;
    
    while((readBytes = read(fd[0], buf, sizeof(buf))) > 0) { // Writes everything that comes from the pipe into the logFile
        write(fdLog, buf, readBytes);
    }
    
    close(fd[0]); // Close the pipe's output
    
    // once everything is fixed, handle waiting for children here in case it was a parallel run

    exit(0);
}
26.01.2021

  • Ничего себе, это неясное и неопределенное поведение! Отвечает ли это на ваш вопрос? Указатель файла C изменяется после fork и (неудачного) выполнения По сути, вам нужно fflush(stdin) перед вызовом fork() (для меня вызов ftell(stdin) после gets() также позволяет избежать этой проблемы). 26.01.2021
  • Могу ли я также порекомендовать просмотр кода? У вас есть несколько сомнительных практик выше — отсутствие sys/time.h, игра с файловыми дескрипторами как в виде необработанных дескрипторов, так и в качестве конструкций stdio, например, — которые можно было бы улучшить. 26.01.2021
  • @pilcrow Вау, это было странно... Большое спасибо! На самом деле код довольно уродлив - это для курса по системным вызовам C и UNIX для начинающих, где они позволяют нам использовать только очень специфический набор библиотечных функций для смешивания с другими системными вызовами (как в случае gets()), а нечетные библиотечная функция здесь и там, такая как gettimeofday, предоставляется вместе с необходимым заголовком как часть проблемы. Итак, это в основном делается в том, что можно назвать слепым режимом, и поэтому я не добавлял отсутствующие заголовки намеренно. 26.01.2021

Новые материалы

Кластеризация: более глубокий взгляд
Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

Как написать эффективное резюме
Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

Частный метод Python: улучшение инкапсуляции и безопасности
Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

Как я автоматизирую тестирование с помощью Jest
Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..