There are multiple problems with the code, including that what you provide is not really an MCVE (Minimal, Complete and Verifiable Example).
Here is revised code, closely based on your code, with some of the flaws in your code still present but others fixed. Some extra instrumentation is added.
#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;
}
Example output
Program was called wst13
, and its output was run through a program vis
that makes non-printing characters visible.
$ 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
$
What changed?
The variable fout
was not set as melpomene noted, so it wasn't safe to use in the first write()
call. I replaced it with the 'official' POSIX name for the standard output file descriptor, STDOUT_FILENO
, though it is tempting to use 1
instead. The variables ppid
and cpid
were not used so they were removed too.
The action for the else if (returnchild == 0)
was written. It reports some PID values, then calls waitpid()
because your comments said it did — and the waitpid()
fails because the child has not created any of its own children.
The action for the else
(parent) process was written. It reports some PID values, then calls waitpid()
in a loop, reporting the PID and status of any dead child, and exiting the loop when there are no more children to wait for. It then reports when it is all done.
One of the problems cited was that the first and third write()
operations write trailing nulls because the lengths are one too long, and hence include the terminal null. You can see these in the output: they are reported as \000
by the vis
program. Also the middle write()
prints the 4 bytes of the pid
as raw data, leading to the byte sequence \035@\000\000
. When treated as the bytes of a little-endian integer, that amounts to 256 * 64 + 29 = 16413, which is the value recorded in pid
.
The sequencing of the outputs is indeterminate on a multi-core machine (Intel Core i7 inside a MacBook Pro, in this case). However, the child's error is reported before any of the standard output is reported by vis
(see printf()
anomaly after fork()
. Without the use of | vis
, sample output looks like:
$ 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
$
What's still a problem?
I commented:
Note that the last write()
prints a null byte to the terminal. The middle write()
prints 4 or 8 bytes of binary data; that is not going to be readable. The first write()
also prints a null byte to the terminal. When using the low-level I/O functions like read()
and write()
, you have to do the formatting, or use the POSIX function dprintf()
to do the formatting to a file descriptor (as opposed to a file stream like fprintf()
et al).
One of the many ways of fixing that would be:
// 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);
}
That still prints gibberish for the PID. Another option is:
// 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);
There are a multitude of other ways to get the same output from that. And you can fix the middle call to 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);
}
And so the possible remedies continue. How many more ways can you find to do it?