【嵌入式Linux应用开发基础】open函数与close函数

目录

一、open函数

1.1. 函数原型

1.2 参数说明

1.3 返回值

1.4. 示例代码

二、close函数

2.1. 函数原型

2.2. 示例代码

三、关键注意事项

3.1. 资源管理与泄漏防范

3.2. 错误处理的严谨性

3.3. 标志(flags)与权限(mode)的陷阱

3.4. 并发与原子操作

3.5. 信号中断(EINTR)处理

3.6. 嵌入式设备文件的特殊问题

3.7. 调试与工具

3.8. 最佳实践清单

四、典型应用场景

4.1. 设备驱动访问

4.2. 配置文件读写

4.3. 资源独占访问

4.4. 非易失性存储操作

4.5. 动态资源管理

4.6. 临时文件操作

五、常见问题

5.1. open函数常见问题

5.2. close函数常见问题

5.3. 解决方案与建议

六、总结


在嵌入式 Linux 应用开发中,open 函数和 close 函数是文件 I/O 操作里极为基础且关键的函数。借助这两个函数,程序能够打开文件、设备文件或者创建新文件,还能在操作完成后关闭相应的文件描述符。

一、open函数

open函数用于打开一个文件,并返回一个文件描述符,用于后续的文件操作。

1.1. 函数原型

#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>int open(const char *pathname, int flags, mode_t mode);

1.2 参数说明

  • pathname:这是一个字符串,代表要打开或者创建的文件的路径名,可以是绝对路径,也可以是相对路径。
  • flags:用于指定文件的打开方式,是一个整数类型的参数,可使用以下常见标志:
    • O_RDONLY:以只读模式打开文件。
    • O_WRONLY:以只写模式打开文件。
    • O_RDWR:以读写模式打开文件。
    • O_CREAT:若文件不存在,则创建该文件。使用此标志时,需要第三个参数 mode 来指定文件的权限。
    • O_TRUNC:若文件已经存在,并且以写模式打开,会将文件长度截断为 0。
    • O_APPEND:以追加模式打开文件,每次写入数据时都会追加到文件末尾。
  • mode:当使用 O_CREAT 标志时,此参数用于指定新创建文件的权限。权限以八进制数表示,例如 0644 表示文件所有者有读写权限,组用户和其他用户有读权限。

1.3 返回值

  • 若成功打开或创建文件,open 函数会返回一个非负整数的文件描述符,用于后续对该文件的操作。
  • 若失败,返回 -1,并且会设置 errno 来指示具体的错误类型。

1.4. 示例代码

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>int main() {int fd;// 以读写模式打开文件,如果文件不存在则创建,权限为 0644fd = open("test.txt", O_RDWR | O_CREAT, 0644);if (fd == -1) {perror("open");return 1;}printf("文件打开成功,文件描述符: %d\n", fd);// 后续可以使用 fd 进行读写操作// ...return 0;
}

 

二、close函数

close函数用于关闭一个打开的文件描述符。

2.1. 函数原型

#include <unistd.h>int close(int fd);

参数说明

  • fd:要关闭的文件描述符。

返回值

  • 成功时返回0。
  • 失败时返回-1,并设置errno以指示错误。

2.2. 示例代码

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>int main() {int fd;// 以读写模式打开文件,如果文件不存在则创建,权限为 0644fd = open("test.txt", O_RDWR | O_CREAT, 0644);if (fd == -1) {perror("open");return 1;}printf("文件打开成功,文件描述符: %d\n", fd);// 关闭文件if (close(fd) == -1) {perror("close");return 1;}printf("文件关闭成功\n");return 0;
}

 

三、关键注意事项

3.1. 资源管理与泄漏防范

① 严格配对open()close()

  • 嵌入式系统资源有限:文件描述符(fd)是内核管理的稀缺资源,默认上限通常较小(如1024)。未关闭的fd会导致耗尽问题,引发EMFILE错误。

  • 最佳实践

