Chapter 32Advanced

UNIX/Linux System Calls

Interact with the operating system directly! Learn open, read, write, fork, exec, and other system calls that give C its power on UNIX/Linux.

25 min readUpdated 2024-12-16
system callsunixlinuxopenreadwriteforkexecpidfile descriptorunistd.h

What You Will Learn

  • Understand system calls vs library functions
  • Use file descriptors (open, read, write, close)
  • Create processes with fork and exec
  • Handle errors with errno and perror
  • Work with directories (getcwd, chdir, mkdir)

01What are System Calls?

🐧 C and the Operating System

System calls are how your C programs talk to the operating system (Linux/UNIX). They let you create files, run programs, manage memory, and more — directly through the OS kernel!

Library Functions (stdio.h)

High-level, buffered, portable

fopen(), fread(), fwrite()

System Calls (unistd.h)

Low-level, direct, OS-specific

open(), read(), write()

📋 Common System Calls

CategorySystem CallsHeader
File I/Oopen, close, read, write, lseek<unistd.h> <fcntl.h>
Processfork, exec, wait, exit, getpid<unistd.h> <sys/wait.h>
Directorymkdir, rmdir, chdir, getcwd<unistd.h> <sys/stat.h>
File Infostat, chmod, chown, unlink<sys/stat.h>

02File Descriptors

📂 What is a File Descriptor?

A file descriptor is a small non-negative integer that the OS uses to identify an open file. Every process starts with 3 open file descriptors:

0

stdin (keyboard input)

1

stdout (screen output)

2

stderr (error output)

03open() - Open a File

open() - Open or Create a File

int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
pathname: Path to the file
flags: O_RDONLY, O_WRONLY, O_RDWR, O_CREAT, O_TRUNC, O_APPEND
mode: Permissions (0644, 0755) - only when creating
Returns: File descriptor (≥0) or -1 on error

Header: <fcntl.h>

🏷️ Common Flags

