【嵌入式Linux应用开发基础】read函数与write函数

目录

一、read 函数

1.1. 函数原型

1.2. 参数说明

1.3. 返回值

1.4. 示例代码

二、write 函数

2.1. 函数原型

2.2. 参数说明

2.3. 返回值

2.4. 示例代码

三、关键注意事项

3.1 部分读写

3.2 错误处理

3.3 阻塞与非阻塞模式

3.4 数据持久化

3.5 线程安全

四、嵌入式场景应用

4.1. 文件数据读写

4.2. 设备驱动交互

4.3. 进程间通信(IPC)

4.4. 网络通信

五、常见问题

5.1. read函数常见问题

5.2. write函数常见问题

5.3 通用建议

六、总结


在嵌入式Linux应用开发中,readwrite函数是文件I/O操作中最基础、最常用的两个系统调用。它们用于从文件描述符(file descriptor)指向的文件或设备中读取数据和向其中写入数据。

一、read 函数

1.1. 函数原型

#include <unistd.h>ssize_t read(int fd, void *buf, size_t count);

1.2. 参数说明

  • fd:文件描述符,由 open 函数返回的一个非负整数,用于标识要读取数据的文件、设备等。
  • buf:指向用于存储读取数据的缓冲区的指针,数据将被读取到这个缓冲区中。
  • count:期望读取的字节数,即希望从文件描述符对应的文件或设备中读取的最大字节数。

1.3. 返回值

  • 大于 0:表示实际成功读取的字节数。
  • 等于 0:表示已经到达文件末尾(EOF),没有更多数据可供读取。
  • 等于 -1:表示读取操作失败,此时 errno 会被设置为相应的错误码,用于指示具体的错误原因。

1.4. 示例代码

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>int main() {int fd = open("example.txt", O_RDONLY);if (fd == -1) {perror("open");return 1;}char buffer[100];ssize_t bytesRead = read(fd, buffer, sizeof(buffer) - 1);if (bytesRead == -1) {perror("read");close(fd);return 1;}buffer[bytesRead] = '\0'; // 确保字符串以NULL结尾printf("Read %zd bytes: %s\n", bytesRead, buffer);close(fd);return 0;
}

二、write 函数

2.1. 函数原型

#include <unistd.h>ssize_t write(int fd, const void *buf, size_t count);

2.2. 参数说明

  • fd:文件描述符,标识要写入数据的文件、设备等。
  • buf:指向包含要写入数据的缓冲区的指针。
  • count:要写入的字节数,即希望将缓冲区中多少字节的数据写入到文件描述符对应的文件或设备中。

2.3. 返回值

  • 大于 0:表示实际成功写入的字节数。
  • 等于 0:通常表示没有写入任何数据,可能是由于某些特殊情况(如文件系统已满但还未返回错误)。
  • 等于 -1:表示写入操作失败,errno 会被设置为相应的错误码,用于指示具体的错误原因。

2.4. 示例代码

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>int main() {int fd = open("output.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);if (fd == -1) {perror("open");return 1;}const char *message = "Hello, World!\n";ssize_t bytesWritten = write(fd, message, strlen(message));if (bytesWritten == -1) {perror("write");close(fd);return 1;}printf("Written %zd bytes\n", bytesWritten);close(fd);return 0;
}

三、关键注意事项

3.1 部分读写

  • 原因:数据未就绪(如网络)、资源限制(如管道缓冲区满)、信号中断等。

  • 处理方式:循环调用函数,直至完成全部数据传输。

    示例代码(读操作):

ssize_t total_read = 0;
while (total_read < count) {ssize_t n = read(fd, buf + total_read, count - total_read);if (n == 0) break; // EOFif (n < 0 && errno != EINTR) break; // 非中断错误if (n > 0) total_read += n;
}

3.2 错误处理

  • 常见errno

    • EAGAIN/EWOULDBLOCK:非阻塞模式下无数据可读或写缓冲区满。

    • EINTR:操作被信号中断。

    • EBADF:无效文件描述符。

  • 处理建议

    • EINTR需重试操作。

    • 对非阻塞I/O的EAGAIN需结合select/poll等待就绪。

3.3 阻塞与非阻塞模式

  • 设置非阻塞模式

int flags = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);

