你已经接近了,但是有很多问题,其中许多在问题的 cmets 中指出:
- 你重新执行
argv[0]——程序——预示着不妙。
- 您通常应该在分叉子项的循环中执行子项的操作。
- 如果您希望子项同时执行,则需要在分叉它们的循环之外调用
wait()。
- 即使规范要求执行命令行参数,您仍从标准输入读取命令。
- 您使用从标准输入读取的数据作为执行进程的命令名称,而不是命令行参数。
- 您有一个局部变量
argv 将argv 参数隐藏(隐藏)到main()。因此,execvp() 中的代码在 counter 达到 2 后访问了 argv 数组的边界外,并且当 counter 为 1 时使用空指针。
- 您不应在子执行失败后报告成功 (
exit(0);)。
- 我认为您应该在子进程执行失败时报告哪个进程执行失败 - 当然是标准错误。
顺便说一句,您的示例命令行都使用命令的完整路径,因此无需使用execvp()。你完全可以只输入命令的名称,就像在 shell 提示符下一样,然后execvp() 会找到命令并运行它(参见我的示例运行)。
异步执行
修复这些并添加一些小的调整,并异步运行命令行参数(可能同时运行许多命令)产生如下代码:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
for (int counter = 1; counter < argc; counter++)
{
pid_t pid = fork();
if (pid < 0)
{
perror("forking child process failed\n");
exit(EXIT_FAILURE);
}
else if (pid == 0) // child
{
char *args[] = { argv[counter], NULL };
execvp(args[0], args);
fprintf(stderr, "%s: failed to execute %s: (%d) %s\n",
argv[0], argv[counter], errno, strerror(errno));
exit(EXIT_FAILURE);
}
}
int corpse;
int status;
int failed = 0;
while ((corpse = waitpid(0, &status, 0)) > 0)
{
if (WIFEXITED(status))
{
printf("Process %d exited with status %d (0x%.4X)\n",
corpse, WEXITSTATUS(status), status);
if (WEXITSTATUS(status) != 0)
failed++;
}
else if (WIFSIGNALED(status))
{
printf("Process %d was signalled %d (0x%.4X)\n",
corpse, WTERMSIG(status), status);
failed++;
}
}
return((failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE);
}
failed 变量跟踪是否有任何命令失败,如果每个命令都成功,进程只报告成功(以 0 或 EXIT_SUCCESS 退出);否则,它会以失败指示退出(以 EXIT_FAILURE 退出,通常为 1)。
在我的 Mac 上运行时(作为从 args41.c 创建的 args41),它产生了:
$ args41 uname date ls
Sun Sep 15 07:26:58 MDT 2019
Darwin
Process 1907 exited with status 0 (0x0000)
Process 1908 exited with status 0 (0x0000)
20000-leagues-under-the-sea.txt bin maxargs.sh
LICENSE.md conn11 overlap.data
Metriseis_2012.dat conn11.c overlap47.c
README.md conn11.dSYM overlap61.c
Safe conn13 overlap73
Untracked conn13.c overlap73.c
acr.list conn13.dSYM overlap73.dSYM
acronym29.c conn17 packages
acronym43 conn17.c pseudo-json.md
acronym43.c conn17.dSYM question.md
acronym43.dSYM conn29 sll43.c
acronym47 conn29.c so-0167-2112
acronym47.c conn29.dSYM so-0167-2112.c
acronym47.dSYM doc so-0167-2112.dSYM
acronym53 dr41 so-1043-1305
acronym53.c dr41.c so-4921-8019
acronym53.dSYM dr41.dSYM so-4970-8730
acronym59 dw41 so-4971-1989
acronym59.c dw41.c so-4985-0919
acronym59.dSYM dw41.dSYM so-5102-0102
argc37 etc so-5134-1743
argc37.c gccw67 so-5225-1783
argc37.dSYM gccw67.c so-5279-4924
args41 gccw67.dSYM so-5358-5962
args41.c gccw67.o so-5394-5215
args41.dSYM get.jl.activity so-5416-6308
argv89 inc so-5424-4465.md
argv89.c lib src
argv89.dSYM makefile
Process 1909 exited with status 0 (0x0000)
$ args41 many things to do
args41: failed to execute many: (2) No such file or directory
args41: failed to execute things: (2) No such file or directory
args41: failed to execute to: (2) No such file or directory
args41: failed to execute do: (2) No such file or directory
Process 1939 exited with status 1 (0x0100)
Process 1941 exited with status 1 (0x0100)
Process 1940 exited with status 1 (0x0100)
Process 1942 exited with status 1 (0x0100)
$
显然,如果您希望进程同步运行,您应该将waitpid() 循环放在for 循环内。您仍然应该使用循环,因为一个进程可以继承它在晦涩的情况下没有派生的子进程。在这种情况下,您可能更喜欢使用waitpid(pid, &status, 0);那么你就不需要围绕waitpid() 的循环了。
如果您想跟踪进程名称并异步运行进程,则父进程将需要在每个子进程被分叉时将其 PID 记录在一个数组中,并且报告循环将搜索要报告哪一个死亡的已知儿童名单。
同步执行
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int failed = 0;
for (int counter = 1; counter < argc; counter++)
{
pid_t pid = fork();
if (pid < 0)
{
perror("forking child process failed\n");
exit(EXIT_FAILURE);
}
else if (pid == 0) // child
{
char *args[] = { argv[counter], NULL };
execvp(args[0], args);
fprintf(stderr, "%s: failed to execute %s: (%d) %s\n",
argv[0], argv[counter], errno, strerror(errno));
exit(EXIT_FAILURE);
}
else
{
int status;
if (waitpid(pid, &status, 0) < 0)
{
fprintf(stderr, "failed to wait for %s process %d: (%d) %s\n",
argv[counter], (int)pid, errno, strerror(errno));
failed++;
}
else if (WIFEXITED(status))
{
printf("Process %s exited with status %d (0x%.4X)\n",
argv[counter], WEXITSTATUS(status), status);
if (WEXITSTATUS(status) != 0)
failed++;
}
else if (WIFSIGNALED(status))
{
printf("Process %s was signalled %d (0x%.4X)\n",
argv[counter], WTERMSIG(status), status);
failed++;
}
else
{
printf("Process %s died unexpectedly (0x%.4X)\n",
argv[counter], status);
failed++;
}
}
}
return((failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE);
}
示例输出(args89 创建自 args89.c):
$ args89 date uname ls
Sun Sep 15 08:34:00 MDT 2019
Process date exited with status 0 (0x0000)
Darwin
Process uname exited with status 0 (0x0000)
20000-leagues-under-the-sea.txt argv89.c makefile
LICENSE.md argv89.dSYM maxargs.sh
Metriseis_2012.dat bin overlap.data
README.md conn11 overlap47.c
Safe conn11.c overlap61.c
Untracked conn11.dSYM overlap73
acr.list conn13 overlap73.c
acronym29.c conn13.c overlap73.dSYM
acronym43 conn13.dSYM packages
acronym43.c conn17 pseudo-json.md
acronym43.dSYM conn17.c question.md
acronym47 conn17.dSYM sll43.c
acronym47.c conn29 so-0167-2112
acronym47.dSYM conn29.c so-0167-2112.c
acronym53 conn29.dSYM so-0167-2112.dSYM
acronym53.c doc so-1043-1305
acronym53.dSYM dr41 so-4921-8019
acronym59 dr41.c so-4970-8730
acronym59.c dr41.dSYM so-4971-1989
acronym59.dSYM dw41 so-4985-0919
argc37 dw41.c so-5102-0102
argc37.c dw41.dSYM so-5134-1743
argc37.dSYM etc so-5225-1783
args41 gccw67 so-5279-4924
args41.c gccw67.c so-5358-5962
args41.dSYM gccw67.dSYM so-5394-5215
args89 gccw67.o so-5416-6308
args89.c get.jl.activity so-5424-4465.md
args89.dSYM inc src
argv89 lib
Process ls exited with status 0 (0x0000)
$
带有进程名称跟踪的异步执行
此变体异步运行命令,但也跟踪属于每个 PID 的进程。请注意,它使用一个函数(冲击、恐怖)来确定与刚刚死亡的 PID 对应的参数。数组的设置便于跟踪数字——PID 数组的元素 0 未被使用。
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
static int find_pid(int pid, int num_pids, pid_t pids[num_pids])
{
for (int i = 1; i < num_pids; i++)
{
if (pids[i] == pid)
return i;
}
return -1;
}
int main(int argc, char *argv[])
{
pid_t pids[argc];
for (int counter = 1; counter < argc; counter++)
{
pid_t pid = fork();
if (pid < 0)
{
perror("forking child process failed\n");
exit(EXIT_FAILURE);
}
else if (pid == 0) // child
{
char *args[] = { argv[counter], NULL };
execvp(args[0], args);
fprintf(stderr, "%s: failed to execute %s: (%d) %s\n",
argv[0], argv[counter], errno, strerror(errno));
exit(EXIT_FAILURE);
}
else
pids[counter] = pid;
}
int corpse;
int status;
int failed = 0;
while ((corpse = waitpid(0, &status, 0)) > 0)
{
int index = find_pid(corpse, argc, pids);
if (index < 0)
{
fprintf(stderr, "Unrecognized PID %d exited with status 0x%.4X\n",
corpse, status);
failed++;
}
else if (WIFEXITED(status))
{
printf("Process %s (PID %d) exited with status %d (0x%.4X)\n",
argv[index], corpse, WEXITSTATUS(status), status);
if (WEXITSTATUS(status) != 0)
failed++;
}
else if (WIFSIGNALED(status))
{
printf("Process %s (PID %d) was signalled %d (0x%.4X)\n",
argv[index], corpse, WTERMSIG(status), status);
failed++;
}
else
{
printf("Process %s (PID %d) died from indeterminate causes (0x%.4X)\n",
argv[index], corpse, status);
failed++;
}
}
return((failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE);
}
示例运行(args79 创建自 args79.c):
$ args79 uname date pwd
/Users/jleffler/soq
Process pwd (PID 2105) exited with status 0 (0x0000)
Darwin
Sun Sep 15 09:04:37 MDT 2019
Process uname (PID 2103) exited with status 0 (0x0000)
Process date (PID 2104) exited with status 0 (0x0000)
$