FlagDescription
O_RDONLYRead only
O_WRONLYWrite only
O_RDWRRead and write
O_CREATCreate if doesn't exist
O_TRUNCTruncate to zero length
O_APPENDAppend to end
open_example.c
C
1#include <stdio.h>
2#include <fcntl.h> // For open() and flags
3#include <unistd.h> // For close()
4
5int main() {
6 // Open existing file for reading
7 int fd1 = open("data.txt", O_RDONLY);
8 if (fd1 == -1) {
9 perror("Error opening file");
10 return 1;
11 }
12 printf("File opened, fd = %d\n", fd1);
13 close(fd1);
14
15 // Create new file (or truncate existing)
16 // 0644 = rw-r--r-- (owner read/write, others read)
17 int fd2 = open("output.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
18 if (fd2 == -1) {
19 perror("Error creating file");
20 return 1;
21 }
22 close(fd2);
23
24 return 0;
25}

04read() and write() - File I/O

read() - Read from File Descriptor

ssize_t read(int fd, void *buf, size_t count);
fd: File descriptor from open()
buf: Buffer to store data
count: Maximum bytes to read
Returns: Bytes read, 0 at EOF, -1 on error

write() - Write to File Descriptor

ssize_t write(int fd, const void *buf, size_t count);
fd: File descriptor (1 for stdout, 2 for stderr)
buf: Data to write
count: Number of bytes to write
Returns: Bytes written, -1 on error
read_write.c
C
1#include <stdio.h>
2#include <fcntl.h>
3#include <unistd.h>
4#include <string.h>
5
6int main() {
7 // Write to stdout using system call
8 char *msg = "Hello from write()!\n";
9 write(1, msg, strlen(msg)); // 1 = stdout
10
11 // Copy file using read/write
12 int src = open("source.txt", O_RDONLY);
13 int dst = open("copy.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
14
15 if (src == -1 || dst == -1) {
16 perror("Error opening files");
17 return 1;
18 }
19
20 char buffer[1024];
21 ssize_t bytes;
22
23 // Read and write in chunks
24 while ((bytes = read(src, buffer, sizeof(buffer))) > 0) {
25 write(dst, buffer, bytes);
26 }
27
28 close(src);
29 close(dst);
30 printf("File copied!\n");
31
32 return 0;
33}

05close() - Close File Descriptor

close() - Release File Descriptor

int close(int fd);
fd: File descriptor to close
Returns: 0 on success, -1 on error

⚠️ Always Close Files!

Each process has a limited number of file descriptors. Not closing files can lead to resource exhaustion and data loss (buffered data may not be written).

06Process System Calls

fork() - Create Child Process

pid_t fork(void);
Returns: Child PID to parent, 0 to child, -1 on error

Creates an exact copy of the current process!

getpid() / getppid() - Get Process IDs

pid_t getpid(void);
pid_t getppid(void);
fork_example.c
C
1#include <stdio.h>
2#include <unistd.h>
3#include <sys/wait.h>
4
5int main() {
6 printf("Parent PID: %d\n", getpid());
7
8 pid_t pid = fork(); // Create child process
9
10 if (pid == -1) {
11 perror("fork failed");
12 return 1;
13 }
14 else if (pid == 0) {
15 // Child process
16 printf("Child: My PID = %d, Parent PID = %d\n",
17 getpid(), getppid());
18 }
19 else {
20 // Parent process
21 printf("Parent: Created child with PID = %d\n", pid);
22 wait(NULL); // Wait for child to finish
23 printf("Parent: Child finished\n");
24 }
25
26 return 0;
27}
Output

Parent PID: 1234

Parent: Created child with PID = 1235

Child: My PID = 1235, Parent PID = 1234

Parent: Child finished

07exec() Family - Run Programs

exec() Functions

Replace current process with a new program. The "l" variants take list of args, "v" variants take array, "p" searches PATH, "e" sets environment.

FunctionArgsPATHEnv
execl()listNoNo
execlp()listYesNo
execv()arrayNoNo
execvp()arrayYesNo
exec_example.c
C
1#include <stdio.h>
2#include <unistd.h>
3#include <sys/wait.h>
4
5int main() {
6 pid_t pid = fork();
7
8 if (pid == 0) {
9 // Child: run "ls -la"
10 printf("Child running ls...\n");
11 execlp("ls", "ls", "-la", NULL); // Search PATH
12
13 // Only reached if exec fails
14 perror("exec failed");
15 return 1;
16 }
17 else {
18 // Parent waits
19 wait(NULL);
20 printf("Command completed\n");
21 }
22
23 return 0;
24}

08Directory Operations

FunctionPrototypeDescription
getcwd()char *getcwd(char *buf, size_t size)Get current directory
chdir()int chdir(const char *path)Change directory
mkdir()int mkdir(const char *path, mode_t mode)Create directory
rmdir()int rmdir(const char *path)Remove empty directory
directory_ops.c
C
1#include <stdio.h>
2#include <unistd.h>
3#include <sys/stat.h>
4
5int main() {
6 char cwd[256];
7
8 // Get current directory
9 if (getcwd(cwd, sizeof(cwd)) != NULL) {
10 printf("Current dir: %s\n", cwd);
11 }
12
13 // Create a new directory
14 if (mkdir("mydir", 0755) == 0) {
15 printf("Created mydir/\n");
16 }
17
18 // Change to new directory
19 if (chdir("mydir") == 0) {
20 getcwd(cwd, sizeof(cwd));
21 printf("Now in: %s\n", cwd);
22 }
23
24 // Go back and remove
25 chdir("..");
26 rmdir("mydir");
27 printf("Removed mydir/\n");
28
29 return 0;
30}

09Error Handling with errno

📛 errno and perror()

When system calls fail, they return -1 and set the global variable errnoto indicate the error. Use perror() to print a human-readable error message.

errno_example.c
C
1#include <stdio.h>
2#include <fcntl.h>
3#include <unistd.h>
4#include <errno.h>
5#include <string.h>
6
7int main() {
8 int fd = open("nonexistent.txt", O_RDONLY);
9
10 if (fd == -1) {
11 // Method 1: perror (prints to stderr)
12 perror("Error opening file");
13
14 // Method 2: strerror (returns string)
15 printf("Error: %s\n", strerror(errno));
16
17 // Method 3: Check specific error
18 if (errno == ENOENT) {
19 printf("File not found!\n");
20 } else if (errno == EACCES) {
21 printf("Permission denied!\n");
22 }
23
24 return 1;
25 }
26
27 close(fd);
28 return 0;
29}
Output

Error opening file: No such file or directory

Error: No such file or directory

File not found!

10Library Functions vs System Calls

FeatureLibrary (stdio.h)System Call (unistd.h)
Handle TypeFILE *int (fd)
BufferingYes (buffered)No (unbuffered)
PortabilityAll platformsUNIX/Linux only
Openfopen()open()
Readfread(), fgets()read()
Writefwrite(), fputs()write()
Closefclose()close()

💡 When to Use What?

Use library functions (stdio.h) for most file I/O — they're portable and buffered. Use system calls when you need low-level control, process creation, or UNIX-specific features.

11Summary

🎯 Key System Calls

File I/O:

open, close, read, write, lseek

Process:

fork, exec, wait, getpid, exit

Directory:

getcwd, chdir, mkdir, rmdir

Error:

errno, perror, strerror

10Next Steps

Learn about preprocessor directives: