NuttX下C语言文件及目录操作完全指南

NuttX下C语言文件及目录操作完全指南:从零到精通

引言

在 NuttX 嵌入式操作系统开发中,文件和目录操作是最基础也是最重要的技能之一。NuttX 遵循 POSIX 标准,提供了一套与 Linux 兼容的文件操作 API,使得开发者可以使用熟悉的 C 语言函数进行文件管理。

本文将详细介绍 NuttX 下使用 C 语言进行文件及目录操作的所有函数和方法,包括文件的创建、读写、删除,目录的创建、遍历,文件信息获取等,并提供完整的代码示例和详细说明。


一、文件操作基础

1.1 文件描述符

在 NuttX 中,文件操作通过文件描述符(File Descriptor)进行。文件描述符是一个非负整数,用于标识一个打开的文件。
#mermaid-svg-YA4MPt9dz3M76fvV{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-YA4MPt9dz3M76fvV .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-YA4MPt9dz3M76fvV .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-YA4MPt9dz3M76fvV .error-icon{fill:#552222;}#mermaid-svg-YA4MPt9dz3M76fvV .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-YA4MPt9dz3M76fvV .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-YA4MPt9dz3M76fvV .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-YA4MPt9dz3M76fvV .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-YA4MPt9dz3M76fvV .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-YA4MPt9dz3M76fvV .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-YA4MPt9dz3M76fvV .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-YA4MPt9dz3M76fvV .marker{fill:#333333;stroke:#333333;}#mermaid-svg-YA4MPt9dz3M76fvV .marker.cross{stroke:#333333;}#mermaid-svg-YA4MPt9dz3M76fvV svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-YA4MPt9dz3M76fvV p{margin:0;}#mermaid-svg-YA4MPt9dz3M76fvV .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-YA4MPt9dz3M76fvV .cluster-label text{fill:#333;}#mermaid-svg-YA4MPt9dz3M76fvV .cluster-label span{color:#333;}#mermaid-svg-YA4MPt9dz3M76fvV .cluster-label span p{background-color:transparent;}#mermaid-svg-YA4MPt9dz3M76fvV .label text,#mermaid-svg-YA4MPt9dz3M76fvV span{fill:#333;color:#333;}#mermaid-svg-YA4MPt9dz3M76fvV .node rect,#mermaid-svg-YA4MPt9dz3M76fvV .node circle,#mermaid-svg-YA4MPt9dz3M76fvV .node ellipse,#mermaid-svg-YA4MPt9dz3M76fvV .node polygon,#mermaid-svg-YA4MPt9dz3M76fvV .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-YA4MPt9dz3M76fvV .rough-node .label text,#mermaid-svg-YA4MPt9dz3M76fvV .node .label text,#mermaid-svg-YA4MPt9dz3M76fvV .image-shape .label,#mermaid-svg-YA4MPt9dz3M76fvV .icon-shape .label{text-anchor:middle;}#mermaid-svg-YA4MPt9dz3M76fvV .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-YA4MPt9dz3M76fvV .rough-node .label,#mermaid-svg-YA4MPt9dz3M76fvV .node .label,#mermaid-svg-YA4MPt9dz3M76fvV .image-shape .label,#mermaid-svg-YA4MPt9dz3M76fvV .icon-shape .label{text-align:center;}#mermaid-svg-YA4MPt9dz3M76fvV .node.clickable{cursor:pointer;}#mermaid-svg-YA4MPt9dz3M76fvV .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-YA4MPt9dz3M76fvV .arrowheadPath{fill:#333333;}#mermaid-svg-YA4MPt9dz3M76fvV .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-YA4MPt9dz3M76fvV .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-YA4MPt9dz3M76fvV .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-YA4MPt9dz3M76fvV .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-YA4MPt9dz3M76fvV .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-YA4MPt9dz3M76fvV .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-YA4MPt9dz3M76fvV .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-YA4MPt9dz3M76fvV .cluster text{fill:#333;}#mermaid-svg-YA4MPt9dz3M76fvV .cluster span{color:#333;}#mermaid-svg-YA4MPt9dz3M76fvV div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-YA4MPt9dz3M76fvV .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-YA4MPt9dz3M76fvV rect.text{fill:none;stroke-width:0;}#mermaid-svg-YA4MPt9dz3M76fvV .icon-shape,#mermaid-svg-YA4MPt9dz3M76fvV .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-YA4MPt9dz3M76fvV .icon-shape p,#mermaid-svg-YA4MPt9dz3M76fvV .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-YA4MPt9dz3M76fvV .icon-shape .label rect,#mermaid-svg-YA4MPt9dz3M76fvV .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-YA4MPt9dz3M76fvV .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-YA4MPt9dz3M76fvV .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-YA4MPt9dz3M76fvV :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 应用程序
文件描述符表
fd=0: 标准输入
fd=1: 标准输出
fd=2: 标准错误
fd=3: 用户文件1
fd=4: 用户文件2

