当前位置: 首页 > news >正文

【嵌入式Linux应用开发基础】opendir函数、readdir函数和closedir函数(二)

目录

一、关键注意事项

1.1. 错误处理

1.2. 资源管理

1.3. 线程安全

1.4. 目录项类型判断

1.5. 递归遍历注意事项

二、常见问题

2.1. opendir函数常见问题

2.2. readdir函数常见问题

2.3. closedir函数常见问题

2.4. 通用注意事项

三、总结

四、参考文献


接https://blog.csdn.net/weixin_37800531/article/details/145622003继续学习。

一、关键注意事项

1.1. 错误处理

① opendir 错误处理:opendir 函数在打开目录时可能会失败,例如目录不存在、权限不足等情况。调用 opendir 后,必须检查其返回值是否为 NULL,如果为 NULL,需要根据 errno 的值进行相应的错误处理。

#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>

int main() {
    DIR *dir = opendir("/nonexistent_directory");
    if (dir == NULL) {
        switch (errno) {
            case ENOENT:
                fprintf(stderr, "Directory does not exist.\n");
                break;
            case EACCES:
                fprintf(stderr, "Permission denied.\n");
                break;
            default:
                perror("opendir");
        }
        return 1;
    }
    // 后续操作
    closedir(dir);
    return 0;
}

readdir 错误处理:readdir 函数在读取目录项时也可能失败,返回 NULL 可能是到达目录末尾,也可能是发生了错误。可以通过检查 errno 是否被设置来区分这两种情况。

#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>

int main() {
    DIR *dir = opendir("/tmp");
    if (dir == NULL) {
        perror("opendir");
        return 1;
    }

    struct dirent *entry;
    errno = 0;
    while ((entry = readdir(dir)) != NULL) {
        // 处理目录项
        printf("%s\n", entry->d_name);
    }

    if (errno != 0) {
        perror("readdir");
    }

    closedir(dir);
    return 0;
}

closedir 错误处理:closedir 函数在关闭目录流时也可能失败,需要检查其返回值。如果返回 -1,表示关闭失败,需要根据 errno 处理错误。

#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>

int main() {
    DIR *dir = opendir("/tmp");
    if (dir == NULL) {
        perror("opendir");
        return 1;
    }

    if (closedir(dir) == -1) {
        perror("closedir");
        return 1;
    }

    return 0;
}

1.2. 资源管理

①及时关闭目录流:使用 opendir 打开目录流后,在不再需要时必须使用 closedir 关闭它,以释放相关的系统资源,避免资源泄漏。特别是在程序中存在多个 opendir 调用时,要确保每个打开的目录流都被正确关闭。

②异常情况下的资源释放:在程序执行过程中,如果发生异常(如内存分配失败、其他系统调用出错等),要确保已经打开的目录流被关闭。可以使用 goto 语句或函数封装来实现异常处理时的资源释放。

#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>

int main() {
    DIR *dir = opendir("/tmp");
    if (dir == NULL) {
        perror("opendir");
        return 1;
    }

    // 模拟异常情况
    if (/* 异常条件 */) {
        fprintf(stderr, "An error occurred.\n");
        closedir(dir);
        return 1;
    }

    // 正常操作
    closedir(dir);
    return 0;
}

1.3. 线程安全

readdir 非线程安全:readdir 函数不是线程安全的,因为它使用了一个静态的内部数据结构来保存当前的读取位置。如果多个线程同时调用 readdir 操作同一个目录流,可能会导致数据竞争和不一致的结果。

②使用 readdir_r:为了在多线程环境中安全地读取目录项,可以使用 readdir_r 函数,它是 readdir 的可重入版本。readdir_r 需要用户提供一个缓冲区来保存目录项信息。

#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <pthread.h>

void *thread_function(void *arg) {
    DIR *dir = (DIR *)arg;
    struct dirent entry;
    struct dirent *result;
    while (readdir_r(dir, &entry, &result) == 0 && result != NULL) {
        printf("Thread: %s\n", entry.d_name);
    }
    return NULL;
}

