In the Unix and Linux philosophy, "Everything is a file". Disk files, directories, terminal screens, pipes, hardware devices, and network sockets are all accessed through standard File Descriptors using low-level POSIX system calls.
Figure 5: Linux standard file descriptors (stdin, stdout, stderr) and pipeline flow.
int open(const char *path, int flags, mode_t mode); : Opens existing file or creates a new one, returning an integer file descriptor.
ssize_t read(int fd, void *buf, size_t count); : Reads up to count bytes from file descriptor into buffer.
ssize_t write(int fd, const void *buf, size_t count); : Writes count bytes from buffer into file descriptor.
off_t lseek(int fd, off_t offset, int whence); : Repositions the read/write file offset pointer.
int close(int fd); : Closes the file descriptor and releases kernel resources.
This complete, compilable C program demonstrates opening an input file in read-only mode, creating a destination file with 0644 permissions, and buffering data using read() and write():
/*
* my_cp.c - Copies file1 to file2 using low-level Linux system calls
* Compile: gcc my_cp.c -o my_cp
* Run : ./my_cp source.txt destination.txt
*/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#define BUFFER_SIZE 4096
int main(int argc, char *argv[]) {
int src_fd, dest_fd;
ssize_t bytes_read, bytes_written;
char buffer[BUFFER_SIZE];
if (argc != 3) {
fprintf(stderr, "Usage: %s <source_file> <destination_file>\n", argv[0]);
return 1;
}
// Open source file in Read-Only mode
src_fd = open(argv[1], O_RDONLY);
if (src_fd < 0) {
perror("Error opening source file");
return 2;
}
// Open/Create destination file: Write-only, Create if missing, Truncate if exists
// Permissions: 0644 (Owner read/write, Group/Others read-only)
dest_fd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (dest_fd < 0) {
perror("Error opening/creating destination file");
close(src_fd);
return 3;
}
// Loop: Read from source, Write to destination
while ((bytes_read = read(src_fd, buffer, BUFFER_SIZE)) > 0) {
bytes_written = write(dest_fd, buffer, bytes_read);
if (bytes_written != bytes_read) {
perror("Write error occurred");
close(src_fd);
close(dest_fd);
return 4;
}
}
if (bytes_read < 0) {
perror("Read error occurred");
} else {
printf("SUCCESS: Copied '%s' to '%s' successfully.\n", argv[1], argv[2]);
}
// Close both file descriptors
close(src_fd);
close(dest_fd);
return 0;
}
lseek() for Random File AccessBy default, file reading and writing occurs sequentially. The lseek() system call allows seeking to any byte offset:
// Seek to beginning of file
lseek(fd, 0, SEEK_SET);
// Seek 10 bytes forward from current position
lseek(fd, 10, SEEK_CUR);
// Seek to the end of file (find file size)
off_t file_size = lseek(fd, 0, SEEK_END);
printf("Total file size: %ld bytes\n", file_size);
Whenever any Linux program starts, the kernel automatically provides three open file descriptors: 0 points to standard input (stdin, usually keyboard), 1 points to standard output (stdout, terminal display), and 2 points to standard error (stderr, terminal error messages).
Leaving descriptors open creates a file descriptor leak. Linux enforces a per-process open file limit (viewable with ulimit -n, typically 1024). Long-running servers that leak descriptors will eventually fail with the error EMFILE: Too many open files.
Connect processes using anonymous pipes, FIFOs, and shared memory in Class 27: IPC Pipes & Shared Memory.
Continue to Class 27: IPC Pipes & Shared Memory → Previous: ← Processes | Full Syllabus