【嵌入式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.rhkb.cn/news/16627.html

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

相关文章

【通俗易懂说模型】一篇弄懂几个经典CNN图像模型(AlexNet、VGGNet、ResNet)

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;深度学习_十二月的猫的博客-CSDN博客 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 目录 1. 前言 2. …

Android 14.0 Launcher3单层模式workspace中app列表页排序功能实现

1.概述 在14.0的定制化开发中,对于Launcher3的功能定制也是好多的,而对于单层app列表页来说排序功能的开发,也是常有的功能这就需要了解加载app数据的流程,然后根据需要进行排序就可以了,接下来就来实现这个功能 如图: 2. Launcher3单层模式workspace中app列表页排序功能…

8K样本在DeepSeek-R1-7B模型上的复现效果

7B Model and 8K Examples: Emerging Reasoning with Reinforcement Learning is Both Effective and Effic (notion.site) 港科大助理教授何俊贤的团队以Qwen2.5-Math-7B&#xff08;基础模型&#xff09;为起点&#xff0c;直接对其进行强化学习。整个过程中&#xff0c;没有…

四、自然语言处理_08Transformer翻译任务案例

0、前言 在Seq2Seq模型的学习过程中&#xff0c;做过一个文本翻译任务案例&#xff0c;多轮训练后&#xff0c;效果还算能看 Transformer作为NLP领域的扛把子&#xff0c;对于此类任务的处理会更为强大&#xff0c;下面将以基于Transformer模型来重新处理此任务&#xff0c;看…

MATLAB 生成脉冲序列 pulstran函数使用详解

MATLAB 生成脉冲序列 pulstran函数使用详解 目录 前言 一、参数说明 二、示例一 三、示例二 总结 前言 MATLAB中的pulstran函数用于生成脉冲序列&#xff0c;支持连续或离散脉冲。该函数通过将原型脉冲延迟并相加&#xff0c;生成脉冲序列&#xff0c;适用于信号处理和系统…

算法练习——滑动窗口

前言&#xff1a;滑动窗口的难点不在于怎么编写代码&#xff0c;而在于如何想到这题是用滑动窗口的算法去解决。其次滑动窗口的左端和右端在滑动时窗口内数据存在单调性。 一&#xff1a;长度最小的子数组 题目要求&#xff1a; 解题思路&#xff1a; 对于第一道滑动窗口算法…

Zabbix-监控SSL证书有效期

背景 项目需要&#xff0c;需要监控所有的SSL证书的有效期&#xff0c;因此需要自定义一个监控项 实现 创建自定义脚本 在Zabbix的scripts目录(/etc/zabbix/scripts/)下创建一个新的shell脚本check_ssl.sh&#xff0c;内容如下 #!/bin/bash time$(echo | openssl s_client…

VSCode中出现“#include错误,请更新includePath“问题,解决方法

1、出现的问题 在编写C程序时&#xff0c;想引用头文件但是出现如下提示&#xff1a; &#xff08;1&#xff09;首先检查要引用的头文件是否存在&#xff0c;位于哪里。 &#xff08;2&#xff09;如果头文件存在&#xff0c;在编译时提醒VSCode终端中"#include错误&am…

讯方·智汇云校华为授权培训机构的介绍

官方授权 华为授权培训服务伙伴&#xff08;Huawei Authorized Learning Partner&#xff0c;简称HALP&#xff09;是获得华为授权&#xff0c;面向公众&#xff08;主要为华为企业业务的伙伴/客户&#xff09;提供与华为产品和技术相关的培训服务&#xff0c;培养华为产业链所…

LabVIEW商业软件开发

在商业软件开发和仪器自动测试领域&#xff0c;LabVIEW以其图形化编程方式、高效的数据采集能力和强大的硬件集成优势&#xff0c;成为众多工程项目的核心开发工具。然而&#xff0c;商业软件的开发远不止编写代码和实现功能那么简单&#xff0c;尤其是在仪器自动测试领域&…

优化关键词还有哪些软件可用?