3.4 数据持久化

  • 立即同步:调用fsync(fd)强制将内核缓冲区数据写入存储设备。

3.5 线程安全

  • 多线程操作同一文件描述符需加锁(如pthread_mutex)。

四、嵌入式场景应用

4.1. 文件数据读写

①配置文件读取

  • 场景:嵌入式系统中的应用程序常常需要从配置文件中读取参数,以此来初始化系统。例如,网络设备的配置文件包含 IP 地址、子网掩码、网关等信息,应用程序需要读取这些信息来完成网络配置。

  • 代码示例

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#define BUFFER_SIZE 1024int main() {int fd = open("config.txt", O_RDONLY);if (fd == -1) {perror("open");return 1;}char buffer[BUFFER_SIZE];ssize_t bytes_read = read(fd, buffer, BUFFER_SIZE);if (bytes_read == -1) {perror("read");} else if (bytes_read > 0) {buffer[bytes_read] = '\0';// 处理读取到的配置信息}close(fd);return 0;
}

 ②数据文件写入

  • 场景:在数据采集系统中,需要将采集到的数据存储到文件中,以便后续分析和处理。比如,温度传感器每隔一段时间采集一次温度数据,应用程序将这些数据写入到文件中。
  • 代码示例
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>#define DATA "25.5"int main() {int fd = open("data.txt", O_WRONLY | O_CREAT | O_APPEND, 0644);if (fd == -1) {perror("open");return 1;}ssize_t bytes_written = write(fd, DATA, strlen(DATA));if (bytes_written == -1) {perror("write");}close(fd);return 0;
}

4.2. 设备驱动交互

①传感器数据读取

  • 场景:嵌入式系统通常会连接各种传感器,如加速度计、陀螺仪等。应用程序通过 read 函数从相应的设备文件中读取传感器数据。
  • 代码示例
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>#define SENSOR_DEVICE "/dev/sensor"
#define BUFFER_SIZE 32int main() {int fd = open(SENSOR_DEVICE, O_RDONLY);if (fd == -1) {perror("open");return 1;}char buffer[BUFFER_SIZE];ssize_t bytes_read = read(fd, buffer, BUFFER_SIZE);if (bytes_read == -1) {perror("read");} else if (bytes_read > 0) {buffer[bytes_read] = '\0';// 处理传感器数据}close(fd);return 0;
}

②设备控制命令写入

  • 场景:对于一些可控制的设备,如 LED 灯、电机等,应用程序可以通过 write 函数向设备文件写入控制命令,从而实现对设备的控制。
  • 代码示例
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>#define LED_DEVICE "/dev/led"
#define COMMAND "ON"int main() {int fd = open(LED_DEVICE, O_WRONLY);if (fd == -1) {perror("open");return 1;}ssize_t bytes_written = write(fd, COMMAND, strlen(COMMAND));if (bytes_written == -1) {perror("write");}close(fd);return 0;
}

③串口/UART通信

串口设备(如/dev/ttyS0)是嵌入式系统中常见的通信接口,readwrite用于收发数据:

// 配置串口后...
char tx_data[] = "Hello UART!";
write(uart_fd, tx_data, strlen(tx_data)); // 发送数据char rx_data[32];
ssize_t len = read(uart_fd, rx_data, sizeof(rx_data)); // 接收数据

4.3. 进程间通信(IPC)

①管道通信

  • 场景:在嵌入式系统中,不同进程之间可能需要进行数据交换。管道是一种简单的进程间通信方式,一个进程通过 write 函数向管道写入数据,另一个进程通过 read 函数从管道读取数据。
  • 代码示例
#include <stdio.h>
#include <unistd.h>
#include <string.h>#define BUFFER_SIZE 1024int main() {int pipefd[2];if (pipe(pipefd) == -1) {perror("pipe");return 1;}pid_t pid = fork();if (pid == -1) {perror("fork");return 1;}if (pid == 0) {// 子进程:读取数据close(pipefd[1]);char buffer[BUFFER_SIZE];ssize_t bytes_read = read(pipefd[0], buffer, BUFFER_SIZE);if (bytes_read == -1) {perror("read");} else if (bytes_read > 0) {buffer[bytes_read] = '\0';printf("Child process read: %s\n", buffer);}close(pipefd[0]);} else {// 父进程:写入数据close(pipefd[0]);const char *message = "Hello from parent!";ssize_t bytes_written = write(pipefd[1], message, strlen(message));if (bytes_written == -1) {perror("write");}close(pipefd[1]);}return 0;
}

