
Следующая программа на языке C должна проиллюстрировать состояние гонки между дочерними и родительскими процессами:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main()
{
fork();
printf("\n 1234567890 \n");
return 0;
}
Когда мои друзья это делают (наУбунту), они получают ожидаемый результат, который представляет собой перемешанные 1234567890s
Один пример:12312345645678907890
Но когда я пробую ту же программу на своемАрч Линукс, он никогда не выдает такого результата. Всегда один за другим.
1234567890
1234567890
Мне нравится, чтоарч линуксэто как-то избегает состояния гонки, но я хотел бызапрещатьлюбые такие функции и хотел бы получить вывод, как у моего друга.
решение1
Вызов printf
запустит один или несколько write(2)
системных вызовов, и порядок их обработки будет фактическим порядком вывода. Один или несколько, поскольку это зависит от буферизации внутри библиотеки C. При выводе с буферизацией строк (переводе на терминал) вы, скорее всего, получите два write
вызова, один для начальной новой строки, а другой для остальных.
write(1, "\n", 1);
write(1, " 1234567890 \n", 13);
Между вызовами может быть запланирован другой процесс, который сначала выдаст две пустые строки, а затем строки с цифрами, но, учитывая, что обработки не так много, в незагруженной системе это маловероятно.
Обратите внимание: поскольку оба процесса печатают одно и то же, не имеет значения, какой из них будет запущен первым, главное, чтобы один не прерывал другой.
Если вывод направляется в файл или канал, по умолчанию он полностью буферизуется, поэтому вы, скорее всего, получите только один write
вызов (на процесс) и не получите смешанного вывода.
Ваш пример смешанных цифр был бы возможен, если бы цифры выводились по одной, с отдельными системными вызовами. Трудно понять, почему разумная реализация библиотеки сделала бы это при печати статической строки, длина которой известна. При большем количестве записей в цикле смешанный вывод был бы более вероятным:
Что-то вроде этого:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int i;
setbuf(stdout, NULL); /* explicitly unbuffered */
int x = fork();
for (i = 0 ; i < 500 ; i++) {
printf("%d", !!x);
}
if (x) {
wait(NULL);
printf("\n");
}
return 0;
}
Выдает мне вывод, как показано ниже. Большую часть времени, но не всегда. Система решает, как планировать процессы. Непредсказуемость — вот почему мы обычно стараемся избегать состояний гонки.
111111111111111111111111111111111111111111111111111111111111111111111111111
111111111111111111111111111111111111111111111111111111111111111111111111111
111100000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000010000001001100
110000000011001100110011000000010011001100110000000100110011001100000001001
100110011000000010011001100110000000100110011001100000001001100110011000000
...
решение2
Я подозреваю, что fork()
системный вызов задерживает либо родительский, либо дочерний процесс достаточно долго, чтобы позволить другому процессу завершить вызов printf()
и увидеть строку в выводе еще до того, как она дойдет до его собственного printf()
.
Вывод большого количества строк в цикле, вероятно, приведет к смешанному выводу, который вы описываете, если и родительский, и дочерний процессы успеют выполнить циклы одновременно.
«Исправление» этой проблемы, скорее всего, потребует переписывания fork()
системного вызова или компонентов ядра, участвующих в нем.