图 1: 文件描述符示意图

1.2 头文件包含

进行文件操作需要包含以下头文件:

c 复制代码
#include <stdio.h>      /* 标准I/O函数 */
#include <stdlib.h>     /* 标准库函数 */
#include <string.h>     /* 字符串操作 */
#include <unistd.h>     /* POSIX系统调用 */
#include <fcntl.h>      /* 文件控制选项 */
#include <sys/stat.h>   /* 文件状态 */
#include <sys/types.h>  /* 类型定义 */
#include <dirent.h>     /* 目录操作 */
#include <errno.h>      /* 错误处理 */

二、文件打开与关闭

2.1 open() 函数

功能:打开或创建文件,返回文件描述符

原型

c 复制代码
int open(const char *pathname, int flags, mode_t mode);

参数说明

参数 说明
pathname 文件路径
flags 打开标志(见下表)
mode 文件权限(仅创建时有效)

常用打开标志

标志 说明
O_RDONLY 只读打开
O_WRONLY 只写打开
O_RDWR 读写打开
O_CREAT 如果文件不存在则创建
O_TRUNC 如果文件存在则截断为0
O_APPEND 追加模式写入
O_EXCL 与 O_CREAT 一起使用,如果文件存在则报错

文件权限

权限值 说明
0644 所有者读写,其他只读
0666 所有人读写
0755 所有者全部权限,其他读执行

示例

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

int main(void)
{
    int fd;
    
    // 创建并打开文件,读写模式
    fd = open("/tmp/test.txt", O_RDWR | O_CREAT | O_TRUNC, 0644);
    if (fd < 0) {
        printf("open failed: %s\n", strerror(errno));
        return -1;
    }
    printf("File opened successfully, fd = %d\n", fd);
    
    // 关闭文件
    close(fd);
    return 0;
}

2.2 close() 函数

功能:关闭文件描述符

原型

c 复制代码
int close(int fd);

示例

c 复制代码
int fd = open("/tmp/test.txt", O_RDONLY);
if (fd < 0) {
    printf("open failed\n");
    return -1;
}

// 使用文件...

if (close(fd) < 0) {
    printf("close failed: %s\n", strerror(errno));
    return -1;
}

三、文件读写操作

3.1 read() 函数

功能:从文件读取数据

原型

c 复制代码
ssize_t read(int fd, void *buf, size_t count);

参数说明

参数 说明
fd 文件描述符
buf 数据缓冲区
count 要读取的字节数

返回值:实际读取的字节数,0 表示文件结束,-1 表示错误

示例

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

#define BUFFER_SIZE 1024

int main(void)
{
    int fd;
    char buffer[BUFFER_SIZE];
    ssize_t nbytes;
    
    fd = open("/tmp/test.txt", O_RDONLY);
    if (fd < 0) {
        printf("open failed: %s\n", strerror(errno));
        return -1;
    }
    
    // 读取文件内容
    nbytes = read(fd, buffer, BUFFER_SIZE - 1);
    if (nbytes < 0) {
        printf("read failed: %s\n", strerror(errno));
        close(fd);
        return -1;
    }
    
    // 添加字符串结束符
    buffer[nbytes] = '\0';
    printf("Read %zd bytes:\n%s\n", nbytes, buffer);
    
    close(fd);
    return 0;
}

3.2 write() 函数

功能:向文件写入数据

原型

c 复制代码
ssize_t write(int fd, const void *buf, size_t count);

示例

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

int main(void)
{
    int fd;
    const char *data = "Hello, NuttX File System!\n";
    ssize_t nbytes;
    
    fd = open("/tmp/test.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (fd < 0) {
        printf("open failed: %s\n", strerror(errno));
        return -1;
    }
    
    // 写入数据
    nbytes = write(fd, data, strlen(data));
    if (nbytes < 0) {
        printf("write failed: %s\n", strerror(errno));
        close(fd);
        return -1;
    }
    
    printf("Written %zd bytes\n", nbytes);
    
    close(fd);
    return 0;
}

3.3 lseek() 函数

功能:设置文件偏移量(文件指针位置)

原型

c 复制代码
off_t lseek(int fd, off_t offset, int whence);

参数说明

参数 说明
fd 文件描述符
offset 偏移量
whence 起始位置(见下表)

whence 取值

说明
SEEK_SET 从文件开头开始
SEEK_CUR 从当前位置开始
SEEK_END 从文件末尾开始

