【嵌入式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.pswp.cn/bicheng/71358.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/71358.shtml
英文地址,請注明出處:http://en.pswp.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…