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.
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
System Calls (unistd.h)
Low-level, direct, OS-specific
📋 Common System Calls
| Category | System Calls | Header |
|---|---|---|
| File I/O | open, close, read, write, lseek | <unistd.h> <fcntl.h> |
| Process | fork, exec, wait, exit, getpid | <unistd.h> <sys/wait.h> |
| Directory | mkdir, rmdir, chdir, getcwd | <unistd.h> <sys/stat.h> |
| File Info | stat, 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:
0stdin (keyboard input)
1stdout (screen output)
2stderr (error output)
03open() - Open a File
open() - Open or Create a File
int open(const char *pathname, int flags, mode_t mode);
Header: <fcntl.h>
🏷️ Common Flags
| Flag | Description |
|---|---|
| O_RDONLY | Read only |
| O_WRONLY | Write only |
| O_RDWR | Read and write |
| O_CREAT | Create if doesn't exist |
| O_TRUNC | Truncate to zero length |
| O_APPEND | Append to end |
1#include <stdio.h>2#include <fcntl.h> // For open() and flags3#include <unistd.h> // For close()45int main() {6 // Open existing file for reading7 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
write() - Write to File Descriptor
1#include <stdio.h>2#include <fcntl.h>3#include <unistd.h>4#include <string.h>56int main() {7 // Write to stdout using system call8 char *msg = "Hello from write()!\n";9 write(1, msg, strlen(msg)); // 1 = stdout10 11 // Copy file using read/write12 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 chunks24 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
⚠️ 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
Creates an exact copy of the current process!
getpid() / getppid() - Get Process IDs
pid_t getppid(void);
1#include <stdio.h>2#include <unistd.h>3#include <sys/wait.h>45int main() {6 printf("Parent PID: %d\n", getpid());7 8 pid_t pid = fork(); // Create child process9 10 if (pid == -1) {11 perror("fork failed");12 return 1;13 }14 else if (pid == 0) {15 // Child process16 printf("Child: My PID = %d, Parent PID = %d\n", 17 getpid(), getppid());18 }19 else {20 // Parent process21 printf("Parent: Created child with PID = %d\n", pid);22 wait(NULL); // Wait for child to finish23 printf("Parent: Child finished\n");24 }25 26 return 0;27}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.
| Function | Args | PATH | Env |
|---|---|---|---|
| execl() | list | No | No |
| execlp() | list | Yes | No |
| execv() | array | No | No |
| execvp() | array | Yes | No |
1#include <stdio.h>2#include <unistd.h>3#include <sys/wait.h>45int 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 PATH12 13 // Only reached if exec fails14 perror("exec failed");15 return 1;16 }17 else {18 // Parent waits19 wait(NULL);20 printf("Command completed\n");21 }22 23 return 0;24}08Directory Operations
| Function | Prototype | Description |
|---|---|---|
| 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 |
1#include <stdio.h>2#include <unistd.h>3#include <sys/stat.h>45int main() {6 char cwd[256];7 8 // Get current directory9 if (getcwd(cwd, sizeof(cwd)) != NULL) {10 printf("Current dir: %s\n", cwd);11 }12 13 // Create a new directory14 if (mkdir("mydir", 0755) == 0) {15 printf("Created mydir/\n");16 }17 18 // Change to new directory19 if (chdir("mydir") == 0) {20 getcwd(cwd, sizeof(cwd));21 printf("Now in: %s\n", cwd);22 }23 24 // Go back and remove25 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.
1#include <stdio.h>2#include <fcntl.h>3#include <unistd.h>4#include <errno.h>5#include <string.h>67int 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 error18 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}Error opening file: No such file or directory
Error: No such file or directory
File not found!
10Library Functions vs System Calls
| Feature | Library (stdio.h) | System Call (unistd.h) |
|---|---|---|
| Handle Type | FILE * | int (fd) |
| Buffering | Yes (buffered) | No (unbuffered) |
| Portability | All platforms | UNIX/Linux only |
| Open | fopen() | open() |
| Read | fread(), fgets() | read() |
| Write | fwrite(), fputs() | write() |
| Close | fclose() | 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: