Здесь участвуют четыре процесса, назовем их a
, b
, c
и d
(d
является родителем b
и c
, а b
- родительский элемент a
):
shell
`-d
+-b
| `-a
`-c
d
- это родительский процесс, который выполняет первый вызов fork(2)
(процесс создания b
). Поскольку он является родителем, он будет go в оператор else
этого первого if
и снова будет fork(2)
(создание процесса c
), а затем печатает в конце строку D0
(оба символа) записываются всегда вместе в одном вызове write(2)
, поскольку printf(3)
буферизует данные, см. ниже причину) - первый ответвление
d
производит процесс b
, который после выполнения второго fork(2)
(процесс получения a
) ожидает его завершения sh, а затем печатает B0
. - второй
fork()
из b
(ну, b
создается после первый разветвляющийся список и выполняет только один разветвление (но это второй разветвленный список), создает процесс a
, который печатает A0
, а затем завершается, что делает процесс b
способным продолжаться после wait(2)
вызовите и напечатайте B0
, это заставит порядок всегда быть A0
до B0
. - третий
fork()
производит процесс c
, задачей которого является печать C
и exit(3)
. (из-за этого C0
не выводится, а просто C
) - , поскольку
d
не ждет ни одного из двух его дочерних элементов, как только напечатано D0
, оно exit(3)
s , заставляя оболочку выводить подсказку ~$
и ждать новой команды. Это вынуждает порядок D0
перед приглашением оболочки. - *
^C
- это Control- C, который вы нажали, думая, что программа все еще работает, он заставляет оболочку выдавать второе приглашение в новой строке.
Поскольку единственными процессами, которые ожидают своих потомков, являются оболочка (заставляющая выводить запрос после D0
) и процесс b
, ожидающий процесс a
(заставляя всегда A0
печататься до B0
) разрешается любая другая последовательность, в зависимости от того, как система планирует процессы. и это включает в себя подсказку. Подумайте, что сообщения всегда печатаются в конце выполнения всех задействованных процессов.
Возможные заказы - это перестановки A0
, B0
, D0
, C
и приглашение оболочки, но из этого числа половина имеет порядок A0
и B0
, а из этой половины половина имеет порядок оболочки и D0
обменены ... так что число возможности должны быть 5!/4 == 30
возможностей. Посмотри, сможешь ли ты получить их все !!! :)
объяснение D0A0~$ B0C
Возможное планирование, которое приводит к выводу выше:
- оболочка запускает процесс
d
и ожидает до фини sh. - процесс
d
дважды разветвляется, создавая процессы b
и c
. Печатает D0
и завершает работу. - process
b
разветвляется, создает процесс a
и ожидает от a
до fini sh. - process
a
печатает A0
и выходит. - оболочка пробуждается от
wait()
и печатает приглашение ~$
. - process
b
пробуждается от wait()
для a
и печатает B0
. - process
c
печатает C
и завершается.
printf()
буферизация.
в режиме терминала (когда вывод на терминал) stdout
буферизация в режиме линии. Если у вас есть, скажем, 512-байтовый буфер, он начинает заполнять его до тех пор, пока не увидит символ \n
, или пока буфер не заполнится полностью, тогда он сбрасывает все содержимое буфера в стандартный вывод одним вызовом write(2)
. Это делает последовательность:
printf("D");
...
printf("0");
заставляет оба символа накапливаться в буфере и фактически выводиться вместе в конце процесса, когда exit(3)
вызывает подпрограмму, с которой пакет stdio
устанавливается atexit(3)
вызов.
Возможные выходы:
все возможные комбинации показаны ниже:
D0A0B0C~$
, D0A0B0~$ C
, D0A0CB0~$
, D0A0C~$ B0
, D0A0~$ CB0
, D0A0~$ B0C
, D0CA0B0~$
, D0CA0~$ B0
, D0C~$ A0B0
, D0~$ CA0B0
, D0~$ A0CB0
, D0~$ A0B0C
, A0D0B0C~$
, A0D0B0~$ C
, A0D0CB0~$
, A0D0C~$ B0
, A0D0~$ CB0
, A0D0~$ B0C
, A0B0D0C~$
, A0B0D0~$ C
, A0B0CD0~$
, A0CB0D0~$
, A0CD0B0~$
, A0CD0~$ B0
, CA0B0D0~$
, CA0D0B0~$
, CA0D0~$ B0
, CD0A0B0~$
, CD0A0~$ B0
, CD0~$ A0B0
.