随着2025年互联网的发展&#xff0c;越来越多的企业认识到关键词优化的重要性。SEO&#xff08;搜索引擎优化&#xff09;作为提升网站流量和排名的重要手段&#xff0c;已经成为每个企业营销战略中的核心组成部分。而在SEO优化过程中&#xff0c;关键词的选择和优化无疑是至关…

程序诗篇里的灵动笔触:指针绘就数据的梦幻蓝图<9>

大家好啊&#xff0c;我是小象٩(๑ω๑)۶ 我的博客&#xff1a;Xiao Xiangζั͡ޓއއ 很高兴见到大家&#xff0c;希望能够和大家一起交流学习&#xff0c;共同进步。 这一节是对之前内容的修整 目录 一、传值调用和传址调用二、数组名的理解三、指针访问数组四、结尾 一…

新一代SCADA: 宏集Panorama Suite 2025 正式发布,提供更灵活、符合人体工学且安全的应用体验

宏集科技宣布正式推出全新Panorama Suite 2025 SCADA软件&#xff01;全新版本标志着 Panorama Suite的一个重要里程碑&#xff0c;代表了从 Panorama Suite 2022 开始并跨越三个版本&#xff08;2022、2023、2025&#xff09;的开发过程的顶峰。 此次重大发布集中在六个核心主…

多机器人系统的大语言模型:综述

25年2月来自 Drexel 大学的论文“Large Language Models for Multi-Robot Systems: A Survey”。 大语言模型 (LLM) 的快速发展为多机器人系统 (MRS) 开辟新的可能性&#xff0c;从而增强通信、任务规划和人机交互。与传统的单机器人和多智体系统不同&#xff0c;MRS 带来独特…

【欧洲数据集】高分辨率网格气象数据集E-OBS

目录 数据概述最新版本 E-OBS 30.0e数据下载下载链接1:ECA&D官网下载链接2:ECMWF参考E-OBS 数据集(E-OBS, European high-resolution gridded dataset)是基于 European Climate Assessment & Dataset (ECA&D) 信息的高分辨率网格化观测数据集,涵盖欧洲地区的多…

游戏引擎学习第100天

仓库:https://gitee.com/mrxiao_com/2d_game_2 昨天的回顾 今天的工作重点是继续进行反射计算的实现。昨天&#xff0c;我们开始了反射和环境贴图的工作&#xff0c;成功地根据法线显示了反射效果。然而&#xff0c;我们还没有实现反射向量的计算&#xff0c;导致反射交点的代…

Mac上搭建宝塔环境并部署PHP项目

安装Docker Desktop》搭建Centos版本的宝塔环境》部署PHP项目 1. 下载Docker for mac 软件&#xff1a;https://www.docker.com/ 或使用终端命令&#xff1a;brew install --cask --appdir/Applications docker 2. 使用命令安装宝塔环境的centos7系统&#xff1a; docker pul…

从肠道菌群到炎症因子:读懂疾病的预警信号

当我们的皮肤被轻微割伤或烧伤时&#xff0c;伤口周围区域可能会变得红肿、发热&#xff0c;甚至伴有疼痛&#xff1b;感冒时&#xff0c;喉咙痛、肿胀&#xff1b;不小心扭伤后&#xff0c;可能会肿胀、疼痛和僵硬…这些都与炎症相关。 炎症&#xff0c;作为身体对损伤或感染的…

83.在 Vue3 中使用 OpenLayers 利用 TLE 计算并显示单个卫星的轨迹

1. 前言 在可视化开发中&#xff0c;卫星轨迹的实时计算与展示是一个比较有趣的应用场景。TLE&#xff08;Two-Line Element Set&#xff09;是一种用于描述卫星轨道参数的格式&#xff0c;我们可以通过 satellite.js 解析 TLE 数据&#xff0c;并计算卫星在任意时间点的位置。…

Vue3(2)

一.Vue新特性 &#xff08;1&#xff09;defineOptions:主要是用来定义Options API的选项 背景说明&#xff1a;有< script setup >之前&#xff0c;如果定义props&#xff0c;emits可以轻而易举地添加一个与setup平级 的属性。但是用了< script setup >后&#…