`mmap` 系統調用詳解

mmap 是 Unix/Linux 系統中一個強大且多用途的系統調用,用于將文件或設備映射到進程的地址空間,實現內存映射I/O。

1. 函數的概念與用途

mmap(內存映射)函數允許程序將文件或其他對象直接映射到其地址空間,這樣文件內容就可以通過內存指針直接訪問,而不需要使用傳統的 read/write 系統調用。

主要用途:

  • 文件I/O優化:提供對文件的高效隨機訪問,避免頻繁的 read/write 系統調用
  • 進程間通信:通過映射同一文件實現共享內存
  • 內存分配:用于實現高效的內存分配器(如 malloc
  • 程序加載:用于加載可執行文件和共享庫
  • 零拷貝I/O:在某些情況下可以實現零拷貝網絡傳輸

2. 函數的聲明與出處

mmap 函數定義在 <sys/mman.h> 頭文件中,是 POSIX 標準的一部分。

#include <sys/mman.h>void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

3. 返回值的含義與取值范圍

  • 成功時:返回映射區域的起始地址
  • 失敗時:返回 MAP_FAILED(通常是 (void *)-1),并設置 errno 來指示錯誤原因

常見錯誤碼 (errno):

  • EACCES:文件描述符不支持所請求的訪問類型
  • EAGAIN:文件已被鎖定,或太多內存被鎖定
  • EBADF:文件描述符無效
  • EINVAL:參數無效(如長度為零、不支持的保護或標志)
  • ENFILE:已達到系統對打開文件總數的限制
  • ENODEV:文件所在文件系統不支持內存映射
  • ENOMEM:沒有可用內存,或進程超出內存映射限制
  • EPERM:操作被拒絕(如嘗試寫入只寫區域)

4. 參數的含義與取值范圍

  1. void *addr

    • 含義:建議的映射起始地址(通常設為 NULL,由內核自動選擇)
    • 取值范圍:任何有效的地址或 NULL
  2. size_t length

    • 含義:要映射的字節數
    • 取值范圍:大于 0 的值
  3. int prot

    • 含義:內存保護標志,指定訪問權限
    • 常見取值(使用按位或組合):
      • PROT_NONE:頁面不可訪問
      • PROT_READ:頁面可讀
      • PROT_WRITE:頁面可寫
      • PROT_EXEC:頁面可執行
  4. int flags

    • 含義:映射類型和選項
    • 常見取值(使用按位或組合):
      • MAP_SHARED:共享映射,修改對其他進程可見
      • MAP_PRIVATE:私有映射,修改不會寫回文件
      • MAP_ANONYMOUS:不映射文件,創建匿名內存
      • MAP_FIXED:必須使用指定地址
      • MAP_LOCKED:鎖定頁面在內存中
  5. int fd

    • 含義:要映射的文件描述符
    • 取值范圍:有效的文件描述符(對于匿名映射,設為 -1)
  6. off_t offset

    • 含義:文件中的偏移量,必須是系統頁面大小的倍數
    • 取值范圍:非負值,通常是頁面大小的倍數

5. 函數使用案例

案例1:文件映射與讀取

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>int main() {const char *filename = "example.txt";const int file_size = 1024;// 創建并寫入示例文件int fd = open(filename, O_RDWR | O_CREAT, 0644);if (fd == -1) {perror("open");exit(EXIT_FAILURE);}// 調整文件大小if (ftruncate(fd, file_size) == -1) {perror("ftruncate");close(fd);exit(EXIT_FAILURE);}// 映射文件到內存char *mapped = mmap(NULL, file_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);if (mapped == MAP_FAILED) {perror("mmap");close(fd);exit(EXIT_FAILURE);}// 通過內存訪問文件內容const char *message = "Hello, mmap!";strncpy(mapped, message, strlen(message));printf("File content via mmap: %s\n", mapped);// 取消映射并關閉文件if (munmap(mapped, file_size) == -1) {perror("munmap");}close(fd);return 0;
}

案例2:進程間共享內存

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>#define SHM_SIZE 1024int main() {// 創建共享內存區域(匿名映射)char *shared_mem = mmap(NULL, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);if (shared_mem == MAP_FAILED) {perror("mmap");exit(EXIT_FAILURE);}pid_t pid = fork();if (pid == -1) {perror("fork");exit(EXIT_FAILURE);}if (pid == 0) {  // 子進程printf("Child process writing to shared memory\n");strcpy(shared_mem, "Message from child process");exit(EXIT_SUCCESS);} else {         // 父進程wait(NULL);  // 等待子進程結束printf("Parent process reading from shared memory: %s\n", shared_mem);// 清理if (munmap(shared_mem, SHM_SIZE) == -1) {perror("munmap");}}return 0;
}

