Fundamentals 28 min read

Master Linux File I/O: Open, Read, Write, and Advanced Flags Explained

This tutorial walks through Linux file programming fundamentals, covering how to open, create, read, write, and manipulate file descriptors and cursor positions with system calls like open, creat, read, write, lseek, and close, and demonstrates practical examples such as implementing a cp command, editing configuration files, and using both low‑level and standard C library I/O functions.

Liangxu Linux
Liangxu Linux
Liangxu Linux
Master Linux File I/O: Open, Read, Write, and Advanced Flags Explained

File Programming Overview

In Linux, everything is represented as a file, including regular files, pipes, sockets, and devices. Programs manipulate files using low‑level system calls to create, open, read, write, seek, and close them.

Opening and Creating Files

The primary system calls are open and creat:

int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
int creat(const char *filename, mode_t mode);

Common flags: O_RDONLY – read‑only O_WRONLY – write‑only O_RDWR – read/write

Additional flags control creation and behavior: O_CREAT – create if the file does not exist (requires a mode argument). O_EXCL – fail if O_CREAT is used and the file already exists. O_APPEND – all writes are appended to the end of the file. O_TRUNC – truncate an existing file to zero length when opened for writing.

Reading and Writing

The unbuffered I/O functions are:

ssize_t write(int fd, const void *buf, size_t count);
ssize_t read(int fd, void *buf, size_t count);

Example – write a string to a file:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main(){
    int fd = open("./file", O_RDWR|O_CREAT, 0600);
    const char *buf = "test";
    write(fd, buf, strlen(buf));
    close(fd);
    return 0;
}

Reading uses the same prototype; it returns the number of bytes read or 0 at EOF.

File Positioning with lseek

lseek

moves the file offset:

off_t lseek(int fd, off_t offset, int whence);
whence

values: SEEK_SET – from the beginning of the file SEEK_CUR – from the current position SEEK_END – from the end of the file

Example – reset cursor to the start:

lseek(fd, 0, SEEK_SET);

File Descriptors

Each opened file is identified by a non‑negative integer file descriptor. Standard descriptors are 0 (stdin), 1 (stdout), and 2 (stderr). The kernel maintains internal structures that map a descriptor to the underlying in‑memory representation of the file.

Practical Exercises

1. Simple cp implementation

Use open, read, write, and lseek to copy a source file to a destination file, handling errors and using O_TRUNC to overwrite existing data.

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv){
    if(argc != 3){
        printf("usage: %s src dest
", argv[0]);
        exit(1);
    }
    int src = open(argv[1], O_RDONLY);
    if(src == -1){ perror("open src"); exit(1); }
    int size = lseek(src, 0, SEEK_END);
    lseek(src, 0, SEEK_SET);
    char *buf = malloc(size);
    read(src, buf, size);
    int dst = open(argv[2], O_WRONLY|O_CREAT|O_TRUNC, 0600);
    if(dst == -1){ perror("open dst"); exit(1); }
    write(dst, buf, size);
    close(src); close(dst);
    free(buf);
    return 0;
}

2. Modifying a configuration file

Read the whole file into a buffer, locate a key with strstr, replace the value, reset the cursor with lseek, and write the buffer back.

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv){
    if(argc != 2){ printf("usage: %s file
", argv[0]); exit(1); }
    int fd = open(argv[1], O_RDWR);
    int size = lseek(fd, 0, SEEK_END);
    lseek(fd, 0, SEEK_SET);
    char *buf = malloc(size+1);
    read(fd, buf, size);
    buf[size] = '\0';
    char *p = strstr(buf, "LENG=");
    if(p){ p += 5; *p = '6'; }
    lseek(fd, 0, SEEK_SET);
    write(fd, buf, strlen(buf));
    close(fd);
    free(buf);
    return 0;
}

3. Writing integers and structures

Write raw binary data directly with write and read it back with read. Example for a single integer:

#include <fcntl.h>
#include <unistd.h>
int main(){
    int fd = open("int.bin", O_WRONLY|O_CREAT|O_TRUNC, 0600);
    int value = 100;
    write(fd, &value, sizeof(value));
    close(fd);
    fd = open("int.bin", O_RDONLY);
    int read_back;
    read(fd, &read_back, sizeof(read_back));
    printf("read %d
", read_back);
    close(fd);
    return 0;
}

Structure example:

#include <fcntl.h>
#include <unistd.h>
struct Test{ int a; char c; };
int main(){
    int fd = open("struct.bin", O_WRONLY|O_CREAT|O_TRUNC, 0600);
    struct Test t = {100, 'a'};
    write(fd, &t, sizeof(t));
    close(fd);
    fd = open("struct.bin", O_RDONLY);
    struct Test r;
    read(fd, &r, sizeof(r));
    printf("a=%d c=%c
", r.a, r.c);
    close(fd);
    return 0;
}

4. Standard C library I/O

The buffered equivalents are fopen, fread, fwrite, fseek, fgetc, fputc, and feof. Mode strings include r, w, a, and their binary variants ( rb, wb, ab). Example:

#include <stdio.h>
#include <string.h>
int main(){
    FILE *fp = fopen("text.txt", "w+");
    const char *msg = "Hello";
    fwrite(msg, 1, strlen(msg), fp);
    fseek(fp, 0, SEEK_SET);
    char buf[64] = {0};
    fread(buf, 1, strlen(msg), fp);
    printf("read: %s
", buf);
    fclose(fp);
    return 0;
}

Key Takeaways

Low‑level system calls ( open, read, write, lseek, close) provide unbuffered I/O and work with file descriptors, which are essential for device and socket operations. The standard C library functions ( fopen, fread, fwrite, etc.) add buffering and are convenient for regular file manipulation. Choose the appropriate API based on performance needs, portability, and whether buffering is desired.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

file I/OC programmingsystem callsPOSIX
Liangxu Linux
Written by

Liangxu Linux

Liangxu, a self‑taught IT professional now working as a Linux development engineer at a Fortune 500 multinational, shares extensive Linux knowledge—fundamentals, applications, tools, plus Git, databases, Raspberry Pi, etc. (Reply “Linux” to receive essential resources.)

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.