int fd = open(...);
if (fd == -1) { /* 错误处理 */ }
// ...操作文件...
if (close(fd) == -1) { /* 记录错误,但可能无法恢复 */ }
  • 使用RAII模式(如C++封装类,在析构时自动关闭)

  • 避免长生命周期持有fd:操作完成后立即关闭,而非延迟到程序退出。 

② 避免重复关闭

  • close()后的fd可能被复用:若重复关闭已关闭的fd,可能意外关闭其他合法资源。

  • 解决方案

if (fd != -1) {  // 确保fd有效后再关闭close(fd);fd = -1;     // 标记为无效,防止二次关闭
}

3.2. 错误处理的严谨性

① open()失败必须处理

  • 典型错误场景

    • ENOENT:路径不存在(如设备未加载驱动)

    • EACCES:权限不足(需检查用户/组权限或SELinux策略)

    • EBUSY:设备被占用(如另一个进程已打开)

int fd = open("/dev/i2c-0", O_RDWR);
if (fd == -1) {if (errno == EACCES) {// 提示用户需要root权限或调整udev规则} else if (errno == ENODEV) {// 检查内核是否加载了对应驱动}perror("open failed");exit(EXIT_FAILURE);
}

② close()失败不可忽视

  • 虽然罕见,但可能发生

    • EBADF:传入无效的fd(通常因编程错误)

    • EINTR:被信号中断(需重试关闭)

if (close(fd) == -1) {if (errno == EINTR) {// 重试关闭(极少数情况需循环处理)close(fd);}// 记录日志,但通常无法恢复
}

3.3. 标志(flags)与权限(mode)的陷阱

① O_CREAT必须指定mode

  • 未设置mode时权限随机:若省略mode参数,创建的文件权限由未初始化的栈数据决定。

  • 正确用法

// 创建用户可读写、组和其他只读的文件
int fd = open("log.txt", O_RDWR | O_CREAT, 0644);
    • 注意umask的影响:实际权限为mode & ~umask。若需精确控制,可在程序开始时调用umask(0)

② 设备文件的特殊标志

  • 串口设备需要O_NOCTTY:防止终端控制(防止成为控制终端): 

int fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NONBLOCK);
  • 块设备的O_SYNC:确保数据写入物理存储(但降低性能)。 

3.4. 并发与原子操作

① O_EXCL防竞态条件

  • 安全创建文件:结合O_CREAT | O_EXCL可确保文件由当前进程创建,避免多进程竞争。

int fd = open("lockfile", O_RDWR | O_CREAT | O_EXCL, 0644);
if (fd == -1 && errno == EEXIST) {// 文件已存在,其他进程正在运行
}

② O_APPEND的多进程写入

  • 追加写入的原子性:使用O_APPEND时,内核保证每次write()前自动定位到文件末尾,避免多进程覆盖。 

// 进程A和进程B同时写日志
int fd = open("app.log", O_WRONLY | O_APPEND);

3.5. 信号中断(EINTR)处理

系统调用可能被信号打断

  • open()close()可能返回EINTR:需判断错误类型并重试。

retry:
int fd = open("/dev/sensor", O_RDWR);
if (fd == -1) {if (errno == EINTR) {goto retry;  // 重试被信号中断的调用}// 处理其他错误
}

3.6. 嵌入式设备文件的特殊问题

① 权限与udev规则

  • 默认设备文件权限受限:如/dev/gpio通常只有root可访问。

    • 解决方案

      • 以root权限运行程序(不推荐)

      • 修改udev规则,赋予普通用户访问权限:

# /etc/udev/rules.d/99-gpio.rules
SUBSYSTEM=="gpio", MODE="0666"

② 设备初始化延迟

  • 驱动加载或设备未就绪:在open()前增加重试机制。

int retries = 5;
while (retries--) {int fd = open("/dev/camera", O_RDWR);if (fd != -1) break;sleep(1);  // 等待驱动初始化
}

3.7. 调试与工具

① 监控文件描述符

  • 查看进程打开的fd

ls -l /proc/<PID>/fd  # 嵌入式系统可能需busybox支持
  • 检测泄漏:通过lsofcat /proc/sys/fs/file-nr观察系统级fd使用情况。

② 使用valgrind检测泄漏

  • 动态分析工具(需交叉编译): 

valgrind --track-fds=yes ./embedded_app

3.8. 最佳实践清单

  • 始终检查返回值open()close()都可能失败。

  • 使用O_CLOEXEC标志:避免fork后子进程继承fd(防止意外操作):

int fd = open("file", O_RDWR | O_CLOEXEC);
  • 最小化fd持有时间:操作完成后立即关闭。

  • 多线程环境加锁:若共享fd,确保read()/write()原子性。

  • 文档化设备依赖:记录设备路径、所需flags和权限要求。

掌握这些细节能显著提升嵌入式Linux应用的健壮性,尤其在资源紧张和高可靠性的场景中。

四、典型应用场景

4.1. 设备驱动访问

嵌入式系统通过设备文件(如/dev/gpio/dev/i2c-1)与硬件交互,open()用于获取设备句柄,close()用于释放资源。

// 示例:打开GPIO设备
int fd = open("/dev/gpiochip0", O_RDWR);
if (fd < 0) {perror("Failed to open GPIO device");return -1;
}// 操作GPIO...
write(fd, &value, sizeof(value));close(fd); // 必须关闭以释放内核资源

设备文件可能需要root权限(O_RDWR)。 

4.2. 配置文件读写

嵌入式设备常通过配置文件(如/etc/config.cfg)存储参数,需用open()获取文件描述符进行读写。

// 读取配置文件
int fd = open("/etc/config.cfg", O_RDONLY);
char buffer[256];
read(fd, buffer, sizeof(buffer));
close(fd);// 写入配置
fd = open("/etc/config.cfg", O_WRONLY | O_TRUNC);
write(fd, new_config, strlen(new_config));
close(fd);

关键参数

  • O_TRUNC:清空文件内容后写入。

  • O_CREAT:文件不存在时创建(需指定权限,如0644)。 

4.3. 资源独占访问

通过O_EXCL标志确保设备或文件的独占访问,避免多进程冲突。

// 创建并独占访问一个锁文件
int fd = open("/var/run/app.lock", O_CREAT | O_EXCL | O_RDWR, 0644);
if (fd < 0) {if (errno == EEXIST) {printf("Another instance is running.\n");exit(1);}
}
// 程序运行期间保持文件打开

4.4. 非易失性存储操作

嵌入式设备频繁操作Flash或EEPROM时,需确保数据完整性。

// 写入数据到Flash(强制同步写入)
int fd = open("/mnt/flash/data.bin", O_WRONLY | O_SYNC);
write(fd, data, data_size);
close(fd); // 确保数据落盘

关键参数O_SYNC:每次写操作等待物理写入完成(防止断电丢失数据)。

4.5. 动态资源管理

在资源受限的嵌入式系统中,及时close()释放文件描述符避免泄漏。

while (1) {int fd = open("/dev/sensor", O_RDONLY);if (fd < 0) break;// 读取传感器数据...close(fd); // 每次循环必须关闭!
}

陷阱:忘记close()会导致文件描述符耗尽,系统崩溃。

4.6. 临时文件操作

配合unlink()实现临时文件自动清理。

int fd = open("/tmp/temp_data.tmp", O_CREAT | O_RDWR, 0600);
unlink("/tmp/temp_data.tmp"); // 删除文件链接
// 文件内容仍可通过fd访问...
close(fd); // 文件实际被删除

五、常见问题

5.1. open函数常见问题

  • 权限不足(Permission denied):当尝试打开一个文件或设备时,如果没有足够的权限,open函数将返回错误。这通常发生在尝试以写模式打开只读文件或尝试访问受保护的设备文件时。
  • 文件或目录不存在(No such file or directory):如果提供的文件路径不正确或文件/目录确实不存在,open函数将返回此错误。
  • 文件是一个目录(File is a directory):尝试以文件的方式打开一个目录时,open函数将返回此错误。在Linux中,目录不是以普通文件的方式打开的,而是使用特定的系统调用(如opendir)来访问。
  • 打开文件数量超过系统限制(Too many open files):每个进程在Linux系统中都有一个打开文件数量的限制。如果尝试打开的文件数量超过了这个限制,open函数将返回错误。
  • 文件正在被其他进程占用(File is in use by another process):如果尝试打开一个已经被其他进程以独占方式打开的文件,可能会遇到此问题。这通常发生在尝试写入一个被其他进程锁定的文件时。
  • 无效参数(Invalid argument):如果传递给open函数的参数无效(如无效的文件路径、不正确的标志组合等),函数将返回此错误。
  • 只读文件系统(Read-only file system):尝试在只读文件系统上写入文件时,将返回此错误。

5.2. close函数常见问题

  • 没有句柄(No such file or directory,但表现为close函数错误):尝试关闭一个无效或已经关闭的文件描述符时,close函数将返回错误。通常发生在文件描述符被误用或重复关闭时。
  • 文件描述符超出范围:如果尝试关闭一个超出当前进程文件描述符范围的文件描述符,close函数将返回错误。
  • 资源忙碌(Resource busy):在极少数情况下,如果尝试关闭一个仍在被使用的资源(如一个正在被其他线程或进程访问的文件),可能会遇到此问题。然而,这种情况在标准的close函数使用中较为罕见,更多发生在底层资源管理和驱动程序开发中。

5.3. 解决方案与建议

  • 检查权限:确保在尝试打开文件或设备时具有足够的权限。可以使用ls -l命令查看文件权限,并使用chmodchown命令调整权限和所有权。
  • 验证文件路径:在调用open函数之前,验证文件路径的正确性。可以使用绝对路径而不是相对路径来避免路径错误。
  • 避免误用文件描述符:仔细管理文件描述符的使用,避免重复打开和关闭同一个文件描述符。可以使用文件描述符表来跟踪打开的文件。
  • 处理错误:在调用openclose函数时,始终检查返回值以处理可能的错误。可以使用errno变量来获取更详细的错误信息。
  • 优化资源使用:监控和管理打开文件的数量,避免超过系统限制。在不再需要文件时及时关闭它们以释放资源。
  • 调试与测试:使用调试工具(如gdb)和日志记录来跟踪和诊断openclose函数使用中的问题。

六、总结

openclose函数是嵌入式Linux应用开发中文件操作的基础。通过合理使用这两个函数,可以实现对文件的读写操作,并有效管理文件资源。在开发过程中,务必注意错误处理和资源释放,以确保程序的稳定性和安全性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/69841.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MT6835 21位 磁编码器 SPI 平台无关通用驱动框架 STM32

MT6835 21位 磁编码器 SPI 平台无关通用驱动框架 STM32 1. 获取代码&#xff1a;2. 加入你的项目2.1 以 STM32 为例:2.2 以 ESP-IDF 为例: 3. 对接 API3.1 以 STM32 为例&#xff1a; 4. 更多函数说明5. 写入 EEPROM 示例 MT6835 Framework 纯C语言实现&#xff0c;跨平台&…

24、深度学习-自学之路-卷积神经网络

一、你怎么理解卷积神经网络呢&#xff0c;我的理解是当你看一个东西的时候&#xff0c;你的眼睛距离图片越近&#xff0c;你看到的东西就越清晰&#xff0c;但是如果你看到的图片只是整个物体的一小部分&#xff0c;那么你将不知道你看到的物品是什么&#xff0c;因为关注整体…

集合家族详情

一、Java集合框架全景图 1.1 核心接口层次结构 graph TDA[Iterable] --> B[Collection]B --> C1[List]B --> C2[Set]B --> C3[Queue]C1 --> D1[ArrayList]C1 --> D2[LinkedList]C2 --> E1[HashSet]C2 --> E2[TreeSet]C3 --> F1[PriorityQueue]G[M…

ArcGIS基础知识之ArcMap基础设置——ArcMap选项:常规选项卡设置及作用

作为一名 GIS 从业者,ArcMap 是我们日常工作中不可或缺的工具。对于初学者来说,掌握 ArcMap 的基础设置是迈向 GIS 分析与制图的第一步。今天,就让我们一起深入了解 ArcMap 选项中常规选项卡的各个设置,帮助大家更好地使用这款强大的软件。 在 ArcMap 中,常规选项卡是用户…

在fedora41中安装钉钉dingtalk_7.6.25.4122001_amd64

在Fedora-Workstation-Live-x86_64-41-1.4中安装钉钉dingtalk_7.6.25.4122001_amd64.deb 到官网下载钉钉Linux客户端com.alibabainc.dingtalk_7.6.25.4122001_amd64.deb https://page.dingtalk.com/wow/z/dingtalk/simple/ddhomedownload#/ 一、直接使用dpkg命令安装deb包报错…

设置mysql的主从复制模式

mysql设置主从复制模式似乎很容易&#xff0c;关键在于1&#xff09;主库启用二进制日志&#xff0c;2&#xff09;从库将主库设为主库。另外&#xff0c;主从复制&#xff0c;复制些什么&#xff1f;从我现在获得的还很少的经验来看&#xff0c;复制的内容有表&#xff0c;用户…

面试真题 | B站C++渲染引擎

一、基础与语法 自我介绍 请简要介绍自己的背景、专业技能和工作经验。实习介绍 详细描述你在实习期间参与的项目、职责和成果。二、智能指针相关问题回答 unique_ptr 是如何实现的?它有哪些特点和优势? unique_ptr 是C++11引入的一种智能指针,用于管理动态分配的内存资源…

【算法学习】拓扑排序(Topological Sorting)

目录 定义 例子 拓扑排序的实现 核心思想 实现方法 1&#xff0c;Kahn算法&#xff08;基于贪心策略&#xff09; 步骤&#xff1a; 用二维数组存储图的例子 用哈希表存储图的例子 2&#xff0c;基于DFS的后序遍历法 总结 拓扑排序的应用场景 1&#xff0c;任务调度 …

AGI时代的认知重塑:人类文明的范式转移与思维革命

文章目录 引言:站在文明转型的临界点一、认知危机:当机器开始理解世界1.1 AGI的本质突破:从模式识别到世界建模1.2 人类认知的脆弱性暴露二、认知革命:重构思维的四个维度2.1 元认知升级:从直觉思维到二阶观察2.2 混合智能:人机认知回路的构建2.3 认知安全:防御机器思维…

解释和对比“application/octet-stream“与“application/x-protobuf“

介绍 在现代 Web 和分布式系统的开发中&#xff0c;数据的传输和交换格式扮演着关键角色。为了确保数据在不同系统之间的传输过程中保持一致性&#xff0c;MIME 类型&#xff08;Multipurpose Internet Mail Extensions&#xff09;被广泛应用于描述数据的格式和内容类型。在 …

零基础学CocosCreator·第九季-网络游戏同步策略与ESC架构

课程里的版本好像是1.9&#xff0c;目前使用版本为3.8.3 开始~ 目录 状态同步帧同步帧同步客户端帧同步服务端ECS框架概念ECS的解释ECS的特点EntityComponentSystemWorld ECS实现逻辑帧&渲染帧 ECS框架使用帧同步&ECS 状态同步 一般游戏的同步策略有两种&#xff1a;…

实现限制同一个账号最多只能在3个客户端(有电脑、手机等)登录(附关键源码)

如上图&#xff0c;我的百度网盘已登录设备列表&#xff0c;有一个手机&#xff0c;2个windows客户端。手机设备有型号、最后登录时间、IP等。windows客户端信息有最后登录时间、操作系统类型、IP地址等。这些具体是如何实现的&#xff1f;下面分别给出android APP中采集手机信…

算法基础:贪心|双指针|二分|倍增

贪心 算法思想&#xff1a; 把整个问题分解成多个步骤&#xff0c;在每个步骤都选取当前步骤的最优方案&#xff0c;直到所有步骤结束&#xff1b;每个步骤都不会影响后续步骤。 核心&#xff1a;采取局部最优&#xff0c;最终结果就全局最优。 双指针 反向扫描 同向扫描 二…

在本地校验密码或弱口令 (windows)

# 0x00 背景 需求是验证服务器的弱口令&#xff0c;如果通过网络侧校验可能会造成账户锁定风险。在本地校验不会有锁定风险或频率限制。 # 0x01 实践 ## 1 使用 net use 命令 可以通过命令行使用 net use 命令来验证本地账户的密码。打开命令提示符&#xff08;CMD&#xff0…

Expo运行模拟器失败错误解决(xcrun simctl )

根据你的描述&#xff0c;问题主要涉及两个方面&#xff1a;xcrun simctl 错误和 Expo 依赖版本不兼容。以下是针对这两个问题的解决方案&#xff1a; 解决 xcrun simctl 错误 错误代码 72 通常表明 simctl 工具未正确配置或路径未正确设置。以下是解决步骤&#xff1a; 确保 …

具身智能训练新思路!将生成视频用于训练机器人

将生成视频用于训练具身智能(Embodied AI)确实是近年来备受关注的前沿方向,这一思路通过结合生成式AI(如扩散模型、神经辐射场等)与机器人学习,为解决真实世界数据稀缺、训练成本高等问题提供了新可能。以下从技术逻辑、潜在优势、挑战及案例方向展开分析: 一、技术逻辑…

【设计模式】【行为型模式】观察者模式(Observer)

&#x1f44b;hi&#xff0c;我不是一名外包公司的员工&#xff0c;也不会偷吃茶水间的零食&#xff0c;我的梦想是能写高端CRUD &#x1f525; 2025本人正在沉淀中… 博客更新速度 &#x1f4eb; 欢迎V&#xff1a; flzjcsg2&#xff0c;我们共同讨论Java深渊的奥秘 &#x1f…

git 克隆指定 tag 的项目

git 克隆指定 tag 的项目 一、克隆指定tag的项目二、验证克隆结果 一、克隆指定tag的项目 以 tinyxml2项目 为例说明&#xff1a; git clone --branch V10.0.0 https://github.com/leethomason/tinyxml2.git解释&#xff1a; git clone&#xff1a;这是克隆一个远程仓库的命…

OSPF高级特性(3):安全特效

引言 OSPF的基础我们已经结束学习了&#xff0c;接下来我们继续学习OSPF的高级特性。为了方便大家阅读&#xff0c;我会将高级特性的几篇链接放在末尾&#xff0c;所有链接都是站内的&#xff0c;大家点击即可阅读&#xff1a; OSPF基础&#xff08;1&#xff09;&#xff1a;工…

把 DeepSeek1.5b 部署在显卡小于4G的电脑上

这里写自定义目录标题 介绍准备安装 Ollama查看CUDA需要版本安装CudaToolkit检查Cuda是否装好二、设置Ollama环境变量三、验证是否跑在GPU上ollama如何导入本地下载的模型安装及配置docker安装open-webui启动open-webui开始对话介绍 Deepseek1.5b能够运行在只用cpu和gpu内存小…