DAY27 Linux File IO and Standard IO Explained: From Concepts to Practice

Linux File IO and Standard IO Explained: From Concepts to Practice

I. Core Concepts: File IO vs Standard IO

1.1 Basic Definitions

  • File IO (System Calls) : Low-level system functions provided by the operating system for users to manipulate files (e.g., open/read/write/close). It interacts directly with the kernel and is also called "low-level IO".
  • Standard IO (C Library Functions) : File operation functions encapsulated by the C standard library (e.g., fopen/fread/fwrite/fclose). It calls File IO at the underlying layer and features better cross-platform compatibility, also known as "high-level IO".

1.2 Underlying Relationship and Core Differences

C library functions are wrappers around system calls: Standard IO adds a user-mode buffer to improve efficiency, while File IO has no buffer (direct kernel-mode operations). The core differences between the two are summarized below:

Feature File IO (System Call) Standard IO (C Library)
Operation Interface open/read/write/close/lseek fopen/fread/fwrite/fclose/fseek
Identifier Type File descriptor (int; e.g., 0/1/2 for stdin/stdout/stderr respectively) FILE stream pointer (FILE*)
Buffering Mechanism No buffer (direct kernel operations) With user-mode buffer (reduces system call frequency)
Cross-platform Support OS-dependent (e.g., Linux-specific) Cross-platform (compliant with C standards)
Applicable Scenarios Device files, real-time requirements Regular files, general scenarios pursuing efficiency
Permission Control Specified directly when opening (e.g., 0666) Depends on fopen mode (e.g., "r+")

II. Practical File IO (System Call)

File IO is a low-level interface provided by the Linux kernel, featuring no buffering and powerful functions. It is suitable for scenarios with device operations or high real-time requirements. Core functions include open/read/write/lseek/close.

2.1 open: Open or Create a File

The open function is used to open or create a file, returning a unique file descriptor (fd). It returns -1 on failure.

c 复制代码
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>

int open(const char *pathname, int flags, int mode);
  • Parameter Description :
    • pathname: File path/name;
    • flags: Open mode (core: O_RDONLY (read-only)/O_WRONLY (write-only)/O_RDWR (read-write); extensions: O_CREAT (create if not exists)/O_TRUNC (truncate content)/O_APPEND (append mode));
    • mode: File permission when creating (e.g., 0666, meaning readable and writable for all users).

Practical Example (01open.c): Create and truncate the 1.txt file

c 复制代码
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <stdio.h>

int main(int argc, char **argv)
{
    // Open in write-only mode, create if not exists, truncate if exists, with 0666 permission
    int fd = open("1.txt",  O_WRONLY| O_CREAT|O_TRUNC,0666);
    if(-1 == fd) // Error handling: -1 indicates failure to get file descriptor
    {
        fprintf(stderr,"open error\n");
        return 1;
    }
    return 0;
}

2.2 read/write: Read and Write Files

File reading and writing in File IO are implemented via read (read) and write (write), which operate directly on byte streams without buffering.

(1) write: Write to a File
c 复制代码
ssize_t write(int fd, const void *buf, size_t count);
  • fd: Target file descriptor;
  • buf: Buffer containing data to be written;
  • count: Number of valid bytes to be written;
  • Return value: Number of bytes actually written on success, -1 on failure.

Practical Example (04write.c): Write "hello" to 1.txt

c 复制代码
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char **argv)
{
    int fd = open("1.txt",  O_WRONLY| O_CREAT|O_TRUNC,0666);
    if(-1 == fd)
    {
        fprintf(stderr,"open error\n");
        return 1;
    }
    char str[100]="hello";
    // Key point: Use strlen(str) instead of sizeof(str) to avoid writing null characters
    ssize_t ret = write(fd,str,strlen(str));
    printf("Wrote %ld bytes to the file",ret); // Output: Wrote 5 bytes to the file
    close(fd); // Must close the file to release the file descriptor
    return 0;
}
(2) read: Read from a File
c 复制代码
ssize_t read(int fd, void *buf, size_t count);
  • fd: Source file descriptor;
  • buf: Buffer to receive data;
  • count: Maximum length of the buffer;
  • Return value: >0 for number of bytes actually read, ==0 for end of file, <0 for read failure.

Practical Example (05read.c): Read content from /etc/passwd

c 复制代码
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main()
{
    int fd = open("/etc/passwd", O_RDONLY); // Open system file in read-only mode
    if (-1 == fd)
    {
        fprintf(stderr, "open error\n");
        return 1;
    }
    char buf[50] = {0};
    while (1) {
        bzero(buf, sizeof(buf)); // Clear the buffer
        int ret = read(fd, buf, sizeof(buf)-1); // Reserve 1 byte for '\0'
        if (ret <= 0) {
            break; // Exit loop when reading is complete or failed
        }
        printf("{%d}:{%s}\n",ret,buf); // Print read length and content
    }
    close(fd);
    return 0;
}