int main() {
    DIR *dir = opendir("/tmp");
    if (dir == NULL) {
        perror("opendir");
        return 1;
    }

    pthread_t thread;
    if (pthread_create(&thread, NULL, thread_function, (void *)dir) != 0) {
        perror("pthread_create");
        closedir(dir);
        return 1;
    }

    pthread_join(thread, NULL);
    closedir(dir);
    return 0;
}

1.4. 目录项类型判断

d_type 的兼容性:struct dirent 结构体中的 d_type 成员用于表示目录项的类型,但并不是所有的文件系统都支持该成员。在使用 d_type 进行目录项类型判断时,要注意其兼容性。如果文件系统不支持 d_type,可以通过 stat 系统调用获取文件的详细信息来判断类型。

#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>

int main() {
    DIR *dir = opendir("/tmp");
    if (dir == NULL) {
        perror("opendir");
        return 1;
    }

    struct dirent *entry;
    while ((entry = readdir(dir)) != NULL) {
        if (entry->d_type == DT_DIR) {
            printf("%s is a directory.\n", entry->d_name);
        } else if (entry->d_type == DT_REG) {
            printf("%s is a regular file.\n", entry->d_name);
        } else {
            struct stat file_stat;
            char file_path[256];
            snprintf(file_path, sizeof(file_path), "/tmp/%s", entry->d_name);
            if (stat(file_path, &file_stat) == 0) {
                if (S_ISDIR(file_stat.st_mode)) {
                    printf("%s is a directory.\n", entry->d_name);
                } else if (S_ISREG(file_stat.st_mode)) {
                    printf("%s is a regular file.\n", entry->d_name);
                }
            }
        }
    }

    closedir(dir);
    return 0;
}

1.5. 递归遍历注意事项

①避免无限循环:在进行目录的递归遍历时,要注意避免无限循环,例如目录中存在符号链接指向自身或父目录的情况。可以使用一个集合来记录已经访问过的目录,避免重复访问。

②性能考虑:递归遍历目录可能会消耗大量的系统资源,特别是在目录结构复杂、文件数量众多的情况下。要考虑性能问题,可以采用迭代方式或限制遍历深度来优化。

二、常见问题

2.1. opendir函数常见问题

①目录不存在或路径错误

问题描述:调用 opendir 函数时返回 NULL,导致无法打开指定目录。这可能是由于多种原因造成的,如目录不存在、权限不足、路径错误等。

解决办法:检查目录是否存在,在调用 opendir 之前,可以使用 stat 函数检查目录是否存在。

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>

int main() {
    const char *dir_path = "/nonexistent_directory";
    struct stat st;
    if (stat(dir_path, &st) == -1) {
        perror("stat");
        return 1;
    }
    if (!S_ISDIR(st.st_mode)) {
        fprintf(stderr, "%s is not a directory.\n", dir_path);
        return 1;
    }

    DIR *dir = opendir(dir_path);
    if (dir == NULL) {
        perror("opendir");
        return 1;
    }

    closedir(dir);
    return 0;
}
  • 检查权限:确保当前用户对要打开的目录具有足够的权限。可以使用 chmod 命令修改目录的权限。
  • 检查路径:仔细检查目录路径是否正确,包括路径中的拼写错误、斜杠的使用等。

②权限不足:如果当前进程没有足够的权限来访问指定的目录,opendir函数也会返回NULL。开发者需要检查目录的权限设置,并确保当前进程具有适当的访问权限。

③内存不足:在极少数情况下,如果系统内存不足,opendir函数可能会因为无法分配必要的资源而失败。此时,开发者需要检查系统的内存使用情况,并确保有足够的可用内存。

2.2. readdir函数常见问题

readdir 返回 NULL 原因判断

问题描述:调用 readdir 函数时返回 NULL,但不清楚是到达目录末尾还是发生了错误。

