Ваш исходный код запускает дочерние процессы в последовательности, а не одновременно, потому что у вас есть вызов wait()
внутри l oop.
Вам не нужно копировать имя программы. Вы можете либо использовать argv[1]
напрямую (или просто присвоить его nameExec
), либо пропустить первые пару символов, используя nameExec = &argv[1][2];
.
Очень сложно понять, как работает l oop в вашем коде; это заставило меня несколько раз закричать, когда я пытался обернуть это вокруг себя. Я собираюсь просто написать код с нуля - в двух вариантах.
Вариант 1
В более простом для понимания варианте родительский (начальный) процесс запускает по одному дочернему элементу на каждый счетчик, а затем он ждет, пока не останется детей. Он сообщает PID и статус выхода детей при их выходе; было бы целесообразно просто собрать трупы, не печатая «в память».
/* SO 6021-0236 */
/* Variant 1: Original process forks children and waits for them to complete */
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
int main(int argc, char **argv)
{
assert(argc > 2);
/* Launch children */
for (int i = 2; i < argc; i++)
{
if (fork() == 0) // child process
{
execl(argv[1], argv[1], argv[i], (char *)0);
fprintf(stderr, "failed to execute %s\n", argv[1]);
exit(EXIT_FAILURE);
}
}
/* Wait for children */
int corpse;
int status;
while ((corpse = wait(&status)) > 0)
{
printf("%d: PID %d exited with status 0x%.4X\n",
(int)getpid(), corpse, status);
}
return 0;
}
Я переименовал вашу программу-счетчик, чтобы исходный файл был counter23.c
, а программа - counter23
и единственный другой значительные изменения удалили пробел перед двоеточием в выводе printf()
.
Я назвал исходный код выше multiple43.c
, скомпилированный в multiple43
.
$ multiple43 count23 1
54251: start
54251: 1
54251: done
54250: PID 54251 exited with status 0x0000
$ multiple43 count23 3 4 5
54261: start
54261: 5
54260: start
54260: 4
54259: start
54259: 3
54261: 4
54260: 3
54259: 2
54261: 3
54260: 2
54259: 1
54261: 2
54260: 1
54259: done
54258: PID 54259 exited with status 0x0000
54261: 1
54260: done
54258: PID 54260 exited with status 0x0000
54261: done
54258: PID 54261 exited with status 0x0000
$
В прогоне с три ребенка, вы можете видеть, что все три производят вывод одновременно.
Это вариант, который я думаю, вы должны использовать, если нет явного требования сделать что-то еще.
Вариант 2
Другой вариант более или менее приближает ваш код (хотя это приближение не очень хорошее) в том смысле, что сам исходный процесс тоже выполняет программу счетчика. Следовательно, если исходный процесс имеет меньше циклов, чем другие, он завершается до завершения других (см. Разницу между примерами 3 4 5
и 5 4 3
). Однако счетчики запускаются одновременно.
/* SO 6021-0236 */
/* Variant 2: Original process launches children, the execs itself */
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
int main(int argc, char **argv)
{
assert(argc > 2);
/* Launch children */
for (int i = 3; i < argc; i++)
{
if (fork() == 0) // child process
{
execl(argv[1], argv[1], argv[i], (char *)0);
fprintf(stderr, "failed to execute %s\n", argv[1]);
exit(EXIT_FAILURE);
}
}
execl(argv[1], argv[1], argv[2], (char *)0);
fprintf(stderr, "failed to execute %s\n", argv[1]);
return(EXIT_FAILURE);
}
Этот код был multiple53.c
скомпилирован в multiple53
.
$ multiple53 count23 3 4 5
54269: start
54268: start
54267: start
54269: 5
54268: 4
54267: 3
54269: 4
54268: 3
54267: 2
54268: 2
54267: 1
54269: 3
54268: 1
54267: done
54269: 2
$ 54268: done
54269: 1
54269: done
$ multiple53 count23 5 4 3
54270: start
54272: start
54270: 5
54272: 3
54271: start
54271: 4
54270: 4
54272: 2
54271: 3
54272: 1
54270: 3
54271: 2
54271: 1
54272: done
54270: 2
54270: 1
54271: done
54270: done
$
Пустая строка появилась, потому что я нажал return - приглашение появился на 3 строки раньше, но за ним последовало больше выводов из 54268 и 54269. Я считаю, что это гораздо реже, чем нужно.
Инструментальный вариант 0
Чтобы попытаться понять исходный код, Я проинструктировал его после внесения некоторых небольших изменений (сохраненных в multiple31.c
и скомпилированных в multiple31
):
/* SO 6021-0236 */
/* Original algorithm with instrumentation */
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
int main(int argc, char **argv)
{
assert(argc > 2);
char *nameExec = argv[1];
char *time;
int number = argc - 2;
if (number == 1)
{
printf("%d: name = %s; time = %s\n", (int)getpid(), nameExec, argv[2]);
execl(argv[1], nameExec, argv[2], NULL);
}
else
{
for (int i = 2; i <= number; i++) // Idempotent change in condition
{
printf("%d: i = %d; number = %d\n", (int)getpid(), i, number);
pid_t kid = fork();
if (kid == 0)
{
time = argv[i];
printf("%d: i = %d; time = %s; ppid = %d\n",
(int)getpid(), i, time, (int)getppid());
}
else
{
time = argv[i + 1];
printf("%d: i = %d; time = %s; waiting for %d\n",
(int)getpid(), i, time, (int)kid);
int status;
int corpse = wait(&status);
printf("%d: i = %d; time = %s; PID %d exited with status 0x%.4X\n",
(int)getpid(), i, time, corpse, status);
}
}
printf("%d: name = %s; time = %s\n", (int)getpid(), nameExec, time);
execl(argv[1], nameExec, time, NULL);
}
printf("%d: this should not be reached!\n", (int)getpid());
return 0;
}
При запуске 4 раза он выдает такие данные, как:
$ multiple31 count23 5 4 3 2
54575: i = 2; number = 4
54575: i = 2; time = 4; waiting for 54576
54576: i = 2; time = 5; ppid = 54575
54576: i = 3; number = 4
54576: i = 3; time = 3; waiting for 54577
54577: i = 3; time = 4; ppid = 54576
54577: i = 4; number = 4
54577: i = 4; time = 2; waiting for 54578
54578: i = 4; time = 3; ppid = 54577
54578: name = count23; time = 3
54578: start
54578: 3
54578: 2
54578: 1
54578: done
54577: i = 4; time = 2; PID 54578 exited with status 0x0000
54577: name = count23; time = 2
54577: start
54577: 2
54577: 1
54577: done
54576: i = 3; time = 3; PID 54577 exited with status 0x0000
54576: i = 4; number = 4
54576: i = 4; time = 2; waiting for 54579
54579: i = 4; time = 3; ppid = 54576
54579: name = count23; time = 3
54579: start
54579: 3
54579: 2
54579: 1
54579: done
54576: i = 4; time = 2; PID 54579 exited with status 0x0000
54576: name = count23; time = 2
54576: start
54576: 2
54576: 1
54576: done
54575: i = 2; time = 4; PID 54576 exited with status 0x0000
54575: i = 3; number = 4
54575: i = 3; time = 3; waiting for 54580
54580: i = 3; time = 4; ppid = 54575
54580: i = 4; number = 4
54580: i = 4; time = 2; waiting for 54581
54581: i = 4; time = 3; ppid = 54580
54581: name = count23; time = 3
54581: start
54581: 3
54581: 2
54581: 1
54581: done
54580: i = 4; time = 2; PID 54581 exited with status 0x0000
54580: name = count23; time = 2
54580: start
54580: 2
54580: 1
54580: done
54575: i = 3; time = 3; PID 54580 exited with status 0x0000
54575: i = 4; number = 4
54575: i = 4; time = 2; waiting for 54582
54582: i = 4; time = 3; ppid = 54575
54582: name = count23; time = 3
54582: start
54582: 3
54582: 2
54582: 1
54582: done
54575: i = 4; time = 2; PID 54582 exited with status 0x0000
54575: name = count23; time = 2
54575: start
54575: 2
54575: 1
54575: done
$
Отслеживание, почему это вывод fiendi sh. Я начал писать объяснение, но обнаружил, что мое объяснение не соответствует фактическому результату - еще раз. Тем не менее, инструментарий, показанный ниже, - вот как я обычно понимаю, что происходит. Один из ключевых моментов (слегка упрощенный) состоит в том, что все ожидают ребенка до d ie, за исключением одного ребенка, который выполняет обратный отсчет. Выполнение тестов с 1, 2 или 3 раза вместо 4 согласуется с этим, но проще (одновременно менее запутанным и более запутанным). Использование в 5 раз увеличивает объем вывода, но на самом деле не дает большего просветления.