【嵌入式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;跨平台&…

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;用户…

【算法学习】拓扑排序(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 认知安全:防御机器思维…

零基础学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…

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

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

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内存小…

WebSocket与Socket.io的区别

文章目录 引言一、WebSocket&#xff1a;原生的实时通信协议&#xff08;一&#xff09;WebSocket 是什么&#xff08;二&#xff09;WebSocket 的工作原理&#xff08;三&#xff09;WebSocket 的使用方法&#xff08;四&#xff09;WebSocket 的优势&#xff08;五&#xff0…

STM32 裸机 C编程 vs micropython编程 vs linux python

以led点亮为例。 STM32 裸机 C编程需要设置时钟&#xff0c;管脚。 static void MX_GPIO_Init(void) {GPIO_InitTypeDef GPIO_InitStruct {0};// GPIO端口时钟使能__HAL_RCC_GPIOA_CLK_ENABLE();// 配置PA5为推挽输出模式GPIO_InitStruct.Pin GPIO_PIN_5;GPIO_InitStruct.M…

AI语言模型的技术之争:DeepSeek与ChatGPT的架构与训练揭秘

云边有个稻草人-CSDN博客 目录 第一章&#xff1a;DeepSeek与ChatGPT的基础概述 1.1 DeepSeek简介 1.2 ChatGPT简介 第二章&#xff1a;模型架构对比 2.1 Transformer架构&#xff1a;核心相似性 2.2 模型规模与参数 第三章&#xff1a;训练方法与技术 3.1 预训练与微调…

稀土抑烟剂——为汽车火灾安全增添防线

一、稀土抑烟剂的基本概念 稀土抑烟剂是一类基于稀土元素&#xff08;如稀土氧化物和稀土金属化合物&#xff09;开发的高效阻燃材料。它可以显著提高汽车内饰材料的阻燃性能&#xff0c;减少火灾发生时有毒气体和烟雾的产生。稀土抑烟剂不仅能提升火灾时的安全性&#xff0c;…

硅基流动平台大模型 DeepSeek API 调用示例

硅基流动平台大模型 API 调用示例 硅基流动平台作为一个集成多种主流开源大模型的云服务平台&#xff0c;为用户提供了便捷的 API 调用方式&#xff0c;让用户无需自建硬件或进行复杂配置&#xff0c;即可轻松使用各种大模型。以下是详细的硅基流动平台大模型 API 调用示例&am…

vue项目 Axios创建拦截器

Axios 1. Axios 和 Ajax 简介2. Axios 和 Ajax 的区别3. 从 按钮 到 Axios请求后端接口的 大致顺序 1. Axios 和 Ajax 简介 Ajax&#xff08;Asynchronous JavaScript and XML&#xff09; 不是一种技术&#xff0c;而是一个编程技术概念&#xff0c;核心是通过 XMLHttpReques…

CNN-BiGRU卷积神经网络双向门控循环单元多变量多步预测,光伏功率预测

CNN-BiGRU卷积神经网络双向门控循环单元多变量多步预测&#xff0c;光伏功率预测 代码下载&#xff1a;CNN-BiGRU卷积神经网络双向门控循环单元多变量多步预测&#xff0c;光伏功率预测 一、引言 1.1、研究背景及意义 随着全球能源危机和环境问题的日益严重&#xff0c;可再…