案例3:高效大數據處理

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>#define FILE_SIZE (1024 * 1024 * 100)  // 100MBint main() {const char *filename = "large_file.bin";int fd = open(filename, O_RDWR | O_CREAT, 0644);if (fd == -1) {perror("open");exit(EXIT_FAILURE);}// 調整文件大小if (ftruncate(fd, FILE_SIZE) == -1) {perror("ftruncate");close(fd);exit(EXIT_FAILURE);}// 映射文件char *mapped = mmap(NULL, FILE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);if (mapped == MAP_FAILED) {perror("mmap");close(fd);exit(EXIT_FAILURE);}// 使用內存映射進行高效處理clock_t start = clock();for (size_t i = 0; i < FILE_SIZE; i++) {mapped[i] = (char)(i % 256);  // 填充數據}clock_t end = clock();double elapsed = (double)(end - start) / CLOCKS_PER_SEC;printf("Processed %d MB in %.2f seconds (%.2f MB/s)\n", FILE_SIZE / (1024 * 1024), elapsed, (FILE_SIZE / (1024.0 * 1024.0)) / elapsed);// 清理if (munmap(mapped, FILE_SIZE) == -1) {perror("munmap");}close(fd);remove(filename);  // 刪除臨時文件return 0;
}

6. 編譯方式與注意事項

編譯命令:

gcc -o mmap_example1 mmap_example1.c
gcc -o mmap_example2 mmap_example2.c
gcc -o mmap_example3 mmap_example3.c

注意事項:

  1. 權限檢查:確保對映射的文件有適當的訪問權限
  2. 對齊要求offset 參數必須是系統頁面大小的倍數(可用 sysconf(_SC_PAGE_SIZE) 獲取)
  3. 資源清理:始終使用 munmap() 取消映射,并使用 close() 關閉文件描述符
  4. 錯誤處理:始終檢查返回值并處理可能的錯誤
  5. 同步:對于共享映射,修改可能不會立即寫回文件,可使用 msync() 強制同步
  6. 內存保護:注意設置適當的保護標志,避免安全漏洞
  7. 可移植性:不同系統可能有不同的頁面大小和限制

7. 執行結果說明

案例1輸出:

File content via mmap: Hello, mmap!

這個示例創建了一個文件,通過內存映射寫入內容,然后讀取并顯示內容。

案例2輸出:

Child process writing to shared memory
Parent process reading from shared memory: Message from child process

這個示例演示了父子進程如何通過匿名映射共享內存區域。

案例3輸出:

Processed 100 MB in 0.15 seconds (666.67 MB/s)

這個示例展示了使用內存映射處理大文件的高效性,通過直接內存訪問避免了頻繁的系統調用。

8. 圖文總結

以下是 mmap 系統調用的工作流程:

文件映射
匿名映射
應用程序調用 mmap()
內核驗證參數并保留地址空間區域
映射類型?
建立文件頁表項
但不立即加載所有內容
分配物理內存頁并清零
返回映射區域的指針
應用程序通過指針訪問內存
訪問的頁面已加載?
觸發缺頁異常
內核加載所需數據到內存
恢復應用程序執行
直接訪問內存
繼續執行

關鍵點總結:

  1. mmap 提供了一種高效的文件訪問方式,避免了頻繁的 read/write 系統調用
  2. 支持文件映射和匿名映射兩種模式,分別用于文件I/O和進程間通信
  3. 采用惰性加載機制,只有在實際訪問時才會將數據加載到內存
  4. 對于共享映射,修改可能會自動寫回文件(取決于標志)
  5. 必須正確使用 munmap() 釋放映射區域,避免資源泄漏
  6. 適當設置保護標志和映射選項對性能和安全性至關重要

mmap 是一個強大的系統調用,正確使用可以顯著提高I/O性能,但需要仔細考慮內存管理和同步問題。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/96896.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/96896.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/96896.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