示例

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

int main(void)
{
    int fd;
    char buffer[32];
    off_t pos;
    
    fd = open("/tmp/test.txt", O_RDWR);
    if (fd < 0) {
        printf("open failed: %s\n", strerror(errno));
        return -1;
    }
    
    // 移动到文件开头
    pos = lseek(fd, 0, SEEK_SET);
    printf("Current position: %lld\n", (long long)pos);
    
    // 读取前5个字符
    read(fd, buffer, 5);
    buffer[5] = '\0';
    printf("Read: %s\n", buffer);
    
    // 移动到文件末尾
    pos = lseek(fd, 0, SEEK_END);
    printf("File size: %lld bytes\n", (long long)pos);
    
    // 从末尾向前移动10个字节
    pos = lseek(fd, -10, SEEK_END);
    printf("Position from end: %lld\n", (long long)pos);
    
    // 读取最后10个字符
    read(fd, buffer, 10);
    buffer[10] = '\0';
    printf("Last 10 chars: %s\n", buffer);
    
    close(fd);
    return 0;
}

四、文件信息获取

4.1 stat()fstat() 函数

功能:获取文件状态信息

原型

c 复制代码
int stat(const char *pathname, struct stat *buf);
int fstat(int fd, struct stat *buf);

struct stat 结构体

成员 说明
st_mode 文件类型和权限
st_size 文件大小(字节)
st_mtime 修改时间
st_atime 访问时间
st_ctime 创建时间
st_uid 文件所有者ID
st_gid 文件组ID

文件类型判断宏

说明
S_ISREG(m) 是否为普通文件
S_ISDIR(m) 是否为目录
S_ISCHR(m) 是否为字符设备
S_ISBLK(m) 是否为块设备
S_ISFIFO(m) 是否为管道
S_ISLNK(m) 是否为符号链接

示例

c 复制代码
#include <stdio.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#include <time.h>

void print_file_info(const char *path)
{
    struct stat st;
    
    if (stat(path, &st) < 0) {
        printf("stat failed: %s\n", strerror(errno));
        return;
    }
    
    printf("File: %s\n", path);
    printf("Size: %lld bytes\n", (long long)st.st_size);
    printf("Owner UID: %d\n", st.st_uid);
    printf("Group GID: %d\n", st.st_gid);
    
    // 判断文件类型
    if (S_ISREG(st.st_mode)) {
        printf("Type: Regular file\n");
    } else if (S_ISDIR(st.st_mode)) {
        printf("Type: Directory\n");
    } else if (S_ISCHR(st.st_mode)) {
        printf("Type: Character device\n");
    } else if (S_ISBLK(st.st_mode)) {
        printf("Type: Block device\n");
    } else {
        printf("Type: Other\n");
    }
    
    // 显示权限
    printf("Permissions: ");
    printf("%c", (st.st_mode & S_IRUSR) ? 'r' : '-');
    printf("%c", (st.st_mode & S_IWUSR) ? 'w' : '-');
    printf("%c", (st.st_mode & S_IXUSR) ? 'x' : '-');
    printf("%c", (st.st_mode & S_IRGRP) ? 'r' : '-');
    printf("%c", (st.st_mode & S_IWGRP) ? 'w' : '-');
    printf("%c", (st.st_mode & S_IXGRP) ? 'x' : '-');
    printf("%c", (st.st_mode & S_IROTH) ? 'r' : '-');
    printf("%c", (st.st_mode & S_IWOTH) ? 'w' : '-');
    printf("%c\n", (st.st_mode & S_IXOTH) ? 'x' : '-');
    
    // 显示时间
    printf("Modify time: %s", ctime(&st.st_mtime));
}

int main(void)
{
    print_file_info("/tmp/test.txt");
    print_file_info("/tmp");
    return 0;
}

4.2 access() 函数

功能:检查文件是否存在及权限

原型

c 复制代码
int access(const char *pathname, int mode);

mode 参数

说明
F_OK 检查文件是否存在
R_OK 检查是否有读权限
W_OK 检查是否有写权限
X_OK 检查是否有执行权限

示例

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

int main(void)
{
    const char *path = "/tmp/test.txt";
    
    if (access(path, F_OK) == 0) {
        printf("File exists\n");
        
        if (access(path, R_OK) == 0) {
            printf("Read permission: Yes\n");
        } else {
            printf("Read permission: No\n");
        }
        
        if (access(path, W_OK) == 0) {
            printf("Write permission: Yes\n");
        } else {
            printf("Write permission: No\n");
        }
    } else {
        printf("File does not exist\n");
    }
    
    return 0;
}

五、目录操作

5.1 mkdir() 函数

功能:创建目录

