Class 26 of 37 • System Programming (C) \t\t\t\t

Low-Level File I/O in Linux (C Programming)

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.

Linux File Descriptors, Standard Streams 0 1 2 and Redirection

Figure 5: Linux standard file descriptors (stdin, stdout, stderr) and pipeline flow.

Core Low-Level System Calls

     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.


Complete C Program: Building a Custom "my_cp" (Copy File) Command

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;
}
							

Using lseek() for Random File Access

By 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);
							

Frequently Asked Questions (FAQ)

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.

Next Step in Your Linux Learning Path:

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

Share Share on Facebook Share on Twitter Share on LinkedIn Pin on Pinterest Share on Stumbleupon Share on Tumblr Share on Reddit Share on Diggit

You may also like this!