解决办法:在调用 readdir 之前,将 errno 置为 0。当 readdir 返回 NULL 时,检查 errno 的值。如果 errno 为 0,则表示到达目录末尾;如果 errno 不为 0,则表示发生了错误。

#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>

int main() {
    DIR *dir = opendir("/tmp");
    if (dir == NULL) {
        perror("opendir");
        return 1;
    }

    struct dirent *entry;
    errno = 0;
    while ((entry = readdir(dir)) != NULL) {
        printf("%s\n", entry->d_name);
    }

    if (errno != 0) {
        perror("readdir");
    }

    closedir(dir);
    return 0;
}

②到达目录末尾:当readdir函数读取到目录的末尾时,它将返回NULL。开发者需要在循环中检查这个返回值,以便正确地处理目录读取的结束。

 ③内存覆盖问题:每次调用readdir函数时,都会返回一个指向静态分配的dirent结构体的指针。意味着每次调用都会覆盖上一次调用的结果。因此,需要在处理每个目录项之前复制或存储所需的信息。

④文件名长度限制dirent结构体中的d_name字段存储了文件名,但其长度是有限的(通常为255个字符)。如果文件名过长,可能会导致截断。需要处理这种情况,以确保文件名的完整性。

⑤文件类型判断错误dirent结构体中的d_type字段提供了文件类型的信息,但有时可能不准确。需要根据实际情况判断文件类型,并可能需要使用其他函数(如stat)来获取更准确的文件信息。

2.3. closedir函数常见问题

①传入无效的目录流指针:如果closedir函数传入了无效的目录流指针(例如,一个未成功打开的目录的指针),可能会失败并返回-1。需要确保在调用closedir之前,目录流指针是有效的。

②资源泄漏:如果忘记调用closedir函数来关闭目录流,可能会导致资源泄漏。需要在完成目录操作后,及时调用closedir函数来释放资源。

2.4. 通用注意事项

  • 错误处理:在使用这些函数时,开发者需要做好错误处理。例如,检查返回值是否为NULL或-1,并使用errno变量来获取具体的错误信息。

  • 线程安全:在多线程环境中使用这些函数时,需要注意线程安全性。例如,避免多个线程同时访问同一个目录流指针。

  • 文件系统限制:不同的文件系统可能有不同的限制和要求。需要了解目标文件系统的特性,并确保在使用这些函数时遵守这些限制和要求。

三、总结

综上所述,opendirreaddirclosedir函数是嵌入式Linux应用开发中进行目录操作的基础函数,掌握它们的用法对于开发高效、稳定的嵌入式Linux应用至关重要。