原型

c 复制代码
int mkdir(const char *pathname, mode_t mode);

示例

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

int main(void)
{
    const char *dir = "/tmp/mydir";
    
    if (mkdir(dir, 0755) == 0) {
        printf("Directory '%s' created successfully\n", dir);
    } else {
        printf("mkdir failed: %s\n", strerror(errno));
        return -1;
    }
    
    return 0;
}

5.2 rmdir() 函数

功能:删除空目录

原型

c 复制代码
int rmdir(const char *pathname);

示例

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

int main(void)
{
    const char *dir = "/tmp/mydir";
    
    if (rmdir(dir) == 0) {
        printf("Directory '%s' removed successfully\n", dir);
    } else {
        printf("rmdir failed: %s\n", strerror(errno));
        return -1;
    }
    
    return 0;
}

5.3 opendir()readdir() 函数

功能:打开目录并读取目录内容

原型

c 复制代码
DIR *opendir(const char *name);
struct dirent *readdir(DIR *dirp);
int closedir(DIR *dirp);

struct dirent 结构体

成员 说明
d_name 文件名
d_type 文件类型
d_ino inode 号

示例

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

void list_directory(const char *path)
{
    DIR *dir;
    struct dirent *entry;
    
    dir = opendir(path);
    if (dir == NULL) {
        printf("opendir failed: %s\n", strerror(errno));
        return;
    }
    
    printf("Contents of '%s':\n", path);
    printf("-------------------\n");
    
    while ((entry = readdir(dir)) != NULL) {
        // 跳过 . 和 ..
        if (strcmp(entry->d_name, ".") == 0 || 
            strcmp(entry->d_name, "..") == 0) {
            continue;
        }
        
        // 判断文件类型
        printf("%s", entry->d_name);
        if (entry->d_type == DT_DIR) {
            printf("/");
        }
        printf("\n");
    }
    
    closedir(dir);
}

int main(void)
{
    list_directory("/tmp");
    return 0;
}

5.4 chdir() 函数

功能:改变当前工作目录

原型

c 复制代码
int chdir(const char *path);

示例

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

int main(void)
{
    char cwd[256];
    
    // 获取当前目录
    getcwd(cwd, sizeof(cwd));
    printf("Current directory: %s\n", cwd);
    
    // 切换目录
    if (chdir("/tmp") == 0) {
        getcwd(cwd, sizeof(cwd));
        printf("Changed to: %s\n", cwd);
    } else {
        printf("chdir failed: %s\n", strerror(errno));
        return -1;
    }
    
    return 0;
}

六、文件管理操作

6.1 rename() 函数

功能:重命名文件或目录

原型

c 复制代码
int rename(const char *oldpath, const char *newpath);

示例

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

int main(void)
{
    const char *oldname = "/tmp/test.txt";
    const char *newname = "/tmp/new_test.txt";
    
    if (rename(oldname, newname) == 0) {
        printf("File renamed from '%s' to '%s'\n", oldname, newname);
    } else {
        printf("rename failed: %s\n", strerror(errno));
        return -1;
    }
    
    return 0;
}

6.2 unlink() 函数

功能:删除文件

原型

c 复制代码
int unlink(const char *pathname);

示例

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

int main(void)
{
    const char *path = "/tmp/test.txt";
    
    if (unlink(path) == 0) {
        printf("File '%s' deleted successfully\n", path);
    } else {
        printf("unlink failed: %s\n", strerror(errno));
        return -1;
    }
    
    return 0;
}

6.3 chmod() 函数

功能:修改文件权限

原型

c 复制代码
int chmod(const char *pathname, mode_t mode);

示例

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

int main(void)
{
    const char *path = "/tmp/test.txt";
    
    // 修改为只读(所有者)
    if (chmod(path, 0400) == 0) {
        printf("File permissions changed to 0400\n");
    } else {
        printf("chmod failed: %s\n", strerror(errno));
        return -1;
    }
    
    // 修改为读写(所有者),读(其他)
    if (chmod(path, 0644) == 0) {
        printf("File permissions changed to 0644\n");
    } else {
        printf("chmod failed: %s\n", strerror(errno));
        return -1;
    }
    
    return 0;
}

6.4 chown() 函数

功能:修改文件所有者

原型

c 复制代码
int chown(const char *pathname, uid_t owner, gid_t group);

示例

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

int main(void)
{
    const char *path = "/tmp/test.txt";
    
    // 修改所有者为 root (uid=0)
    if (chown(path, 0, -1) == 0) {
        printf("File owner changed to root\n");
    } else {
        printf("chown failed: %s\n", strerror(errno));
        return -1;
    }
    
    return 0;
}

七、实战案例