深度剖析Spring AI源碼(二):Model抽象層 - “馴服”天下AI的“緊箍咒”

深度剖析Spring AI源碼&#xff08;二&#xff09;&#xff1a;Model抽象層 - “馴服”天下AI的“緊箍咒”上一章我們鳥瞰了Spring AI的宏偉藍圖&#xff0c;今天&#xff0c;我們要深入這座大廈的基石——Model抽象層。如果說Spring AI是連接Java與AI世界的橋梁&#xff0c;那…

永磁同步電機無速度算法--高頻脈振正弦波注入到兩相靜止坐標系

一、原理介紹采用一種改進的永磁同步電機低速無位置傳感器控制策略。與傳統的旋轉高頻信號注入法和脈振高頻信號注入法不同&#xff0c;該策略選擇向靜止坐標軸系注入高頻脈振信號&#xff0c;轉子位置估計信息可以通過載波電流響應提取。并使用一種類似于簡化型擴展卡爾曼濾波…

嵌入式學習日志————ADC模數轉換器之實驗

1.配置ADC的步驟①開啟RCC時鐘&#xff0c;包括ADC和GPIO的時鐘②配置GPIO&#xff0c;把相應端口配置成模擬輸入模式③配置多路開關&#xff0c;把左邊的通道接入右邊的規則組列表里④配置ADC轉換器⑤調用ADC_Cmd函數&#xff0c;開啟ADC2.庫函數配置ADCCLK分頻器void RCC_ADC…

Java設計模式之《狀態模式》

目錄 1、狀態模式 1.1、介紹 1.2、設計背景 1.3、適用場景 2、實現 2.1、if-else實現 2.2、狀態模式實現 2.3、最終版 1、關于if-else的優化 2、狀態模式下的優化 3、ArrayList 配置“狀態流” 3、總結 前言 關于Java的設計模式分類如下&#xff1a; 對于狀態模式…

three.js+WebGL踩坑經驗合集(9.2):polygonOffsetFactor工作原理大揭秘

本篇延續上篇內容&#xff1a; three.jsWebGL踩坑經驗合集(9.1):polygonOffsetUnits工作原理大揭秘-CSDN博客 跟polygonOffsetUnits相比&#xff0c;polygonOffsetFactor的系數m要復雜得多&#xff0c;因為它跟平面的視角相關&#xff0c;而不像r那樣&#xff0c;在一個固定的…

C++高級特性與設計模式答案

目錄 C++高級特性與設計模式:從資源管理到架構設計 一、C++高級特性:超越基礎語法的利器 1. 什么是RAII(資源獲取即初始化)?它有什么作用? 實現原理 核心作用 2. 什么是Pimpl慣用法?它有什么優勢? 實現方式 核心優勢 3. 什么是CRTP(奇異遞歸模板模式)?它的應用場景是…

論文閱讀:arxiv 2025 Can You Trick the Grader? Adversarial Persuasion of LLM Judges

總目錄 大模型安全相關研究&#xff1a;https://blog.csdn.net/WhiffeYF/article/details/142132328 Can You Trick the Grader? Adversarial Persuasion of LLM Judges https://arxiv.org/pdf/2508.07805 https://www.doubao.com/chat/17534937260220418 文章目錄論文翻譯…

6pen Art

本文轉載自&#xff1a;6pen Art - Hello123工具導航 ** 一、&#x1f3a8; 6pen 是什么&#xff1f; 6pen 是一款由國內團隊開發的 AI 繪畫工具&#xff0c;讓你只需用文字描述想法&#xff0c;就能瞬間生成驚艷的視覺畫作。不管是寫實風景還是抽象概念&#xff0c;它都能理…

Let‘s Encrypt證書在 Android5.x 的設備上報錯

報錯信息&#xff1a; com.android.volley.NoConnectionError: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.at com.android.volley.toolbox.NetworkUtility.shouldRetryException(N…

C語言數組名與sizeof的深層關聯

要理解 “數組名本質代表整個數組的類型和內存塊” 與 “sizeof(arr) 輸出總字節數” 的關聯&#xff0c;核心是抓住 sizeof 運算符的設計邏輯 和 數組類型的本質屬性—— 這兩者是直接掛鉤的&#xff0c;我們一步步拆解&#xff1a;第一步&#xff1a;先明確 sizeof 的核心作用…

