Во многих программах и man-страницах Linux я видел код с помощью fork()
. Почему нам нужно использовать fork()
и какова его цель?
Какова цель fork()?
Ответ 1
fork()
заключается в том, как вы создаете новые процессы в Unix. Когда вы вызываете fork
, вы создаете копию собственного процесса, у которого есть собственное адресное пространство. Это позволяет нескольким задачам работать независимо друг от друга, как если бы каждый из них обладал полной памятью машины для себя.
Вот несколько примеров использования fork
:
- Ваш shell использует
fork
для запуска программ, которые вы вызываете из командной строки. - Веб-серверы, такие как apache, используют
fork
для создания нескольких серверных процессов, каждый из которых обрабатывает запросы в своем собственном адресном пространстве. Если кто-то умирает или теряет память, другие не подвержены влиянию, поэтому он функционирует как механизм отказоустойчивости. - Google Chrome использует
fork
для обработки каждой страницы в отдельном процессе. Это предотвратит доступ клиентского кода на одной странице к вашему браузеру. -
fork
используется для создания процессов в некоторых параллельных программах (например, написанных с использованием MPI). Обратите внимание, что это отличается от использования потоков, которые не имеют собственного адресного пространства и существуют в процессе. - Языки сценариев используют
fork
косвенно для запуска дочерних процессов. Например, каждый раз, когда вы используете командуsubprocess.Popen
в Python, выfork
выполняете дочерний процесс и читаете его вывод. Это позволяет программам работать вместе.
Типичное использование fork
в оболочке может выглядеть примерно так:
int child_process_id = fork();
if (child_process_id) {
// Fork returns a valid pid in the parent process. Parent executes this.
// wait for the child process to complete
waitpid(child_process_id, ...); // omitted extra args for brevity
// child process finished!
} else {
// Fork returns 0 in the child process. Child executes this.
// new argv array for the child process
const char *argv[] = {"arg1", "arg2", "arg3", NULL};
// now start executing some other program
exec("/path/to/a/program", argv);
}
Оболочка запускает дочерний процесс с помощью exec
и ждет его завершения, а затем продолжает свое собственное выполнение. Обратите внимание, что вам не нужно использовать fork таким образом. Вы всегда можете создавать множество дочерних процессов, как может выполнять параллельная программа, и каждый из них может запускать программу одновременно. В принципе, в любое время, когда вы создаете новые процессы в системе Unix, вы используете fork()
. Для эквивалента Windows взгляните на CreateProcess
.
Если вы хотите больше примеров и более подробное объяснение, Wikipedia имеет приличное резюме. И здесь приведены некоторые слайды о том, как процессы, потоки и concurrency работают в современных операционных системах.
Ответ 2
fork() - это то, как Unix создает новые процессы. В тот момент, когда вы вызвали fork(), ваш процесс клонируется, и два разных процесса продолжают выполнение оттуда. Один из них, дочерний, будет иметь fork(), возвращающий 0. Другой, родительский, будет иметь fork(), возвращающий PID (идентификатор процесса) дочернего элемента.
Например, если вы введете следующее в оболочке, программа оболочки вызовет fork(), а затем выполнит команду, которую вы передали (telnetd, в данном случае), в дочернем процессоре, в то время как родительский файл также отобразит приглашение снова. как сообщение, указывающее PID фонового процесса.
$ telnetd &
Что касается причины, вы создаете новые процессы, то, как ваша операционная система может делать много вещей одновременно. Вот почему вы можете запустить программу и, пока она работает, переключиться в другое окно и сделать что-то еще.
Ответ 3
fork() используется для создания дочернего процесса. Когда вызывается функция fork(), новый процесс будет порожден, а вызов функции fork() вернет другое значение для дочернего элемента и родителя.
Если возвращаемое значение равно 0, вы знаете, что вы являетесь дочерним процессом, и если возвращаемое значение является числом (которое является идентификатором дочернего процесса), вы знаете, что вы являетесь родителем. (и если это отрицательное число, вилка была неудачной и не был создан дочерний процесс)
Ответ 4
fork() создаст новый дочерний процесс, идентичный родительскому. Таким образом, все, что вы запускаете в коде после этого, будет выполняться обоими процессами - очень полезно, если у вас есть, например, сервер, и вы хотите обрабатывать несколько запросов.
Ответ 5
fork() в основном используется для создания дочернего процесса для процесса, в котором вы вызываете эту функцию. Всякий раз, когда вы вызываете fork(), он возвращает ноль для дочернего id.
pid=fork()
if pid==0
//this is the child process
else if pid!=0
//this is the parent process
этим вы можете предоставить различные действия для родителя и дочернего элемента и использовать функцию многопоточности.
Ответ 6
Вероятно, вам не нужно использовать fork в повседневном программировании, если вы пишете приложения.
Даже если вы хотите, чтобы ваша программа запускала другую программу для выполнения какой-либо задачи, есть и другие более простые интерфейсы, которые используют fork за кулисами, такие как "система" в C и perl.
Например, если вы хотите, чтобы ваше приложение запускало другую программу, например, bc, чтобы выполнить некоторые вычисления, вы можете использовать "систему" для ее запуска. Система создает "вилку" для создания нового процесса, а затем "exec", чтобы превратить этот процесс в bc. Когда bc завершается, система возвращает управление вашей программе.
Вы также можете запускать другие программы асинхронно, но я не помню, как.
Если вы пишете серверы, оболочки, вирусы или операционные системы, вы, скорее всего, захотите использовать fork.
Ответ 7
Форк создает новые процессы. Без fork у вас будет система unix, которая может запускать только init.
Ответ 8
Системный вызов fork() используется для создания процессов. Он не принимает аргументов и возвращает идентификатор процесса. Цель fork() - создать новый процесс, который станет дочерним процессом вызывающего. После создания нового дочернего процесса оба процесса будут выполнять следующую команду после системного вызова fork(). Поэтому мы должны отличить родителя от ребенка. Это можно сделать, проверив возвращаемое значение fork():
Если fork() возвращает отрицательное значение, создание дочернего процесса не увенчалось успехом. fork() возвращает ноль во вновь созданный дочерний процесс. fork() возвращает положительное значение, идентификатор процесса дочернего процесса, родительскому. Идентификатор возвращаемого процесса имеет тип pid_t, определенный в sys/types.h. Обычно идентификатор процесса является целым числом. Кроме того, процесс может использовать функцию getpid() для получения идентификатора процесса, назначенного этому процессу. Поэтому после системного вызова fork() простой тест может определить, какой процесс является дочерним. Обратите внимание, что Unix сделает точную копию родительского адресного пространства и передаст его дочернему. Поэтому родительский и дочерний процессы имеют отдельные адресные пространства.
Давайте рассмотрим это с примером, чтобы сделать вышеизложенные моменты ясными. В этом примере не различаются родительские и дочерние процессы.
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#define MAX_COUNT 200
#define BUF_SIZE 100
void main(void)
{
pid_t pid;
int i;
char buf[BUF_SIZE];
fork();
pid = getpid();
for (i = 1; i <= MAX_COUNT; i++) {
sprintf(buf, "This line is from pid %d, value = %d\n", pid, i);
write(1, buf, strlen(buf));
}
}
Предположим, что вышеуказанная программа выполняет до точки вызова fork().
Если вызов fork() выполняется успешно, Unix сделает две идентичные копии адресных пространств, одну для родителя, а другую для дочернего. Оба процесса начнут выполнение в следующем выражении, следующем за вызовом fork(). В этом случае оба процесса начнут выполнение при присваивании
pid = .....;
Оба процесса начинают свое выполнение сразу после системного вызова fork(). Поскольку оба процесса имеют одинаковые, но отдельные адресные пространства, эти переменные, инициализированные до вызова fork(), имеют одинаковые значения в обоих адресных пространствах. Поскольку каждый процесс имеет собственное адресное пространство, любые модификации не зависят от других. Другими словами, если родительский параметр изменяет значение своей переменной, модификация влияет только на переменную в адресном пространстве родительского процесса. Другие адресные пространства, созданные вызовами fork(), не будут затронуты, даже если они имеют идентичные имена переменных.
В чем причина использования write, а не printf? Это потому, что printf() "буферизирован", то есть printf() будет группировать вывод процесса вместе. Буферизируя вывод для родительского процесса, ребенок также может использовать printf для печати некоторой информации, которая также будет буферизована. В результате, поскольку вывод не будет сразу отправляться на экран, вы можете не получить правильный порядок ожидаемого результата. Хуже того, выход из двух процессов может быть смешано странным образом. Чтобы преодолеть эту проблему, вы можете использовать "небуферизованную" запись.
Если вы запустите эту программу, вы можете увидеть на экране следующее:
................
This line is from pid 3456, value 13
This line is from pid 3456, value 14
................
This line is from pid 3456, value 20
This line is from pid 4617, value 100
This line is from pid 4617, value 101
................
This line is from pid 3456, value 21
This line is from pid 3456, value 22
................
Идентификатор процесса 3456 может быть назначенным родительскому или дочернему. В связи с тем, что эти процессы запускаются одновременно, их выходные линии смешиваются довольно непредсказуемым образом. Более того, порядок этих строк определяется планировщиком ЦП. Следовательно, если вы снова запустите эту программу, вы можете получить совершенно другой результат.
Ответ 9
Многопроцессорность является центральной для вычислений. Например, ваш IE или Firefox могут создать процесс загрузки файла для вас, пока вы все еще просматриваете Интернет. Или, пока вы печатаете документ в текстовом редакторе, вы все равно можете посмотреть на разные страницы и по-прежнему редактировать с ним.
Ответ 10
Fork() используется для создания новых процессов по мере написания каждого тела.
Вот мой код, который создает процессы в виде двоичного дерева....... Он попросит сканировать количество уровней, до которых вы хотите создавать процессы в двоичном дереве
#include<unistd.h>
#include<fcntl.h>
#include<stdlib.h>
int main()
{
int t1,t2,p,i,n,ab;
p=getpid();
printf("enter the number of levels\n");fflush(stdout);
scanf("%d",&n);
printf("root %d\n",p);fflush(stdout);
for(i=1;i<n;i++)
{
t1=fork();
if(t1!=0)
t2=fork();
if(t1!=0 && t2!=0)
break;
printf("child pid %d parent pid %d\n",getpid(),getppid());fflush(stdout);
}
waitpid(t1,&ab,0);
waitpid(t2,&ab,0);
return 0;
}
ВЫХОД
enter the number of levels
3
root 20665
child pid 20670 parent pid 20665
child pid 20669 parent pid 20665
child pid 20672 parent pid 20670
child pid 20671 parent pid 20670
child pid 20674 parent pid 20669
child pid 20673 parent pid 20669
Ответ 11
fork()
используется для создания дочернего процесса. Обычно он используется в подобных ситуациях, таких как потоки, но есть различия. В отличие от потоков, fork()
создает целые отдельные процессы, а это значит, что ребенок и родитель, когда они являются прямыми копиями друг друга в точке, в которой вызывается fork()
, они полностью разделены, и не могут получить доступ к другому пространству памяти ( не обращаясь к обычным проблемам, вы переходите к другой памяти программы).
fork()
по-прежнему используется некоторыми серверными приложениями, в основном теми, которые выполняются как root на машине * NIX, которая удаляет разрешения перед обработкой запросов пользователей. Есть еще кое-что еще, но в основном люди перешли на многопоточность.
Ответ 12
Сначала нужно понять, что такое системный вызов fork(). Позвольте мне объяснить
-
системный вызов fork() создает точный дубликат родительского процесса, он создает дубликат родительского стека, кучи, инициализированных данных, неинициализированных данных и передает код в режиме только для чтения с родительским процессом.
-
Системный вызов Fork копирует память на основе копирования на запись, означает, что ребенок делает на странице виртуальной памяти, когда есть необходимость копирования.
Теперь Назначение fork():
- Fork() может использоваться в том месте, где есть разделение работы, так как сервер должен обрабатывать несколько клиентов, поэтому родитель должен принимать соединение на регулярной основе, поэтому сервер делает fork для каждого клиента для выполнения чтения-записи.
Ответ 13
Системный вызов Fork() используется для создания дочернего процесса. Это точная копия родительского процесса. Fork копирует секцию стека, секцию кучи, секцию данных, переменную окружения, аргументы командной строки из parent.
Ответ 14
Функция fork() используется для создания нового процесса путем дублирования существующего процесса, из которого он вызывается. Существующий процесс, из которого вызывается эта функция, становится родительским процессом, а вновь созданный процесс становится дочерним процессом. Как уже говорилось, дочерний объект является дубликатом родительского экземпляра, но есть некоторые исключения из него.
-
У дочернего элемента есть уникальный PID, как и у любого другого процесса, работающего в операционной системе.
-
У дочернего элемента есть идентификатор родительского процесса, который совпадает с идентификатором PID
процесс, который создал его. -
Счетчики использования ресурсов и времени ЦП сбрасываются на ноль в дочернем процессе.
-
Набор ожидающих сигналов у ребенка пуст.
-
Ребенок не наследует таймеры от своего родителя
Пример :
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
#include <sys/wait.h>
#include <stdlib.h>
int var_glb; /* A global variable*/
int main(void)
{
pid_t childPID;
int var_lcl = 0;
childPID = fork();
if(childPID >= 0) // fork was successful
{
if(childPID == 0) // child process
{
var_lcl++;
var_glb++;
printf("\n Child Process :: var_lcl = [%d], var_glb[%d]\n", var_lcl, var_glb);
}
else //Parent process
{
var_lcl = 10;
var_glb = 20;
printf("\n Parent process :: var_lcl = [%d], var_glb[%d]\n", var_lcl, var_glb);
}
}
else // fork failed
{
printf("\n Fork failed, quitting!!!!!!\n");
return 1;
}
return 0;
}
Теперь, когда приведенный выше код скомпилирован и запущен:
$ ./fork
Parent process :: var_lcl = [10], var_glb[20]
Child Process :: var_lcl = [1], var_glb[1]
Ответ 15
Обоснование использования fork() вместо наличия функции exec() для запуска нового процесса объясняется в ответе на аналогичный вопрос об обмене стеками Unix.
По сути, поскольку fork копирует текущий процесс, все возможные варианты процесса устанавливаются по умолчанию, поэтому у программиста их нет.
В операционной системе Windows, напротив, программистам приходится использовать функцию CreateProcess, которая НАМНОГО сложнее и требует заполнения разнообразной структуры для определения параметров нового процесса.
Подводя итог, можно сказать, что причиной разветвления (а не исполнения) является простота создания новых процессов.