7.1 案例一:文件复制

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

#define BUFFER_SIZE 4096

int copy_file(const char *src, const char *dst)
{
    int src_fd, dst_fd;
    char buffer[BUFFER_SIZE];
    ssize_t nbytes;
    
    // 打开源文件
    src_fd = open(src, O_RDONLY);
    if (src_fd < 0) {
        printf("Cannot open source file: %s\n", strerror(errno));
        return -1;
    }
    
    // 创建目标文件
    dst_fd = open(dst, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (dst_fd < 0) {
        printf("Cannot create destination file: %s\n", strerror(errno));
        close(src_fd);
        return -1;
    }
    
    // 循环读写
    while ((nbytes = read(src_fd, buffer, BUFFER_SIZE)) > 0) {
        if (write(dst_fd, buffer, nbytes) != nbytes) {
            printf("Write failed: %s\n", strerror(errno));
            close(src_fd);
            close(dst_fd);
            return -1;
        }
    }
    
    if (nbytes < 0) {
        printf("Read failed: %s\n", strerror(errno));
        close(src_fd);
        close(dst_fd);
        return -1;
    }
    
    printf("File copied successfully\n");
    
    close(src_fd);
    close(dst_fd);
    return 0;
}

int main(int argc, char *argv[])
{
    if (argc != 3) {
        printf("Usage: %s <source> <destination>\n", argv[0]);
        return -1;
    }
    
    return copy_file(argv[1], argv[2]);
}

7.2 案例二:目录遍历与统计

c 复制代码
#include <stdio.h>
#include <dirent.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>

typedef struct {
    int file_count;
    int dir_count;
    long long total_size;
} DirStats;

void traverse_directory(const char *path, DirStats *stats, int depth)
{
    DIR *dir;
    struct dirent *entry;
    struct stat st;
    char full_path[512];
    
    dir = opendir(path);
    if (dir == NULL) {
        printf("Cannot open directory: %s\n", strerror(errno));
        return;
    }
    
    while ((entry = readdir(dir)) != NULL) {
        // 跳过 . 和 ..
        if (strcmp(entry->d_name, ".") == 0 || 
            strcmp(entry->d_name, "..") == 0) {
            continue;
        }
        
        // 构建完整路径
        snprintf(full_path, sizeof(full_path), "%s/%s", path, entry->d_name);
        
        // 获取文件信息
        if (stat(full_path, &st) < 0) {
            printf("Cannot stat %s: %s\n", full_path, strerror(errno));
            continue;
        }
        
        // 缩进显示
        for (int i = 0; i < depth; i++) {
            printf("  ");
        }
        
        if (S_ISDIR(st.st_mode)) {
            printf("[DIR] %s\n", entry->d_name);
            stats->dir_count++;
            // 递归遍历子目录
            traverse_directory(full_path, stats, depth + 1);
        } else {
            printf("[FILE] %s (%lld bytes)\n", entry->d_name, (long long)st.st_size);
            stats->file_count++;
            stats->total_size += st.st_size;
        }
    }
    
    closedir(dir);
}

int main(int argc, char *argv[])
{
    DirStats stats = {0, 0, 0};
    const char *path = (argc > 1) ? argv[1] : "/tmp";
    
    printf("Directory traversal of '%s':\n", path);
    printf("================================\n");
    
    traverse_directory(path, &stats, 0);
    
    printf("================================\n");
    printf("Total directories: %d\n", stats.dir_count);
    printf("Total files: %d\n", stats.file_count);
    printf("Total size: %lld bytes\n", stats.total_size);
    
    return 0;
}

7.3 案例三:日志文件管理器

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

#define LOG_DIR "/tmp/logs"
#define MAX_LOG_SIZE (1024 * 1024)  // 1MB

int init_log_directory(void)
{
    struct stat st;
    
    if (stat(LOG_DIR, &st) != 0) {
        if (errno == ENOENT) {
            if (mkdir(LOG_DIR, 0755) != 0) {
                printf("Cannot create log directory: %s\n", strerror(errno));
                return -1;
            }
            printf("Log directory created\n");
        } else {
            printf("Cannot stat log directory: %s\n", strerror(errno));
            return -1;
        }
    } else if (!S_ISDIR(st.st_mode)) {
        printf("%s is not a directory\n", LOG_DIR);
        return -1;
    }
    
    return 0;
}

void log_message(const char *format, ...)
{
    char log_path[256];
    char timestamp[64];
    char message[512];
    int fd;
    struct stat st;
    va_list args;
    
    // 获取当前时间
    time_t now = time(NULL);
    strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", localtime(&now));
    
    // 构建日志路径
    snprintf(log_path, sizeof(log_path), "%s/app.log", LOG_DIR);
    
    // 检查文件大小
    if (stat(log_path, &st) == 0) {
        if (st.st_size >= MAX_LOG_SIZE) {
            // 备份旧日志
            char backup_path[256];
            snprintf(backup_path, sizeof(backup_path), "%s/app.log.bak", LOG_DIR);
            rename(log_path, backup_path);
            printf("Log file rotated\n");
        }
    }
    
    // 打开日志文件(追加模式)
    fd = open(log_path, O_WRONLY | O_CREAT | O_APPEND, 0644);
    if (fd < 0) {
        printf("Cannot open log file: %s\n", strerror(errno));
        return;
    }
    
    // 格式化消息
    va_start(args, format);
    vsnprintf(message, sizeof(message), format, args);
    va_end(args);
    
    // 写入日志(带时间戳)
    char log_line[1024];
    snprintf(log_line, sizeof(log_line), "[%s] %s\n", timestamp, message);
    write(fd, log_line, strlen(log_line));
    
    close(fd);
}

int main(void)
{
    if (init_log_directory() != 0) {
        return -1;
    }
    
    log_message("Application started");
    log_message("User logged in: %s", "admin");
    log_message("Processing data...");
    log_message("Operation completed successfully");
    log_message("Application stopped");
    
    printf("Log messages written\n");
    
    return 0;
}

八、文件操作流程图

8.1 文件操作流程

#mermaid-svg-l6WWFhLo0vC9BMCY{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-l6WWFhLo0vC9BMCY .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-l6WWFhLo0vC9BMCY .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-l6WWFhLo0vC9BMCY .error-icon{fill:#552222;}#mermaid-svg-l6WWFhLo0vC9BMCY .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-l6WWFhLo0vC9BMCY .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-l6WWFhLo0vC9BMCY .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-l6WWFhLo0vC9BMCY .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-l6WWFhLo0vC9BMCY .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-l6WWFhLo0vC9BMCY .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-l6WWFhLo0vC9BMCY .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-l6WWFhLo0vC9BMCY .marker{fill:#333333;stroke:#333333;}#mermaid-svg-l6WWFhLo0vC9BMCY .marker.cross{stroke:#333333;}#mermaid-svg-l6WWFhLo0vC9BMCY svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-l6WWFhLo0vC9BMCY p{margin:0;}#mermaid-svg-l6WWFhLo0vC9BMCY .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-l6WWFhLo0vC9BMCY .cluster-label text{fill:#333;}#mermaid-svg-l6WWFhLo0vC9BMCY .cluster-label span{color:#333;}#mermaid-svg-l6WWFhLo0vC9BMCY .cluster-label span p{background-color:transparent;}#mermaid-svg-l6WWFhLo0vC9BMCY .label text,#mermaid-svg-l6WWFhLo0vC9BMCY span{fill:#333;color:#333;}#mermaid-svg-l6WWFhLo0vC9BMCY .node rect,#mermaid-svg-l6WWFhLo0vC9BMCY .node circle,#mermaid-svg-l6WWFhLo0vC9BMCY .node ellipse,#mermaid-svg-l6WWFhLo0vC9BMCY .node polygon,#mermaid-svg-l6WWFhLo0vC9BMCY .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-l6WWFhLo0vC9BMCY .rough-node .label text,#mermaid-svg-l6WWFhLo0vC9BMCY .node .label text,#mermaid-svg-l6WWFhLo0vC9BMCY .image-shape .label,#mermaid-svg-l6WWFhLo0vC9BMCY .icon-shape .label{text-anchor:middle;}#mermaid-svg-l6WWFhLo0vC9BMCY .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-l6WWFhLo0vC9BMCY .rough-node .label,#mermaid-svg-l6WWFhLo0vC9BMCY .node .label,#mermaid-svg-l6WWFhLo0vC9BMCY .image-shape .label,#mermaid-svg-l6WWFhLo0vC9BMCY .icon-shape .label{text-align:center;}#mermaid-svg-l6WWFhLo0vC9BMCY .node.clickable{cursor:pointer;}#mermaid-svg-l6WWFhLo0vC9BMCY .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-l6WWFhLo0vC9BMCY .arrowheadPath{fill:#333333;}#mermaid-svg-l6WWFhLo0vC9BMCY .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-l6WWFhLo0vC9BMCY .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-l6WWFhLo0vC9BMCY .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-l6WWFhLo0vC9BMCY .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-l6WWFhLo0vC9BMCY .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-l6WWFhLo0vC9BMCY .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-l6WWFhLo0vC9BMCY .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-l6WWFhLo0vC9BMCY .cluster text{fill:#333;}#mermaid-svg-l6WWFhLo0vC9BMCY .cluster span{color:#333;}#mermaid-svg-l6WWFhLo0vC9BMCY div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-l6WWFhLo0vC9BMCY .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-l6WWFhLo0vC9BMCY rect.text{fill:none;stroke-width:0;}#mermaid-svg-l6WWFhLo0vC9BMCY .icon-shape,#mermaid-svg-l6WWFhLo0vC9BMCY .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-l6WWFhLo0vC9BMCY .icon-shape p,#mermaid-svg-l6WWFhLo0vC9BMCY .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-l6WWFhLo0vC9BMCY .icon-shape .label rect,#mermaid-svg-l6WWFhLo0vC9BMCY .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-l6WWFhLo0vC9BMCY .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-l6WWFhLo0vC9BMCY .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-l6WWFhLo0vC9BMCY :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} open


close
开始
打开文件
成功?
读写操作
错误处理
read/write
lseek
关闭文件
结束

图 2: 文件操作流程

8.2 目录遍历流程

#mermaid-svg-p2Bmd0e96zWrHbsu{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-p2Bmd0e96zWrHbsu .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-p2Bmd0e96zWrHbsu .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-p2Bmd0e96zWrHbsu .error-icon{fill:#552222;}#mermaid-svg-p2Bmd0e96zWrHbsu .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-p2Bmd0e96zWrHbsu .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-p2Bmd0e96zWrHbsu .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-p2Bmd0e96zWrHbsu .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-p2Bmd0e96zWrHbsu .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-p2Bmd0e96zWrHbsu .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-p2Bmd0e96zWrHbsu .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-p2Bmd0e96zWrHbsu .marker{fill:#333333;stroke:#333333;}#mermaid-svg-p2Bmd0e96zWrHbsu .marker.cross{stroke:#333333;}#mermaid-svg-p2Bmd0e96zWrHbsu svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-p2Bmd0e96zWrHbsu p{margin:0;}#mermaid-svg-p2Bmd0e96zWrHbsu .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-p2Bmd0e96zWrHbsu .cluster-label text{fill:#333;}#mermaid-svg-p2Bmd0e96zWrHbsu .cluster-label span{color:#333;}#mermaid-svg-p2Bmd0e96zWrHbsu .cluster-label span p{background-color:transparent;}#mermaid-svg-p2Bmd0e96zWrHbsu .label text,#mermaid-svg-p2Bmd0e96zWrHbsu span{fill:#333;color:#333;}#mermaid-svg-p2Bmd0e96zWrHbsu .node rect,#mermaid-svg-p2Bmd0e96zWrHbsu .node circle,#mermaid-svg-p2Bmd0e96zWrHbsu .node ellipse,#mermaid-svg-p2Bmd0e96zWrHbsu .node polygon,#mermaid-svg-p2Bmd0e96zWrHbsu .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-p2Bmd0e96zWrHbsu .rough-node .label text,#mermaid-svg-p2Bmd0e96zWrHbsu .node .label text,#mermaid-svg-p2Bmd0e96zWrHbsu .image-shape .label,#mermaid-svg-p2Bmd0e96zWrHbsu .icon-shape .label{text-anchor:middle;}#mermaid-svg-p2Bmd0e96zWrHbsu .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-p2Bmd0e96zWrHbsu .rough-node .label,#mermaid-svg-p2Bmd0e96zWrHbsu .node .label,#mermaid-svg-p2Bmd0e96zWrHbsu .image-shape .label,#mermaid-svg-p2Bmd0e96zWrHbsu .icon-shape .label{text-align:center;}#mermaid-svg-p2Bmd0e96zWrHbsu .node.clickable{cursor:pointer;}#mermaid-svg-p2Bmd0e96zWrHbsu .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-p2Bmd0e96zWrHbsu .arrowheadPath{fill:#333333;}#mermaid-svg-p2Bmd0e96zWrHbsu .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-p2Bmd0e96zWrHbsu .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-p2Bmd0e96zWrHbsu .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-p2Bmd0e96zWrHbsu .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-p2Bmd0e96zWrHbsu .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-p2Bmd0e96zWrHbsu .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-p2Bmd0e96zWrHbsu .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-p2Bmd0e96zWrHbsu .cluster text{fill:#333;}#mermaid-svg-p2Bmd0e96zWrHbsu .cluster span{color:#333;}#mermaid-svg-p2Bmd0e96zWrHbsu div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-p2Bmd0e96zWrHbsu .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-p2Bmd0e96zWrHbsu rect.text{fill:none;stroke-width:0;}#mermaid-svg-p2Bmd0e96zWrHbsu .icon-shape,#mermaid-svg-p2Bmd0e96zWrHbsu .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-p2Bmd0e96zWrHbsu .icon-shape p,#mermaid-svg-p2Bmd0e96zWrHbsu .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-p2Bmd0e96zWrHbsu .icon-shape .label rect,#mermaid-svg-p2Bmd0e96zWrHbsu .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-p2Bmd0e96zWrHbsu .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-p2Bmd0e96zWrHbsu .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-p2Bmd0e96zWrHbsu :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} opendir


readdir




closedir
开始
打开目录
成功?
读取条目
错误处理
条目存在?
处理条目
是目录?
递归遍历
处理文件
关闭目录
结束

图 3: 目录遍历流程


九、常见问题与解决方案

9.1 文件打开失败

问题open() 返回 -1

可能原因及解决方案

错误码 原因 解决方案
ENOENT 文件不存在 检查路径是否正确,或使用 O_CREAT 创建
EACCES 权限不足 检查文件/目录权限
EMFILE 打开文件数过多 检查 CONFIG_NFILE_DESCRIPTORS 配置
ENOSPC 磁盘空间不足 清理空间或使用 RAM 磁盘

调试示例

c 复制代码
int fd = open("/tmp/test.txt", O_RDWR);
if (fd < 0) {
    printf("open failed: errno=%d, %s\n", errno, strerror(errno));
    switch (errno) {
        case ENOENT:
            printf("File does not exist\n");
            break;
        case EACCES:
            printf("Permission denied\n");
            break;
        case EMFILE:
            printf("Too many open files\n");
            break;
        default:
            printf("Unknown error\n");
    }
    return -1;
}

9.2 写入数据不完整

问题write() 返回值小于预期

解决方案:使用循环写入

c 复制代码
ssize_t full_write(int fd, const void *buf, size_t count)
{
    const char *ptr = buf;
    size_t remaining = count;
    ssize_t nwritten;
    
    while (remaining > 0) {
        nwritten = write(fd, ptr, remaining);
        if (nwritten <= 0) {
            if (nwritten < 0 && errno == EINTR) {
                continue;  // 被中断,重试
            }
            return nwritten;  // 真正的错误
        }
        ptr += nwritten;
        remaining -= nwritten;
    }
    
    return count;
}

9.3 目录遍历失败

问题opendir() 返回 NULL

解决方案

c 复制代码
DIR *dir = opendir(path);
if (dir == NULL) {
    printf("opendir failed: %s\n", strerror(errno));
    if (errno == ENOENT) {
        printf("Directory does not exist\n");
    } else if (errno == ENOTDIR) {
        printf("Path is not a directory\n");
    } else if (errno == EACCES) {
        printf("Permission denied\n");
    }
    return -1;
}

9.4 文件系统未挂载

问题:所有文件操作都失败

解决方案

c 复制代码
// 在使用文件前检查挂载点
struct stat st;
if (stat("/mnt", &st) != 0) {
    printf("Mount point does not exist\n");
    return -1;
}

// 尝试挂载文件系统
if (mount("/dev/ram0", "/mnt", "tmpfs", 0, NULL) < 0) {
    printf("Mount failed: %s\n", strerror(errno));
    return -1;
}

十、关键配置选项

在 NuttX 中,文件系统相关的配置选项需要在 menuconfig 中设置:

bash 复制代码
make menuconfig

关键配置路径

复制代码
Application Configuration  --->
  NSH Library  --->
    [*] Enable NuttShell

RTOS Features  --->
  File System  --->
    [*] Enable file system
    [*] Enable mountpoint support

Build Setup  --->
  Build Configuration  --->
    (X) Flat build

常用配置选项

配置项 说明
CONFIG_NFILE_DESCRIPTORS 最大打开文件数
CONFIG_FS_TMPFS 启用 TMPFS 文件系统
CONFIG_FS_FAT 启用 FAT 文件系统
CONFIG_FS_ROMFS 启用 ROMFS 文件系统
CONFIG_DISABLE_MOUNTPOINT 是否禁用挂载点

结束语

NuttX 提供了一套完整的 POSIX 兼容文件操作 API,使得开发者可以使用标准的 C 语言函数进行文件和目录管理。本文详细介绍了文件打开关闭、读写操作、目录遍历、文件信息获取等核心功能,并提供了多个实用的代码示例。

掌握这些文件操作技能是 NuttX 开发的基础,建议在 SIM 模拟器中多练习,熟悉各种函数的用法和错误处理方式。在实际项目中,合理使用文件系统可以帮助您构建功能丰富的嵌入式应用。


参考资料


如果你觉得这篇文章对你有帮助,欢迎点赞、收藏和评论!如果有任何问题或建议,也欢迎在评论区留言讨论。