Subtopic 2
State
Code Examples
Why Shared State Matters
The shared file offset and status flags between parent and child are not just a curiosity — they are intentional and useful. When both processes write to the same file, sharing the offset prevents them from overwriting each other. But if you don’t want sharing, you need to close and re-open files in each process independently.
When multiple processes write to a log file, O_APPEND ensures each write goes to the end atomically — no overwriting:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <time.h>
#define LOG_FILE "/tmp/process_log.txt"
void write_log(int fd, const char *who, int count)
{
char buf[128];
for (int i = 0; i < count; i++) {
int len = snprintf(buf, sizeof(buf),
"[%s PID=%d] Log entry #%d\n",
who, getpid(), i+1);
write(fd, buf, len);
/* Small sleep to interleave with other process */
usleep(10000);
}
}
int main(void)
{
/* Open with O_APPEND: writes always go to end, atomically */
int fd = open(LOG_FILE,
O_WRONLY | O_CREAT | O_TRUNC | O_APPEND,
0644);
if (fd == -1) { perror("open"); exit(1); }
pid_t pid = fork();
if (pid == -1) { perror("fork"); exit(1); }
if (pid == 0) {
write_log(fd, "Child ", 5);
close(fd);
_exit(0);
}
write_log(fd, "Parent", 5);
close(fd);
wait(NULL);
printf("Log file contents (%s):\n", LOG_FILE);
system("cat " LOG_FILE);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
void print_flags(int fd, const char *label)
{
int flags = fcntl(fd, F_GETFL);
printf("[%s] O_APPEND=%s O_NONBLOCK=%s\n",
label,
(flags & O_APPEND) ? "ON" : "off",
(flags & O_NONBLOCK) ? "ON" : "off");
}
int main(void)
{
int fd = open("/tmp/flags_test.txt",
O_RDWR | O_CREAT | O_TRUNC, 0644);
if (fd == -1) { perror("open"); exit(1); }
setbuf(stdout, NULL);
print_flags(fd, "Before fork");
pid_t pid = fork();
if (pid == -1) { perror("fork"); exit(1); }
if (pid == 0) {
/* Child adds O_APPEND and O_NONBLOCK */
int flags = fcntl(fd, F_GETFL);
flags |= O_APPEND | O_NONBLOCK;
fcntl(fd, F_SETFL, flags);
print_flags(fd, "Child after setting");
_exit(0);
}
wait(NULL);
/* Parent sees flags set by child (shared OFT entry!) */
print_flags(fd, "Parent after child exits");
/* Parent removes O_APPEND */
int flags = fcntl(fd, F_GETFL);
flags &= ~O_APPEND;
fcntl(fd, F_SETFL, flags);
print_flags(fd, "Parent after removing O_APPEND");
close(fd);
return 0;
}
If you DON’T want shared offsets, open the file independently in each process:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
int main(void)
{
setbuf(stdout, NULL);
/* Write test content */
int setup_fd = open("/tmp/read_test.txt",
O_WRONLY | O_CREAT | O_TRUNC, 0644);
write(setup_fd, "ABCDEFGHIJ", 10);
close(setup_fd);
pid_t pid = fork();
if (pid == -1) { perror("fork"); exit(1); }
if (pid == 0) {
/* Child opens its OWN file descriptor independently */
int fd = open("/tmp/read_test.txt", O_RDONLY);
if (fd == -1) { perror("open child"); exit(1); }
lseek(fd, 5, SEEK_SET); /* child seeks to position 5 */
char buf[4] = {0};
read(fd, buf, 3);
printf("[Child ] Read from pos 5: '%s'\n", buf);
close(fd);
_exit(0);
}
/* Parent also opens its OWN file descriptor */
int fd = open("/tmp/read_test.txt", O_RDONLY);
if (fd == -1) { perror("open parent"); exit(1); }
/* Parent reads from position 0 regardless of what child does */
char buf[4] = {0};
read(fd, buf, 3);
printf("[Parent] Read from pos 0: '%s'\n", buf);
wait(NULL);
close(fd);
return 0;
}
[Parent] Read from pos 0: 'ABC'[Child ] Read from pos 5: 'FGH'Each process has its own offset because they opened the file independently after fork.
Use O_APPEND flag when opening. Writes with O_APPEND are atomic: the OS does a seek-to-end + write in a single atomic operation. You can also use file locking (fcntl F_SETLKW) to get exclusive access before writing, or use a pipe/socket to serialize writes through a single process.
Close the inherited file descriptor in the child and re-open the file with a new open() call. The new open() creates a fresh open file description with its own offset starting at 0 (or wherever you lseek to). The parent’s offset is unaffected.
O_APPEND means every write() automatically seeks to the current end-of-file before writing, as a single atomic operation. This prevents two processes from overwriting each other even when writing concurrently. The kernel holds a lock internally during the seek+write sequence.
