Карл ответил великолепно. Я хотел бы добавить, что во многих операционных системах возвращаемые значения передаются в один из регистров. В архитектуре x86 этот регистр может быть eax, в архитектуре ARM этот регистр может быть R0 и т. Д.
Каждый процесс также имеет блок управления процессом (PCB), в котором хранятся значения регистров в тот момент, когда произошло какое-то прерывание, системный вызов или исключение, и управление было передано в ОС. В следующий раз, когда запланирован процесс, значения регистров восстанавливаются с печатной платы.
Теперь, когда происходит fork (), ОС может сделать:
child_process->PCB[return_value_register] = 0;
parrent_process->PCB[return_value_register] = child_pid;
Итак, когда процессы перепланированы, каждый из них видит различное возвращаемое значение.
В качестве примера вы можете увидеть реализацию xv6 для fork . Там родительский процесс все еще находится в рабочем состоянии, поэтому он возвращает возвращаемое значение родителя с помощью простого оператора возврата. Но он устанавливает значение регистра EAX для дочернего процесса равным 0, поэтому, когда дочерний процесс запланирован, он видит 0 в качестве возвращаемого значения:
// Clear %eax so that fork returns 0 in the child.
np->tf->eax = 0;
Обратите внимание, что return 0 также будет компилироваться во что-то вроде "mov eax, 0".
Обновление: Я только что реализовал fork () для хобби ОС, которой занимаюсь. Вы можете увидеть исходный код здесь .