Подтвердить что ты не робот

Можно ли определить порядок выполнения fork()?

Я работаю над упражнением в учебнике "Концепции операционной системы 7-го издания", и я немного смущен тем, как работает fork(). По моему мнению, fork() создает дочерний процесс, который запускается одновременно с его родителем. Но тогда, как мы точно знаем, какой процесс запускается первым? Я имел в виду порядок исполнения.

Проблема
Напишите программу C с помощью системного вызова fork(), который генерирует последовательность Фибоначчи в дочернем процессе. Номер последовательности будет указан в командной строке.

Это мое решение:

#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

void display_fibonacci_sequence( int n ) {
    int i = 0;
    int a = 1;
    int b = 1;
    int value;
    printf( "%d, %d, ", a, b );
    for( ;i < n - 2; ++i ) {
        value = a + b;
        printf( "%d, ", value );
        a = b;
        b = value;
    }
    printf( "\n" );
}

int main( int argc, char** argv ) {
    int n;
    pid_t pid;
    pid = fork();
    if( argc != 2 ) {
        fprintf( stderr, "Invalid arguments" );
        exit( -1 );
    }
    n = atoi( argv[1] );

    if( pid < 0 ) {
        fprintf( stderr, "Fork failed" );
        exit( -1 );
    }
    else if( pid == 0 ) {
        display_fibonacci_sequence( n );
    }
    else { // parent process
        // what do we need to do here? 
    }
}

Честно говоря, я не вижу разницы между использованием fork и не использованием fork. Кроме того, если я хочу, чтобы родительский процесс обрабатывал входные данные от пользователя, и пусть дочерний процесс обрабатывает отображение, как я могу это сделать?

4b9b3361

Ответ 1

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

В вашей конкретной проблеме нет особых причин для использования fork(). Ваш профессор, вероятно, дал вам это просто для тривиального примера.

Если вы хотите, чтобы родительский элемент обрабатывал входные данные, а ребенок вычислялся, все, что вам нужно сделать, - это перевести вызов на fork() ниже точки, в которой вы обрабатываете аргументы командной строки. Используя ту же основную логику, что и выше, родительский вызов display_fibonacci_sequence и родительский просто подождите

Ответ 2

Вы задаете много вопросов, я постараюсь ответить им в удобном порядке.

Первый вопрос

Если честно, я не вижу разницы между использованием вилки, а не используя fork.

Это потому, что пример не очень хороший. В вашем примере родитель ничего не делает, поэтому вилка бесполезна.

Второй

else {
    // what do we need to do here? 
}

Вам нужно wait (2), чтобы ребенок закончил. Убедитесь, что вы внимательно прочитали эту страницу.

Третий

Я хочу, чтобы родительский процесс обрабатывал ввод от пользователя, и пусть дочерний процесс обрабатывает отображение

Прочитайте ввод перед вилкой и "обработайте" дисплей внутри if (pid == 0)

Четвертый

Но тогда, как мы точно знаем, какой процесс запускается первым?

Очень немногие программы должны заниматься этим. Вы не можете знать порядок выполнения, он полностью зависит от среды. TLPI говорит следующее:

После fork() неопределенный процесс - родительский или child-next имеет доступ к CPU. В многопроцессорной системе они могут одновременно получать доступ к ЦП.

Приложения, которые неявно или явно полагаются на конкретный последовательность выполнения для достижения правильных результатов открыта для отказ из-за условий гонки

Тем не менее, операционная система может позволить вам контролировать этот порядок. Например, Linux имеет /proc/sys/kernel/sched_child_runs_first.

Ответ 3

Процесс, выбранный вашим system scheduler, выбран для запуска, в отличие от любого другого приложения, запущенного в вашей операционной системе. Обработанный процесс обрабатывается как любой другой процесс, когда планировщик назначает приоритет или место в очереди или независимо от реализации.

Ответ 4

Но тогда, как мы точно знаем, какой процесс запускается первым? Я имел в виду порядок выполнения.

Нет гарантии, с которой вы побежали первым. fork возвращает 0, если это дочерний элемент и pid дочернего элемента, если он является родительским. Теоретически они могли работать точно в одно и то же время на многопроцессорной системе. Если вы действительно хотели определить, с какого первого запуска вы могли бы иметь общий замок между двумя процессами. Можно сказать, что тот, кто получает блокировку, первым запускал.

С точки зрения того, что делать в вашем заявлении else. Вы хотите дождаться завершения дочернего процесса с помощью wait или waitpid.

Честно говоря, я не вижу разницы между использованием fork и не использованием fork.

Разница заключается в том, что вы создаете дочерний процесс. Другой процесс в системе, выполняющий вычисления. Для этой простой проблемы конечный пользовательский интерфейс тот же. Но fork очень отличается, когда вы пишете такие системы, как серверы, которые должны иметь дело с вещами одновременно.

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

У вас уже есть эта настройка. Родительский процесс просто должен дождаться завершения дочернего процесса. Детский процесс будет printf выводить результаты на терминал. И родительский процесс в настоящее время получает пользовательский ввод из командной строки.

Ответ 5

Пока вы не можете контролировать, какой процесс (родительский или дочерний) получает запланированный сначала после fork (на самом деле в SMP/многоядерном он может быть и!), существует множество способов синхронизации двух процессов, ожидая, пока другой не достигнет определенного момента, прежде чем он выполнит какие-либо нетривиальные операции. Один классический, чрезвычайно переносимый метод:

  • До fork вызовите pipe, чтобы создать канал.
  • Сразу после fork процесс, который хочет подождать, должен закрыть конец записи в трубке и вызвать read на считывающем конце канала.
  • Другой процесс должен немедленно закрыть конец считывания трубы и подождать, чтобы закрыть конец записи, пока он не будет готов к запуску другого процесса. (read затем возвращает 0 в другом процессе)