最近對javashop做了壓力測試:百萬級并發下完全不是問題

最近對 javashop 做了壓力測試&#xff1a;百萬級并發下完全不是問題 在電商行業競爭白熱化的今天&#xff0c;系統性能直接決定了用戶體驗和企業商業成功。本文基于《Javashop 壓測報告》&#xff0c;從技術架構、核心指標、業務價值三大維度深度解析其性能優勢&#xff0c;并…

Java大廠面試實戰:從Spring Boot到微服務架構的全鏈路技術解析

Java大廠面試實戰&#xff1a;從Spring Boot到微服務架構的全鏈路技術解析 面試場景&#xff1a;某互聯網大廠Java后端開發崗 面試官&#xff08;嚴肅&#xff09;&#xff1a;謝飛機&#xff0c;我們今天來聊點硬核的。先說說你對Java生態的理解。 謝飛機&#xff08;撓頭&…

在分布式環境下正確使用MyBatis二級緩存

在分布式環境下使用 MyBatis 二級緩存&#xff0c;核心挑戰是解決多節點緩存一致性問題。單機環境中&#xff0c;二級緩存是內存級別的本地緩存&#xff0c;而分布式環境下多節點獨立部署&#xff0c;本地緩存無法跨節點共享&#xff0c;易導致 “緩存孤島” 和數據不一致。本文…

血緣元數據采集開放標準:OpenLineage Integrations Apache Spark Quickstart with Jupyter

OpenLineage 是一個用于元數據和血緣采集的開放標準&#xff0c;專為在作業運行時動態采集數據而設計。它通過統一的命名策略定義了由作業&#xff08;Job&#xff09;、運行實例&#xff08;Run&#xff09;和數據集&#xff08;Dataset&#xff09; 組成的通用模型&#xff0…

人工智能之數學基礎:離散隨機變量和連續隨機變量

本文重點 隨機變量是概率論與統計學中的核心概念,用于將隨機現象的抽象結果轉化為可量化的數值。根據取值特性的不同,隨機變量可分為離散型和連續型兩大類。 在前面的課程中我們學習了隨機變量,隨機變量可以理解為一個函數,通過這個函數我們就可以將隨機試驗中的結果數值…

SQL語句(查詢)

單表查詢 常量查詢 讓我們來看一個具體的 SQL 代碼和結果示例&#xff0c;假設有一張名為 orders 的數據表&#xff0c;它存儲了訂單信息&#xff0c;包括訂單編號&#xff08;order_id&#xff09;、商品單價&#xff08;unit_price&#xff09;、購買數量&#xff08;quantit…

Java 大視界 -- Java 大數據機器學習模型在金融市場波動預測與資產配置動態調整中的應用

Java 大視界 -- Java 大數據機器學習模型在金融市場波動預測與資產配置動態調整中的應用引言&#xff1a;正文&#xff1a;一、Java 構建的金融數據處理架構1.1 多源數據實時融合與清洗1.2 跨市場數據關聯&#xff08;風險傳導分析&#xff09;二、Java 驅動的市場波動預測模型…

基于muduo庫的圖床云共享存儲項目(一)

基于muduo庫的圖床云共享存儲項目&#xff08;一&#xff09;項目簡介整體架構項目依賴基礎組件muduo庫Channel類Poller / EpollPoller 類EventLoopAcceptor類FastDfsJSON的使用項目簡介 當前所實現的項目是一個基于muduo庫的圖床云共享存儲項目&#xff0c;他的主要的功能就是…

數字化轉型三階段:從信息化、數字化到數智化的戰略進化

企業的數字化轉型包括信息化、數字化、數智化三個階段&#xff0c;并非一個階段結束才能進入到下一個階段。01信息化→業務數據化信息化是將企業在生產經營過程中產生的業務信息進行記錄、儲存和管理&#xff0c;通過電子終端呈現&#xff0c;便于信息的傳播與溝通。信息化是對…

SpringBoot如何獲取系統Controller名稱和方法名稱

這種代碼里面的Controller和里面的方法怎么獲取代碼&#xff1a;/*** 獲取所有Controller名稱*/ApiDescription("獲取所有Controller名稱")PostMapping("/getControllerNames")public Result getControllerNames() {return dataDesensitizationRulesServic…