2.3 Practical Case: File Copy (06readcp.c)

Implement a simplified cp command using read/write, with the core logic of "loop reading from source file → writing to target file":

c 复制代码
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>

int main(int argc ,char **argv)
{
    // Check parameters: source and target files must be provided
    if (argc < 3)
    {
        printf("usage:./a.out srcfile dstfile\n");
        return 1;
    }
    // Open source file (read-only) and target file (write-only/create/truncate)
    int fd_src = open(argv[1], O_RDONLY);
    int fd_dst = open(argv[2], O_WRONLY|O_CREAT|O_TRUNC,0666);

    if (-1 == fd_dst || -1 ==fd_src)
    {
        fprintf(stderr, "open error\n");
        return 1;
    }
    // Loop read/write: 1024-byte buffer improves efficiency
    while (1) {
        char buffer[1024] = {0};
        int ret = read(fd_src, buffer, sizeof(buffer));
        if (ret <= 0)
        {
            break;
        }
        write(fd_dst, buffer, ret); // Write according to actual read length
    }
    // Close file descriptors
    close(fd_dst);
    close(fd_src);
    return 0;
}

2.4 lseek: Adjust File Offset

lseek is used to move the file read/write pointer, enabling functions such as "getting file size" and "creating sparse files":

c 复制代码
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main()
{
    int fd = open("1.txt", O_RDWR);
    if (-1 == fd)
    {
        fprintf(stderr, "open error\n");
        return 1;
    }
    // Method 1: Get file size (offset to end, return offset value)
    long size = lseek(fd, 0, SEEK_END);
    printf("size %ld\n",size);

    // Method 2: Create sparse file (write at 1MB offset)
    lseek(fd, 1024*1024, SEEK_SET);
    char str[]="travel";
    write(fd,str,strlen(str));
    close(fd);
    return 0;
}

2.5 Standard Input/Output (07stdin.c)

In Linux, 0/1/2 correspond to standard input (stdin), standard output (stdout), and standard error (stderr) respectively, which can be directly operated via file descriptors:

c 复制代码
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

int	main(int argc, char **argv)
{
    char buf[10]={0};
    printf("pls input num:");
    fflush(stdout); // Flush buffer to ensure prompt is output first
    read(0,buf,sizeof(buf)); // Read input from stdin (0)
    int num =atoi(buf); // Convert to integer
    write(2,&num,4); // Write integer (binary) to stderr (2)
    return 0;
}

III. Practical Standard IO (C Library)

Standard IO is a wrapper of File IO by the C library, with built-in buffering and better cross-platform compatibility. Core functions include fopen/fread/fwrite/fseek/ftell.

3.1 Core Functions: File Read/Write and Offset

Standard IO uses FILE* pointers to identify files. Combined with fseek/ftell, it can easily implement "file content insertion" (02insert.c):

c 复制代码
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int	main(int argc, char **argv)
{
    FILE*fp = fopen("1.txt","r+"); // Open in read-write mode
    if(NULL == fp)
    {
        fprintf(stderr,"fopen error\n");
        return 1;
    }

    // 1. Get file size: offset to end and get offset value
    fseek(fp,0,SEEK_END);
    long size = ftell(fp);
    printf("size is %ld\n",size);

    // 2. Prepare insertion: save content after the specified position
    int pos = 15; // Insertion position
    char insert_str[100]="hello";
    fseek(fp,pos,SEEK_SET); // Offset to insertion position
    char * data =(char*)malloc(size); // Allocate memory to save the latter part
    fread(data,size-pos,1,fp); // Read the latter part of the content

    // 3. Insert content: overwrite with new content + original latter part
    fseek(fp,pos,SEEK_SET);
    fwrite(insert_str, strlen(insert_str), 1, fp); // Write inserted content
    fwrite(data, size-pos, 1, fp); // Write original latter part

    // 4. Release resources
    fclose(fp);
    free(data);
    return 0;
}

3.2 Practical Case: Dictionary Query (03dict.c)

Implement a simple dictionary query function by reading files with Standard IO's fgets and storing data in a linked list:

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "list.h"

// Dictionary data structure: word + meaning + linked list node
typedef struct
{
    char word[50];
    char mean[512];
    struct list_head node;
} DATATYPE;