4.4. 网络通信

套接字数据读写

  • 场景:在嵌入式网络应用中,通过套接字进行网络通信时,使用 read 函数接收网络数据,使用 write 函数发送网络数据。
  • 代码示例(简单 TCP 客户端)
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 8080
#define BUFFER_SIZE 1024int main() {int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd == -1) {perror("socket");return 1;}struct sockaddr_in server_addr;server_addr.sin_family = AF_INET;server_addr.sin_port = htons(SERVER_PORT);inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr);if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {perror("connect");close(sockfd);return 1;}const char *message = "Hello, server!";ssize_t bytes_written = write(sockfd, message, strlen(message));if (bytes_written == -1) {perror("write");}char buffer[BUFFER_SIZE];ssize_t bytes_read = read(sockfd, buffer, BUFFER_SIZE);if (bytes_read == -1) {perror("read");} else if (bytes_read > 0) {buffer[bytes_read] = '\0';printf("Received from server: %s\n", buffer);}close(sockfd);return 0;
}

五、常见问题

5.1. read函数常见问题

①读取到的字节数少于请求数

  • 原因
    • 读普通文件时,在读到请求字节数之前已到达文件尾端。
    • 从终端设备读时,通常一次最多读一行。
    • 从网络读时,网络中的缓冲机构可能造成返回值小于请求读的字节数。
    • 某些面向记录的设备(如磁带),一次最多返回一个记录。
  • 解决方案
    • 在读取文件时,需要检查返回值是否小于请求字节数,并处理文件尾端的情况。
    • 对于从终端设备或网络读取的数据,需要采用适当的缓冲机制来处理数据。

②读取操作失败

  • 原因
    • 文件描述符无效或没有读权限。
    • 提供的缓冲区指针无效。
    • 文件已被其他进程锁定或删除。
  • 解决方案
    • 确保文件描述符有效且具有读权限。
    • 检查缓冲区指针的有效性。
    • 使用文件锁或其他同步机制来避免文件被其他进程锁定或删除。

③读取的数据不准确

  • 原因
    • 文件指针未正确设置。
    • 文件内容在读取过程中被其他进程修改。
  • 解决方案
    • 在读取文件之前,确保文件指针已正确设置到所需的位置。
    • 使用文件锁或其他同步机制来避免文件内容在读取过程中被其他进程修改。

5.2. write函数常见问题

①写入操作失败

  • 原因
    • 文件描述符无效或没有写权限。
    • 磁盘已满或文件系统已满。
    • 提供的缓冲区指针无效。
  • 解决方案
    • 确保文件描述符有效且具有写权限。
    • 检查磁盘和文件系统的剩余空间。
    • 检查缓冲区指针的有效性。

②写入的字节数少于请求数

  • 原因
    • 磁盘已满或文件系统限制导致无法写入更多数据。
    • 网络或设备缓冲区已满,导致写入操作被阻塞或提前返回。
  • 解决方案
    • 在写入文件之前,检查磁盘和文件系统的剩余空间。
    • 对于网络或设备写入操作,需要采用适当的缓冲机制和重试策略来处理写入失败的情况。

③写入的数据未立即生效

  • 原因:数据被写入内核缓冲区,而尚未被刷新到磁盘。
  • 解决方案:使用fsyncfdatasync函数来强制将缓冲区中的数据同步到磁盘。

5.3 通用建议

  • 错误处理
    • 在使用readwrite函数时,务必检查返回值,并根据返回值进行相应的错误处理。
    • 可以使用errno变量来获取更详细的错误信息。
  • 资源管理
    • 在使用文件描述符时,要确保在不再需要时关闭它们,以释放系统资源。
    • 对于网络套接字或其他资源,也需要进行适当的资源管理和释放。
  • 同步与并发
    • 在多进程或多线程环境中,需要确保对文件或其他资源的访问是同步的,以避免数据竞争和不一致性。
    • 可以使用文件锁、信号量或其他同步机制来实现这一点。

六、总结

