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 模拟器中多练习,熟悉各种函数的用法和错误处理方式。在实际项目中,合理使用文件系统可以帮助您构建功能丰富的嵌入式应用。
参考资料:
如果你觉得这篇文章对你有帮助,欢迎点赞、收藏和评论!如果有任何问题或建议,也欢迎在评论区留言讨论。