四、参考文献

  • 《UNIX 环境高级编程(第 3 版)》
    • 作者:W. Richard Stevens、Stephen A. Rago
    • 简介:一本经典的 Unix 和 Linux 编程领域的权威书籍。书中详细介绍了 Unix/Linux 系统编程的各个方面,包括文件和目录操作。对于 opendirreaddir 和 closedir 函数,不仅有函数原型、参数和返回值的说明,还结合实际例子讲解了如何在不同场景下使用这些函数进行目录遍历和管理,同时深入探讨了相关的系统调用和底层原理。
  • 《Linux 程序设计(第 4 版)》
    • 作者:Neil Matthew、Richard Stones
    • 简介:本书专注于 Linux 平台下的程序设计,涵盖了文件操作、进程管理、网络编程等多个方面。在文件和目录操作章节,对 opendirreaddir 和 closedir 函数进行了清晰的阐述,并给出了简单易懂的示例代码,帮助读者快速掌握这些函数的使用方法。此外,书中还介绍了一些与目录操作相关的实用技巧和最佳实践。
  • 《嵌入式 Linux 应用开发完全手册》
    • 作者:韦东山
    • 简介:这本书针对嵌入式 Linux 应用开发进行了全面的讲解,包含了从基础的 Linux 操作到高级的应用程序开发的内容。在文件系统操作部分,详细介绍了 opendirreaddir 和 closedir 函数在嵌入式系统中的应用,结合实际的嵌入式开发案例,让读者了解如何在资源受限的环境中高效地使用这些函数。
  • Linux 手册页(man pages)
    • 获取方式:在 Linux 系统中,通过命令行输入 man opendirman readdir 和 man closedir 即可查看这些函数的官方手册页。
    • 简介:手册页是最权威和详细的函数参考资料,包含了函数的原型、参数说明、返回值、使用示例、相关注意事项以及与其他函数的关联等信息。同时,手册页还会提及函数在不同 Linux 内核版本中的变化和兼容性问题。
  • GNU C Library 文档
    • 地址:https://www.gnu.org/software/libc/manual/
    • 简介:GNU C 库是 Linux 系统中广泛使用的 C 语言标准库,该文档对库中的各个函数进行了详细的说明。对于 opendirreaddir 和 closedir 函数,文档不仅有基本的使用介绍,还深入探讨了函数的实现细节和性能优化建议,适合有一定基础的开发者进一步学习。
  • Linux 内核源代码
    • 地址:kernel/git/stable/linux.git - Linux kernel stable tree
    • 简介:Linux 内核源代码中包含了大量与文件系统和目录操作相关的代码。通过查看内核源码,可以了解 opendirreaddir 和 closedir 函数在底层是如何实现的,以及它们与其他内核组件的交互方式。有助于深入理解这些函数的工作原理和性能特点。
  • BusyBox 项目
    • 地址:BusyBox
    • 简介:BusyBox 是一个集成了许多常用 Unix 工具的软件包,广泛应用于嵌入式系统中。在 BusyBox 的源代码中,有很多使用 opendirreaddir 和 closedir 函数的实际案例,通过研究这些代码,可以学习到如何在嵌入式环境中高效地使用这些函数进行文件和目录管理。
  • Stack Overflow
    • 地址:https://stackoverflow.com/
    • 简介:这是一个知名的技术问答社区,有大量关于 opendirreaddir 和 closedir 函数的问题和解答。
  • CSDN
    • 地址:https://www.csdn.net/
    • 简介:国内知名的技术社区,有许多开发者分享的关于嵌入式 Linux 开发和文件目录操作的技术文章和经验总结。

相关文章:

  • 机器学习 - 关于逻辑回归的若干问题
  • 零基础开发自己的微信小程序(工具箱之父)(二)
  • CPP集群聊天服务器开发实践(三):群组聊天业务
  • 请求超时处理
  • 软考教材重点内容 信息安全工程师 第16章 网络安全风险评枯技术原理与应用
  • 【愚公系列】《Python网络爬虫从入门到精通》009-使用match()进行匹配
  • 十四、GitLab 流水线自动化部署之 Windows Server
  • python轻量级框架-flask
  • 135,【2】 buuctf web bestphp‘s revenge
  • AI辅助编程工具详细介绍
  • SSH 登录到 Linux 服务器为什么没有要求输入密码
  • JVM的类加载器
  • 高效利用Python爬虫获取淘宝店铺详情:电商数据挖掘
  • Linux 设备驱动 -- I2C 子系统快速入门
  • 【教程】MySQL数据库学习笔记(七)——多表操作(持续更新)
  • DeepSeek从入门到精通(清华大学)
  • CRMEB 多商户版v3.0.1源码全开源+PC端+Uniapp前端+搭建教程
  • 不需要移植和配置xinetd 等相类似执行文件,tftp-hpa服务器交叉移植使用说明
  • 【流程图】在 .NET (WPF 或 WinForms) 中实现流程图中的连线算法
  • 青少年编程与数学 02-009 Django 5 Web 编程 07课题、数据迁移
  • 信俗与共:清代新疆回疆儒释道庙宇的中华政教
  • 浙江省台州市政协原副主席林虹被“双开”
  • 商务部:中方敦促美方尽快停止232关税措施
  • 丹麦外交大臣拉斯穆森将访华
  • 杞支雅男评《1517》|放眼世界,立足德国
  • 缅甸内观冥想的历史漂流:从“人民鸦片”到东方灵修