readwrite函数是嵌入式Linux应用开发中用于文件I/O操作的基础工具。通过这两个函数,可以实现从文件或设备读取数据和向文件或设备写入数据。了解并正确使用这些函数,对于开发稳定、高效的嵌入式应用程序至关重要。

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

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

相关文章

嵌入式八股文(四)计算机网络篇

第一章 基础概念 1. 服务 指网络中各层为紧邻的上层提供的功能调用,是垂直的。包括面向连接服务、无连接服务、可靠服务、不可靠服务。 2. 协议 是计算机⽹络相互通信的对等层实体之间交换信息时必须遵守的规则或约定的集合。⽹络协议的三个基本要素:语法、…

LabVIEW 天然气水合物电声联合探测

天然气水合物被认为是潜在的清洁能源&#xff0c;其储量丰富&#xff0c;预计将在未来能源格局中扮演重要角色。由于其独特的物理化学特性&#xff0c;天然气水合物的探测面临诸多挑战&#xff0c;涉及温度、压力、电学信号、声学信号等多个参数。传统的人工操作方式不仅效率低…

JAVA代码走查重构常用prompt

代码重构prompt&#xff1a; ## 主题&#xff1a; 代码重构 ## 角色扮演: 你是软件开发大师Martin Fowler&#xff0c;精通代码重构、面向对象编程、Clean Code和设计模式&#xff0c;且熟练掌握《重构&#xff0c;改善既有代码的设计》这本书中的重构思想和各种重构方法。 ## …

[数据结构]红黑树,详细图解插入

目录 一、红黑树的概念 二、红黑树的性质 三、红黑树节点的定义 四、红黑树的插入&#xff08;步骤&#xff09; 1.为什么新插入的节点必须给红色&#xff1f; 2、插入红色节点后&#xff0c;判定红黑树性质是否被破坏 五、插入出现连续红节点情况分析图解&#xff08;看…

STM32 HAL库USART串口DMA IDLE中断编程:避坑指南

HAL_UART_Receive接收最容易丢数据了,STM32 HAL库UART查询方式实例 可以考虑用中断来实现,但是HAL_UART_Receive_IT还不能直接用,容易数据丢失,实际工作中不会这样用,STM32 HAL库USART串口中断编程&#xff1a;演示数据丢失, 需要在此基础优化一下. STM32F103 HAL库USART串口…

sql注入中information_schema被过滤的问题

目录 一、information_schema库的作用 二、获得表名 2.1 sys.schema_auto_increment_columns 2.2 schema_table_statistics 三、获得列名 join … using … order by盲注 子查询 在进行sql注入时&#xff0c;我们经常会使用information_schema来进行爆数据库名、表名、…

Jenkins 给任务分配 节点(Node)、设置工作空间目录

Jenkins 给任务分配 节点(Node)、设置工作空间目录 创建 Freestyle project 类型 任务 任务配置 Node 打开任务-> Configure-> General 勾选 Restrict where this project can be run Label Expression 填写一个 Node 的 Label&#xff0c;输入有效的 Label名字&#x…

Electron:使用electron-react-boilerplate创建一个react + electron的项目

使用 electron-react-boilerplate git clone --depth 1 --branch main https://github.com/electron-react-boilerplate/electron-react-boilerplate.git your-project-name cd your-project-name npm install npm start 安装不成功 在根目录加上 .npmrc文件 内容为 electron_…

数控机床设备分布式健康监测与智能维护系统MTAgent

数控机床设备分布式健康监测与智能维护系统MTAgent-v1.1融合了目前各种先进的信号处理以及信息分析算法以算法工具箱的方式&#xff0c;采用了一种开发的、模块化的结构实现信号各种分析处理&#xff0c;采用Python编程语言&#xff0c;满足不同平台需求(包括Windows、Linux)。…

FPGA VIVADO:axi-lite 从机和主机

FPGA VIVADO:axi-lite 从机和主机 TOC在这里插入代码片 前言 协议就不详细讲解了&#xff0c;直接看手册即可。下面主要如何写代码和关键的时序。 此外下面的代码可以直接用于实际工程 一、AXI-LITE 主机 数据转axi lite接口&#xff1a; 读/写数据FIFO缓存 仲裁&#xff1a…

1. 对比 LVS 负载均衡群集的 NAT 模式和 DR 模式,比较其各自的优势 。2. 基于 openEuler 构建 LVS-DR 群集。

