代码存在多个问题,包括您提供的不是真正的 MCVE (Minimal, Complete and Verifiable Example)。
这里是修改后的代码,与您的代码密切相关,您的代码中的一些缺陷仍然存在,但其他缺陷已修复。添加了一些额外的仪器。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main(void)
{
int status, waitcall;
pid_t pid = getpid();
// write to terminal
write(STDOUT_FILENO, "Hello World - I am parent: ", 29);
write(STDOUT_FILENO, (void *) & pid, sizeof(pid));
write(STDOUT_FILENO, " !!\n\n", 7);
int returnchild = fork();
if (returnchild < 0)
{
perror("fork() failed");
exit(EXIT_FAILURE);
}
else if (returnchild == 0)
{
// child (new process)
printf("Child of %d (%d) at play: %d\n", (int)pid, (int)getppid(), (int)getpid());
waitcall = waitpid(-1, &status, 0);
if (waitcall == -1)
{
perror("child - waitpid() failed");
exit(EXIT_FAILURE);
}
printf("Wait return PID %d (status 0x%.4X)\n", waitcall, status);
exit(EXIT_SUCCESS);
}
else
{
// parent goes down this path
printf("Parent %d created child %d\n", (int)getpid(), returnchild);
while ((waitcall = waitpid(-1, &status, 0)) != -1)
printf("Parent waited for %d (status 0x%.4X)\n", waitcall, status);
printf("Parent: all done\n");
}
return 0;
}
示例输出
程序被称为wst13,它的输出是通过一个程序vis 运行的,该程序使非打印字符可见。
$ wst13 | vis
child - waitpid() failed: No child processes
Hello World - I am parent: \000\035@\000\000 !!
\000Child of 16413 (16413) at play: 16415
Parent 16413 created child 16415
Parent waited for 16415 (status 0x0100)
Parent: all done
$
发生了什么变化?
变量fout was not set 如melpomene 所述,因此在第一个write() 调用中使用它是不安全的。我将它替换为标准输出文件描述符的“官方”POSIX 名称STDOUT_FILENO,尽管使用1 来代替它很诱人。变量ppid 和cpid 没有被使用,所以它们也被删除了。
else if (returnchild == 0) 的操作已写入。它会报告一些 PID 值,然后调用 waitpid(),因为您的 cmets 说它这样做了 - 而 waitpid() 失败,因为孩子没有创建任何自己的孩子。
else(父)进程的操作已写入。它报告一些 PID 值,然后在循环中调用waitpid(),报告任何死子的 PID 和状态,并在没有更多子等待时退出循环。然后它会在所有完成时报告。
cited 的问题之一是第一个和第三个write() 操作写入尾随空值,因为长度太长,因此包含终端空值。您可以在输出中看到这些:vis 程序将它们报告为 \000。同样,中间的write() 将pid 的4 个字节打印为原始数据,导致字节序列\035@\000\000。当作为 little-endian 整数的字节处理时,等于 256 * 64 + 29 = 16413,也就是 pid 中记录的值。
在多核机器(本例中为 MacBook Pro 内的 Intel Core i7)上,输出的顺序是不确定的。但是,在vis 报告任何标准输出之前报告孩子的错误(请参阅printf() anomaly after fork()。不使用| vis,示例输出如下所示:
$ wst13
Hello World - I am parent: %@ !!
Parent 16421 created child 16422
Child of 16421 (16421) at play: 16422
child - waitpid() failed: No child processes
Parent waited for 16422 (status 0x0100)
Parent: all done
$
还有什么问题?
我commented:
请注意,最后一个write() 会向终端打印一个空字节。中间的write()打印4或8字节的二进制数据;那是不可读的。第一个write() 也会向终端打印一个空字节。当使用 read() 和 write() 等低级 I/O 函数时,您必须进行格式化,或者使用 POSIX 函数 dprintf() 对文件描述符进行格式化(而不是像fprintf() 等)。
解决此问题的众多方法之一是:
// write to terminal
const char hw[] = "Hello World - I am parent: ";
const char nn[] = " !!\n\n";
if (write(STDOUT_FILENO, hw, sizeof(hw) - 1) != sizeof(hw) - 1 ||
write(STDOUT_FILENO, (void *) & pid, sizeof(pid)) != sizeof(pid) ||
write(STDOUT_FILENO, nn, sizeof(nn) - 1) != sizeof(nn) - 1)
{
perror("short write to standard output");
exit(EXIT_FAILURE);
}
这仍然会为 PID 打印乱码。另一种选择是:
// write to terminal
const char hw[] = "Hello World - I am parent: ";
const char nn[] = " !!\n\n";
dprintf(STDOUT_FILENO, "%s%d%s", hw, (int)pid, nn);
还有许多其他方法可以从中获得相同的输出。您可以将中间调用修复为write():
// write to terminal
const char hw[] = "Hello World - I am parent: ";
const char nn[] = " !!\n\n";
char pidstr[16];
snprintf(pidstr, sizeof(pidstr), "%d", (int)pid);
int pidlen = strlen(pidstr);
if (write(STDOUT_FILENO, hw, sizeof(hw) - 1) != sizeof(hw) - 1 ||
write(STDOUT_FILENO, pidstr, pidlen) != pidlen ||
write(STDOUT_FILENO, nn, sizeof(nn) - 1) != sizeof(nn) - 1)
{
perror("short write to standard output");
exit(EXIT_FAILURE);
}
因此可能的补救措施仍在继续。你还能找到多少种方法呢?