#c #process #fork
#c #процесс #вилка
Вопрос:
Учитывая следующий код, я должен проверить его поведение, то есть, сколько процессов производится.
#include lt;unistd.hgt; #include lt;string.hgt; #include lt;stdlib.hgt; int main(int argc, char*argv[]) { int pid, i , j; pid_t t; for (i = 0; i lt; 2; i ) { pid = getpid(); for (j = 0 ; j lt; i 2; j ){ t = fork(); if (t != 0) { t = fork(); break; } } if (pid != getpid()) break; } printf("I am the process %d and my father is the process %dn", getpid(), getppid()); while(wait(NULL)gt;0) {} return 0; }
После компиляции и выполнения результат выглядит следующим образом:
I am the process 3749 and my father is the process 2254 I am the process 3750 and my father is the process 3749 I am the process 3751 and my father is the process 3749 I am the process 3752 and my father is the process 3749 I am the process 3754 and my father is the process 3749 I am the process 3753 and my father is the process 3750 I am the process 3755 and my father is the process 3750 I am the process 3756 and my father is the process 3752 I am the process 3757 and my father is the process 3752 I am the process 3758 and my father is the process 3756 I am the process 3759 and my father is the process 3756
Это означает, что соответствующий древовидный процесс выглядит следующим образом:
2254 | -- 3749 ---- / | 3750 3751 3752 3754 / / 3753 3755 3756 3757 / 3758 3759
Всего производится 11 процессов, если считать начальный процесс.
Я ожидал, что будет сгенерировано гораздо больше процессов, чем просто 11; в любом случае, я могу понять возникновение всех процессов, кроме самых отдаленных листьев (3758 и 3759).
В программе есть два цикла for, один из которых вложен в другой. Поскольку два оператора break находятся внутри каждого цикла, я предполагаю, что цикл или два должны завершиться в какой-то момент до их естественного конца.
Может ли кто-нибудь подтвердить поведение этого кода?
Заранее спасибо
Комментарии:
1. Я вижу 12 процессов в вашей превосходной древовидной диаграмме процессов. Вы имели в виду 12 вместо 11?
2. Да @Эдвин Бак, я имел в виду 12 процессов, если считать отца начального процесса. Спасибо
Ответ №1:
Эти виды программ больше иллюстрируют, что вы понимаете, как работает разветвление, я надеюсь, что вы никогда не напишете разветвленную программу в нескольких вложенных циклах с перерывами.
Они отслеживают это, имитируя машину на бумаге, но каждый раз, когда вы имитируете вилку, вы копируете симуляцию, устанавливая код возврата fork()
для одной копии равным нулю, а код возврата другой копии-pid другого процесса.
Позвольте мне переформатировать вашу программу, чтобы сделать ее проще
int pid; int i; int j; pid_t t;
будем придерживаться наших «текущих ценностей» для процесса. Мы также укажем местоположение указателя инструкции с помощью «IP ==gt;»
int pid; int i; int j; pid_t t; IP ===gt; for (i = 0; i lt; 2; i ) { pid = getpid(); for (j = 0 ; j lt; i 2; j ){ t = fork(); if (t != 0) { t = fork(); break; } } if (pid != getpid()) break; }
Следующий шаг (для PID 1)
=== PROCESS 1 ==== int pid; int i; (set to 0) int j; pid_t t; for (i = 0; i lt; 2; i ) { IP ===gt; pid = getpid(); for (j = 0 ; j lt; i 2; j ){ t = fork(); if (t != 0) { t = fork(); break; } } if (pid != getpid()) break; }
next step (for PID 1)
=== PROCESS 1 ==== int pid (set to 1 // fake pid, but easy to count) int i; (set to 0) int j; pid_t t; for (i = 0; i lt; 2; i ) { pid = getpid(); IP ===gt; for (j = 0 ; j lt; i 2; j ){ t = fork(); if (t != 0) { t = fork(); break; } } if (pid != getpid()) break; }
next step (for process 1)
=== PROCESS 1 ==== int pid (set to 1) int i; (set to 0) int j; (set to 0) pid_t t; for (i = 0; i lt; 2; i ) { pid = getpid(); for (j = 0 ; j lt; i 2; j ){ IP ===gt; t = fork(); if (t != 0) { t = fork(); break; } } if (pid != getpid()) break; }
next step (for PID 1)
=== PROCESS 1 ==== int pid (set to 1) int i; (set to 0) int j; (set to 0) pid_t t; (set to 2, parent process) for (i = 0; i lt; 2; i ) { pid = getpid(); for (j = 0 ; j lt; i 2; j ){ t = fork(); IP ===gt; if (t != 0) { t = fork(); break; } } if (pid != getpid()) break; } === PROCESS 2 === int pid (set to 1) int i; (set to 0) int j; (set to 0) pid_t t; (set to 0, child process) for (i = 0; i lt; 2; i ) { pid = getpid(); for (j = 0 ; j lt; i 2; j ){ t = fork(); IP ===gt; if (t != 0) { t = fork(); break; } } if (pid != getpid()) break; }
next step (for PROCESS 1)
=== PROCESS 1 ==== int pid (set to 1) int i; (set to 0) int j; (set to 0) pid_t t; (set to 2, parent process) for (i = 0; i lt; 2; i ) { pid = getpid(); for (j = 0 ; j lt; i 2; j ){ t = fork(); if (t != 0) { // t == 2 in this process, so enter the block IP ===gt; t = fork(); break; } } if (pid != getpid()) break; } === PROCESS 2 === int pid (set to 1) int i; (set to 0) int j; (set to 0) pid_t t; (set to 0, child process) for (i = 0; i lt; 2; i ) { pid = getpid(); for (j = 0 ; j lt; i 2; j ){ t = fork(); IP ===gt; if (t != 0) { t = fork(); break; } } if (pid != getpid()) break; }
next step (for PID 1)
=== PROCESS 1 ==== int pid (set to 1) int i; (set to 0) int j; (set to 0) pid_t t; (set to 2, parent process) for (i = 0; i lt; 2; i ) { pid = getpid(); for (j = 0 ; j lt; i 2; j ){ t = fork(); IP ===gt; if (t != 0) { t = fork(); break; } } if (pid != getpid()) break; } === PROCESS 2 === int pid (set to 1) int i; (set to 0) int j; (set to 0) pid_t t; (set to 0, child process) for (i = 0; i lt; 2; i ) { pid = getpid(); for (j = 0 ; j lt; i 2; j ){ t = fork(); IP ===gt; if (t != 0) { t = fork(); break; } } if (pid != getpid()) break; }
next step (for PROCESS 1)
=== PROCESS 1 ==== int pid (set to 1) int i; (set to 0) int j; (set to 0) pid_t t; (set to 2, parent process) for (i = 0; i lt; 2; i ) { pid = getpid(); for (j = 0 ; j lt; i 2; j ){ t = fork(); if (t != 0) { IP ===gt; t = fork(); break; } } if (pid != getpid()) break; } === PROCESS 2 === int pid (set to 1) int i; (set to 0) int j; (set to 0) pid_t t; (set to 0, child process) for (i = 0; i lt; 2; i ) { pid = getpid(); for (j = 0 ; j lt; i 2; j ){ t = fork(); IP ===gt; if (t != 0) { t = fork(); break; } } if (pid != getpid()) break; }
следующий шаг (для ПРОЦЕССА 1)
=== PROCESS 1 ==== int pid (set to 1) int i; (set to 0) int j; (set to 0) pid_t t; (set to 3, parent process) for (i = 0; i lt; 2; i ) { pid = getpid(); for (j = 0 ; j lt; i 2; j ){ t = fork(); if (t != 0) { t = fork(); IP ===gt; break; } } if (pid != getpid()) break; } === PROCESS 2 === int pid (set to 1) int i; (set to 0) int j; (set to 0) pid_t t; (set to 0, child process) for (i = 0; i lt; 2; i ) { pid = getpid(); for (j = 0 ; j lt; i 2; j ){ t = fork(); IP ===gt; if (t != 0) { t = fork(); break; } } if (pid != getpid()) break; } === PROCESS 3 ==== int pid (set to 1) int i; (set to 0) int j; (set to 0) pid_t t; (set to 0, child of PID 1) for (i = 0; i lt; 2; i ) { pid = getpid(); for (j = 0 ; j lt; i 2; j ){ t = fork(); if (t != 0) { t = fork(); IP ===gt; break; } } if (pid != getpid()) break; }
следующий шаг (для ПРОЦЕССА 1)
=== PROCESS 1 ==== int pid (set to 1) int i; (set to 0) int j; (set to 0) pid_t t; (set to 3, parent process) for (i = 0; i lt; 2; i ) { pid = getpid(); for (j = 0 ; j lt; i 2; j ){ t = fork(); if (t != 0) { t = fork(); break; } } IP ===gt; if (pid != getpid()) break; } === PROCESS 2 === int pid (set to 1) int i; (set to 0) int j; (set to 0) pid_t t; (set to 0, child process) for (i = 0; i lt; 2; i ) { pid = getpid(); for (j = 0 ; j lt; i 2; j ){ t = fork(); IP ===gt; if (t != 0) { t = fork(); break; } } if (pid != getpid()) break; } === PROCESS 3 ==== int pid (set to 1) int i; (set to 0) int j; (set to 0) pid_t t; (set to 0, child of PID 1) for (i = 0; i lt; 2; i ) { pid = getpid(); for (j = 0 ; j lt; i 2; j ){ t = fork(); if (t != 0) { t = fork(); IP ===gt; break; } } if (pid != getpid()) break; }
следующий шаг (для ПРОЦЕССА 1)
=== PROCESS 1 ==== int pid (set to 1) int pid = 1; int i; (set to 0) int j; (set to 1, due to j ) pid_t t; (set to 3, parent process) for (i = 0; i lt; 2; i ) { pid = getpid(); for (j = 0 ; j lt; i 2; j ){ t = fork(); if (t != 0) { // t == 2 in this process, so enter the block t = fork(); break; } } if (pid != getpid()) // pid == 1, getpid == 1. so skip break break; IP ===gt; } === PROCESS 2 === int i; (set to 0) int j; (set to 0) pid_t t; (set to 0, child process) for (i = 0; i lt; 2; i ) { pid = getpid(); for (j = 0 ; j lt; i 2; j ){ t = fork(); IP ===gt; if (t != 0) { t = fork(); break; } } if (pid != getpid()) break; } === PROCESS 3 ==== int i; (set to 0) int j; (set to 0) pid_t t; (set to 0, child of PID 1) for (i = 0; i lt; 2; i ) { pid = getpid(); for (j = 0 ; j lt; i 2; j ){ t = fork(); if (t != 0) { // t == 2 in this process, so enter the block t = fork(); IP ===gt; break; } } if (pid != getpid()) break; }
next step (for PROCESS 1)
=== PROCESS 1 ==== int pid (set to 1) int pid = 1; int i; (set to 1) int j; (set to 1, due to j ) pid_t t; (set to 3, parent process) for (i = 0; i lt; 2; IP ===gt; i ) { pid = getpid(); for (j = 0 ; j lt; i 2; j ){ t = fork(); if (t != 0) { // t == 2 in this process, so enter the block t = fork(); break; } } if (pid != getpid()) // pid == 1, getpid == 1. so skip break break; } === PROCESS 2 === int i; (set to 0) int j; (set to 0) pid_t t; (set to 0, child process) for (i = 0; i lt; 2; i ) { pid = getpid(); for (j = 0 ; j lt; i 2; j ){ t = fork(); IP ===gt; if (t != 0) { t = fork(); break; } } if (pid != getpid()) break; } === PROCESS 3 ==== int i; (set to 0) int j; (set to 0) pid_t t; (set to 0, child of PID 1) for (i = 0; i lt; 2; i ) { pid = getpid(); for (j = 0 ; j lt; i 2; j ){ t = fork(); if (t != 0) { // t == 2 in this process, so enter the block t = fork(); IP ===gt; break; } } if (pid != getpid()) break; }
next step (for PROCESS 1)
=== PROCESS 1 ==== int pid (set to 1) int pid = 1; int i; (set to 1) int j; (set to 1, due to j ) pid_t t; (set to 3, parent process) for (i = 0; IP ===gt; i lt; 2; i ) { // 1 is less than 2 so enter loop block pid = getpid(); for (j = 0 ; j lt; i 2; j ){ t = fork(); if (t != 0) { // t == 2 in this process, so enter the block t = fork(); break; } } if (pid != getpid()) // pid == 1, getpid == 1. so skip break break; } === PROCESS 2 === int i; (set to 0) int j; (set to 0) pid_t t; (set to 0, child process) for (i = 0; i lt; 2; i ) { pid = getpid(); for (j = 0 ; j lt; i 2; j ){ t = fork(); IP ===gt; if (t != 0) { t = fork(); break; } } if (pid != getpid()) break; } === PROCESS 3 ==== int i; (set to 0) int j; (set to 0) pid_t t; (set to 0, child of PID 1) for (i = 0; i lt; 2; i ) { pid = getpid(); for (j = 0 ; j lt; i 2; j ){ t = fork(); if (t != 0) { // t == 2 in this process, so enter the block t = fork(); IP ===gt; break; } } if (pid != getpid()) break; }
next step (for PROCESS 1)
=== PROCESS 1 ==== int pid (set to 1) int pid = 1; int i; (set to 1) int j; (set to 1) pid_t t; (set to 3, parent process) for (i = 0; i lt; 2; i ) { IP ===gt; pid = getpid(); for (j = 0 ; j lt; i 2; j ){ t = fork(); if (t != 0) { // t == 2 in this process, so enter the block t = fork(); break; } } if (pid != getpid()) // pid == 1, getpid == 1. so skip break break; } === PROCESS 2 === int i; (set to 0) int j; (set to 0) pid_t t; (set to 0, child process) for (i = 0; i lt; 2; i ) { pid = getpid(); for (j = 0 ; j lt; i 2; j ){ t = fork(); IP ===gt; if (t != 0) { t = fork(); break; } } if (pid != getpid()) break; } === PROCESS 3 ==== int i; (set to 0) int j; (set to 0) pid_t t; (set to 0, child of PID 1) for (i = 0; i lt; 2; i ) { pid = getpid(); for (j = 0 ; j lt; i 2; j ){ t = fork(); if (t != 0) { // t == 2 in this process, so enter the block t = fork(); IP ===gt; break; } } if (pid != getpid()) break; }
next step (for PROCESS 1)
=== PROCESS 1 ==== int pid (set to 1, again) int pid = 1; int i; (set to 1) int j; (set to 1) pid_t t; (set to 3) for (i = 0; i lt; 2; i ) { pid = getpid(); IP ===gt; for (j = 0 ; j lt; i 2; j ){ t = fork(); if (t != 0) { // t == 2 in this process, so enter the block t = fork(); break; } } if (pid != getpid()) // pid == 1, getpid == 1. so skip break break; } === PROCESS 2 === int i; (set to 0) int j; (set to 0) pid_t t; (set to 0, child process) for (i = 0; i lt; 2; i ) { pid = getpid(); for (j = 0 ; j lt; i 2; j ){ t = fork(); IP ===gt; if (t != 0) { t = fork(); break; } } if (pid != getpid()) break; } === PROCESS 3 ==== int i; (set to 0) int j; (set to 0) pid_t t; (set to 0, child of PID 1) for (i = 0; i lt; 2; i ) { pid = getpid(); for (j = 0 ; j lt; i 2; j ){ t = fork(); if (t != 0) { // t == 2 in this process, so enter the block t = fork(); IP ===gt; break; } } if (pid != getpid()) break; }
next step (for PROCESS 1)
=== PROCESS 1 ==== int pid (set to 1) int pid = 1; int i; (set to 1) int j; (set to 0) pid_t t; (set to 3) for (i = 0; i lt; 2; i ) { pid = getpid(); for (j = 0 ; j lt; i 2; j ){ IP ===gt; t = fork(); if (t != 0) { // t == 2 in this process, so enter the block t = fork(); break; } } if (pid != getpid()) // pid == 1, getpid == 1. so skip break break; } === PROCESS 2 === int i; (set to 0) int j; (set to 0) pid_t t; (set to 0, child process) for (i = 0; i lt; 2; i ) { pid = getpid(); for (j = 0 ; j lt; i 2; j ){ t = fork(); IP ===gt; if (t != 0) { t = fork(); break; } } if (pid != getpid()) break; } === PROCESS 3 ==== int i; (set to 0) int j; (set to 0) pid_t t; (set to 0, child of PID 1) for (i = 0; i lt; 2; i ) { pid = getpid(); for (j = 0 ; j lt; i 2; j ){ t = fork(); if (t != 0) { // t == 2 in this process, so enter the block t = fork(); IP ===gt; break; } } if (pid != getpid()) break; }
И так далее. Как только вы выводите процесс 1 из его циклов (и ждете завершения его дочерних элементов), вы выполняете ту же процедуру для процесса 2, пока он не выйдет из своих циклов и не дождется завершения своих дочерних элементов.
Смысл этого упражнения в том, можете ли вы смоделировать какой-то запутанный код процесса. Понятно, что, поскольку процесс верхнего уровня будет повторяться дважды, и он никогда не выйдет из цикла, под ним вы получите 4 процесса. По мере того как вы будете практиковаться в прохождении других процессов, вы начнете видеть шаблоны, которые они реализуют.
Лично для меня такого рода проблема-это большая работа, а не та работа, которая делает вас лучшим программистом; но она продемонстрирует, если вы поймете, что программа разветвляется с ее текущим состоянием (обратите внимание, что pid неверен у многих детей, потому что он не был обновлен после развилки), и если вы полностью это понимаете, вы можете смоделировать программу до ее окончательного завершения.
Конечно, мы используем компьютеры для автоматизации скучных вещей (как это), поэтому вы можете придумать новую строку ведения журнала, подобную этой:
void printState(int me, int pid, int i, int j, int t) { printf("PID %d: pid = %d, i = %d, j = %d, t = %dn"); }
а потом беги
printState(getpid(), pid, i, j, t); for (i = 0; i lt; 2; i ) { printState(getpid(), pid, i, j, t); pid = getpid(); printState(getpid(), pid, i, j, t); for (j = 0 ; j lt; i 2; j ){ printState(getpid(), pid, i, j, t); t = fork(); printState(getpid(), pid, i, j, t); if (t != 0) { printState(getpid(), pid, i, j, t); t = fork(); printState(getpid(), pid, i, j, t); break; } } if (pid != getpid()) printState(getpid(), pid, i, j, t); break; }
и соберите выходные данные, упорядочив их по их самоотчетным номерам pid (getpid(), а не переменная pid)
Комментарии:
1. Большое вам спасибо @Edwin Buck за ваш замечательный ответ.
2. Как я уже сказал, я понимаю большую часть процесса создания, но я все еще не вижу смысла в двух самых внешних процессах или созданных листьях (#3758 и #3759 в моем дереве процессов). Внутренний цикл применяется два раза fork(), первый из которых является общей вилкой (), а второй предназначен только для отцовских процессов. Два самых внешних листа создаются в соответствии с внутренним шаблоном цикла, но почему бы и остальным процессам внуков не порождать листья тоже? Спасибо
3. @The_Student Существует разница между пониманием того, как работает вилка, и моделированием процессов, описанных выше. Только правильное моделирование приведенных выше процессов полностью объяснит, почему были созданы эти последние процессы. Я предполагаю, что вы смоделировали кое-что из этого, но не все, и строите логические догадки, которые охватывают многие (если не большинство) процессов. Пожалуйста, предоставьте более подробную информацию, кроме «покажите мне», и я смогу помочь разобраться в ошибках (или, по крайней мере, лучше понять вашу проблему).
4. Именно @Эдвин Бак, только отслеживая каждый процесс, вы можете понять все это целиком. Я создал дерево процессов со всеми i и j, и да, создано 11 процессов, и два самых внешних листа генерируются i = 1 и j = 2 (это единственные процессы, которые удовлетворяют этому условию). Ваш ответ был решающим для того, чтобы помочь мне смоделировать весь код. Еще раз большое вам спасибо. С уважением
5. Я скоро опубликую упомянутое дерево процессов.
Ответ №2:
Вот процедура создания процесса на основе моделирования программы, объясненная @Edwin Buck (спасибо, Эдвин!)
1 | ---- 2 ----- / / 3 4 5 6 / / 7 8 9 10 / 11 12
Процесс № 1: отец начального процесса.
Процесс № 2: Начальный процесс. Это единственный процесс, который выполняет pid = getpid().
Процесс № 3: Порожден при i = 0, j = 0.
Процесс № 4: Порожден при i = 0, j = 0.
Процесс № 7: Порожден при i = 0, j = 1.
Процесс № 8: Порожден при i = 0, j = 1.
Процесс № 5: Порожден при i = 1, j = 0.
Процесс № 6: Порожден при i = 1, j = 0.
Процесс № 9: Порожден при i = 1, j = 1.
Процесс № 10: Порожден при i = 1, j = 1.
Процесс № 11: Порожден при i = 1, j = 2.
Процесс № 12: Порожден при i = 1, j = 2.