DR 模式 * 负载各节点服务器通过本地网络连接&#xff0c;不需要建立专用的IP隧道 原理&#xff1a;首先负载均衡器接收到客户的请求数据包时&#xff0c;根据调度算法决定将请求发送给哪个后端的真实服务器&#xff08;RS&#xff09;。然后负载均衡器就把客户端发送的请求数…

ollama server启动服务后如何停止

要停止 Ollama 服务器服务&#xff0c;取决于如何启动该服务的。以下是几种常见的启动方法和相应的停止服务的步骤&#xff1a; 1. 直接在命令行中启动 如果是在命令行中直接启动 Ollama 服务器的&#xff0c;例如使用以下命令&#xff1a; ollama serve 可以通过以下方式停…

【设计模式】03-理解常见设计模式-行为型模式(专栏完结)

前言 前面我们介绍完创建型模式和创建型模式&#xff0c;这篇介绍最后的行为型模式&#xff0c;也是【设计模式】专栏的最后一篇。 一、概述 行为型模式主要用于处理对象之间的交互和职责分配&#xff0c;以实现更灵活的行为和更好的协作。 二、常见的行为型模式 1、观察者模…

mapbox基础,使用geojson加载line线图层,实现纯色填充、图片填充、虚线和渐变效果

👨‍⚕️ 主页: gis分享者 👨‍⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍⚕️ 收录于专栏:mapbox 从入门到精通 文章目录 一、🍀前言1.1 ☘️mapboxgl.Map 地图对象1.2 ☘️mapboxgl.Map style属性1.3 ☘️line线图层样式二、🍀使用geojson加载…

深入浅出:CUDA是什么,如何利用它进行高效并行计算

在当今这个数据驱动的时代&#xff0c;计算能力的需求日益增加&#xff0c;特别是在深度学习、科学计算和图像处理等领域。为了满足这些需求&#xff0c;NVIDIA推出了CUDA&#xff08;Compute Unified Device Architecture&#xff09;&#xff0c;这是一种并行计算平台和编程模…

LNMP+Zabbix安装部署(Zabbix6.0 Lnmp+Zabbix Installation and Deployment)

LNMPZabbix安装部署&#xff08;Zabbix6.0&#xff09; 简介 LNMP&#xff08;Linux Nginx MySQL PHP&#xff09;是一种流行的Web服务器架构&#xff0c;广泛用于搭建高性能的网站和应用程序。Zabbix 是一个开源的监控软件&#xff0c;可以用来监控网络、服务器和应用程序…

Docker 部署 Dify:轻松集成 Ollama 和 DeepSeek

1 Ollama的安装及使用 1.1 什么是Ollama&#xff1f; Ollama 是一个用于本地部署和运行大型语言模型的框架。 Ollama 的作用包括&#xff1a; 本地模型运行&#xff1a;Ollama 允许在本地机器上运行大型语言模型&#xff08;如 LLaMA、DeepSeek 等&#xff09;&#xff0c;无…

C++笔记之标准库中用于处理迭代器的`std::advance`和`std::distance`

C++笔记之标准库中用于处理迭代器的std::advance和std::distance code review! 文章目录 C++笔记之标准库中用于处理迭代器的`std::advance`和`std::distance`一.`std::advance`函数原型参数说明使用场景示例代码示例 1:移动 `std::vector` 的随机访问迭代器示例 2:移动 `st…

工业制造能耗管理新突破,漫途MTIC-ECM平台助力企业绿色转型!

在工业制造领域&#xff0c;能源消耗一直是企业运营成本的重要组成部分。随着“双碳”目标的推进&#xff0c;如何实现高效能耗管理&#xff0c;成为制造企业亟待解决的问题。漫途MTIC-ECM能源能耗在线监测平台&#xff0c;结合其自研的硬件产品&#xff0c;为工业制造企业提供…

C语言——深入理解指针(2)(数组与指针)

文章目录 数组名的理解使用指针访问数组一维数组传参的本质冒泡排序二级指针指针数组指针数组模拟二维数组 数组名的理解 之前我们在使用指针访问数组内容时&#xff0c;有这样的代码&#xff1a; int arr[10]{1,2,3,4,5,6,7,8,9,10}; int* p&arr[0];这里我们使用&ar…