Como @AndrewHenle comentou, dependendo do sistema para agendar seus processos em uma sequência específica, é inseguro e injustificado. Mesmo quando o agendamento parece ser consistente (como no seu caso), não há nada que impeça os implementadores do sistema operacional de alterar o comportamento do agendador.
Se a ordem de operações entre processos / threads for relevante, alguma forma de comunicação é necessária.
Para o seu cenário, uma simples leitura de bloqueio pode fazer o trabalho. Crie um tubo antes do garfo. Em seguida, escreva para o pipe do pai apenas depois que o pai tiver imprimido sua mensagem. Enquanto isso, a tentativa da criança de ler do canal bloqueará sua execução até que o pai escreva.
Negligenciando a manipulação de erros e os descritores de arquivos de pipe não utilizados em cada processo (que geralmente são explicitamente fechados após o bifurcação):
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(void) {
char buf[1];
int pipe_fds[2];
pipe(pipe_fds);
pid_t pid = fork();
if (pid > 0) {
printf("[%ld]: Writing from parent process\n", getpid());
write(pipe_fds[1], "_", 1);
wait(NULL);
}
if (pid == 0) {
read (pipe_fds[0], buf, 1);
execl("/usr/bin/cat", "/usr/bin/cat", "file.c", (char *) 0);
perror("exec failed");
exit(1);
}
Você provavelmente deve usar wait no pai, de modo que, se for executado a partir de um terminal, a saída do filho e o prompt do shell não serão intercalados.