// Add word to linked list
int add_word(struct list_head* head, char* word, char* mean)
{
    DATATYPE* p = (DATATYPE*)malloc(sizeof(DATATYPE));
    if (NULL == p)
    {
        fprintf(stderr, "add malloc error\n");
        return -1;
    }
    strcpy(p->word, word);
    strcpy(p->mean, mean);
    list_add(&p->node, head); // Insert into linked list
    return 0;
}

// Find word
DATATYPE* find_word(struct list_head* head, char* want_word)
{
    DATATYPE* p = NULL;
    DATATYPE* n = NULL;
    // Safely traverse the linked list
    list_for_each_entry_safe(p, n, head, node)
    {
        if (0 == strcmp(want_word, p->word))
        {
            return p;
        }
    }
    return NULL;
}

int main(int argc, char** argv)
{
    // 1. Open dictionary file
    FILE* fp = fopen("/home/linux/dict.txt", "r");
    if (NULL == fp)
    {
        fprintf(stderr, "open error\n");
        return 1;
    }

    // 2. Initialize linked list and read dictionary file
    struct list_head dict_head;
    INIT_LIST_HEAD(&dict_head);
    while (1)
    {
        char str[1024] = {0};
        if (NULL == fgets(str, sizeof(str), fp)) // Read line by line
        {
            break;
        }
        // Split word and meaning
        char* word = strtok(str, " ");
        char* mean = strtok(NULL, "\r");
        add_word(&dict_head, word, mean); // Add to linked list
    }

    // 3. Interactive word query
    while (1)
    {
        char want_word[50] = {0};
        printf("pls input want_word:");
        fgets(want_word, sizeof(want_word), stdin); // Read user input
        want_word[strlen(want_word) - 1] = '\0'; // Remove newline character
        if(0 == strcmp(want_word,"#quit")) // Exit condition
        {
            break;
        }
        // Find and output result
        DATATYPE* tmp = find_word(&dict_head,want_word);
        if(NULL == tmp)
        {
            printf("cant find word:%s\n",want_word);
        }
        else  
        {
            printf("word:%s mean:%s\n",tmp->word,tmp->mean);
        }
    }
    return 0;
}

IV. Core Summary

4.1 Key Conclusions

  1. Underlying Relationship: Standard IO is a wrapper of File IO. Choose Standard IO for cross-platform needs, and File IO for device or real-time scenarios;
  2. Buffering Impact: Standard IO's buffer reduces the number of system calls (improving efficiency), while File IO has no buffer (higher real-time performance);
  3. Identifier Difference : File IO uses file descriptors (int), and Standard IO uses FILE stream pointers (FILE*);
  4. Error Handling: File IO returns -1 for failure, while Standard IO returns NULL (e.g., fopen). Return values must be strictly verified.

4.2 Selection Recommendations

  • For regular files (e.g., txt, configuration files): Prefer Standard IO (fopen/fread/fwrite);
  • For device files (e.g., serial ports, network cards): Prefer File IO (open/read/write);
  • For cross-platform requirements: Must use Standard IO;
  • For precise control of file offset/permission: Use File IO.

V. Common Issues and Precautions

  1. File Permissions : The mode parameter of open (e.g., 0666) is affected by umask (actual permission = mode & ~umask);
  2. Buffering Issues : Standard IO requires fflush to flush the buffer to avoid data not being written to disk;
  3. Memory Leaks : Manually release malloc memory, file descriptors, and stream pointers in Standard IO;
  4. Parameter Validation : The count parameter of read/write must be set appropriately (e.g., use sizeof(buf)-1 for read to avoid buffer overflow).

相关推荐
GeniuswongAir2 小时前
飞牛NAS死机排查
linux·运维·服务器
hhcgchpspk2 小时前
linux查找并杀死进程部分方法
linux·运维·服务器·网络·经验分享
董世昌412 小时前
JavaScript 变量声明终极指南:var/let/const 深度解析(2025 版)
java·服务器·前端
gaize12132 小时前
服务器选购指南
服务器
天上飞的粉红小猪2 小时前
线程概念&&控制
linux·开发语言·c++
中屹指纹浏览器2 小时前
基于机器学习的代理 IP 风险动态评估与指纹协同技术
服务器·网络·经验分享·笔记·媒体
云雾J视界2 小时前
当AI下沉到MCU:嵌入式开发者的“能力护城河”正在被重写
人工智能·单片机·嵌入式硬件·mcu·freertos·岗位技能
嘻哈baby2 小时前
WireGuard为何取代IPSec成为Linux内核首选:协议架构与性能实测
linux·arm开发·架构
starvapour2 小时前
配置ollama的显卡和模型保存路径(Ubuntu, systemd)
linux·ubuntu·ollama