ไลบรารีการแมปหน่วยความจำ C++11 ข้ามแพลตฟอร์มเฉพาะส่วนหัวที่ใช้งานง่ายพร้อมใบอนุญาต MIT
mio ได้รับการสร้างขึ้นโดยมีเป้าหมายเพื่อให้สามารถรวมเข้าด้วยกันได้อย่างง่ายดาย (เช่น ไม่มีการพึ่งพา) ในโปรเจ็กต์ C ++ ใด ๆ ที่ต้องการไฟล์ที่แมปหน่วยความจำ IO โดยไม่จำเป็นต้องดึง Boost
โปรดอย่าลังเลที่จะเปิดประเด็น เราจะพยายามแก้ไขข้อกังวลต่างๆ ให้ดีที่สุด
เพราะการทำแผนที่หน่วยความจำเป็นสิ่งที่ดีที่สุดนับตั้งแต่หั่นขนมปัง!
ที่จริงจังกว่านั้น แรงจูงใจหลักในการเขียนไลบรารีนี้แทนที่จะใช้ Boost.Iostreams คือการขาดการสนับสนุนในการสร้างการแมปหน่วยความจำด้วยตัวจัดการ/ตัวอธิบายไฟล์ที่เปิดอยู่แล้ว สิ่งนี้เป็นไปได้ด้วย mio
นอกจากนี้ โซลูชันของ Boost.Iostreams กำหนดให้ผู้ใช้เลือกออฟเซ็ตที่ขอบเขตหน้าพอดี ซึ่งยุ่งยากและเกิดข้อผิดพลาดได้ง่าย ในทางกลับกัน mio จะจัดการสิ่งนี้ภายใน โดยยอมรับการชดเชยใดๆ และค้นหาขอบเขตของหน้าที่ใกล้ที่สุด
แม้ว่าจะเป็นเพียงเล็กน้อย แต่ Boost.Iostreams ก็ใช้ไฟล์ที่แมปหน่วยความจำ IO ด้วย std::shared_ptr
เพื่อจัดเตรียมซีแมนทิกส์ที่ใช้ร่วมกัน แม้ว่าจะไม่จำเป็น และโอเวอร์เฮดของการจัดสรรฮีปอาจไม่จำเป็นและ/หรือไม่ต้องการ ใน mio มีสองคลาสที่ครอบคลุมการใช้งานทั้งสองกรณี: คลาสหนึ่งเป็นแบบเคลื่อนย้ายเท่านั้น (โดยพื้นฐานแล้วเป็นนามธรรมแบบไม่มีค่าใช้จ่ายเหนือฟังก์ชัน mmapping เฉพาะระบบ) และคลาสอื่นที่ทำหน้าที่เหมือนกับ Boost.Iostreams ที่เทียบเท่ากับ ความหมายที่ใช้ร่วมกัน
หมายเหตุ: ต้องมีไฟล์ก่อนสร้างการแมป
มีสามวิธีในการแมปไฟล์เข้ากับหน่วยความจำ:
std::system_error
เมื่อเกิดความล้มเหลว: mio::mmap_source mmap (path, offset, size_to_map);
หรือคุณสามารถละเว้นอาร์กิวเมนต์ offset
และ size_to_map
ได้ ซึ่งในกรณีนี้ไฟล์ทั้งหมดจะถูกแมป:
mio::mmap_source mmap (path);
std::error_code error;
mio::mmap_source mmap = mio::make_mmap_source(path, offset, size_to_map, error);
หรือ:
mio::mmap_source mmap = mio::make_mmap_source(path, error);
map
: std::error_code error;
mio::mmap_source mmap;
mmap.map(path, offset, size_to_map, error);
หรือ:
mmap.map(path, error);
หมายเหตุ: ตัวสร้าง จำเป็นต้องมี ข้อยกเว้นในการเปิดใช้งาน หากคุณต้องการสร้างโปรเจ็กต์ของคุณด้วย -fno-exceptions
คุณยังคงสามารถใช้วิธีอื่นได้
นอกจากนี้ ในแต่ละกรณี คุณสามารถระบุประเภทสตริงสำหรับเส้นทางของไฟล์ หรือคุณสามารถใช้ตัวจัดการไฟล์ที่มีอยู่และถูกต้องได้
# include < sys/types.h >
# include < sys/stat.h >
# include < fcntl.h >
# include < mio/mmap.hpp >
// #include <mio/mio.hpp> if using single header
# include < algorithm >
int main ()
{
// NOTE: error handling omitted for brevity.
const int fd = open ( " file.txt " , O_RDONLY);
mio::mmap_source mmap (fd, 0 , mio::map_entire_file);
// ...
}
อย่างไรก็ตาม mio ไม่ได้ตรวจสอบว่าตัวอธิบายไฟล์ที่ให้มานั้นมีสิทธิ์การเข้าถึงเหมือนกับการแมปที่ต้องการหรือไม่ ดังนั้นการแมปจึงอาจล้มเหลว ข้อผิดพลาดดังกล่าวจะถูกรายงานผ่านพารามิเตอร์ std::error_code
out ที่ส่งผ่านไปยังฟังก์ชันการแมป
ผู้ใช้ WINDOWS : ไลบรารีนี้ รองรับ การใช้ประเภทอักขระแบบกว้างสำหรับฟังก์ชันที่คาดหวังสตริงอักขระ (เช่น พารามิเตอร์เส้นทาง)
# include < mio/mmap.hpp >
// #include <mio/mio.hpp> if using single header
# include < system_error > // for std::error_code
# include < cstdio > // for std::printf
# include < cassert >
# include < algorithm >
# include < fstream >
int handle_error ( const std::error_code& error);
void allocate_file ( const std::string& path, const int size);
int main ()
{
const auto path = " file.txt " ;
// NOTE: mio does *not* create the file for you if it doesn't exist! You
// must ensure that the file exists before establishing a mapping. It
// must also be non-empty. So for illustrative purposes the file is
// created now.
allocate_file (path, 155 );
// Read-write memory map the whole file by using `map_entire_file` where the
// length of the mapping is otherwise expected, with the factory method.
std::error_code error;
mio::mmap_sink rw_mmap = mio::make_mmap_sink (
path, 0 , mio::map_entire_file, error);
if (error) { return handle_error (error); }
// You can use any iterator based function.
std::fill (rw_mmap. begin (), rw_mmap. end (), ' a ' );
// Or manually iterate through the mapped region just as if it were any other
// container, and change each byte's value (since this is a read-write mapping).
for ( auto & b : rw_mmap) {
b += 10 ;
}
// Or just change one value with the subscript operator.
const int answer_index = rw_mmap. size () / 2 ;
rw_mmap[answer_index] = 42 ;
// Don't forget to flush changes to disk before unmapping. However, if
// `rw_mmap` were to go out of scope at this point, the destructor would also
// automatically invoke `sync` before `unmap`.
rw_mmap. sync (error);
if (error) { return handle_error (error); }
// We can then remove the mapping, after which rw_mmap will be in a default
// constructed state, i.e. this and the above call to `sync` have the same
// effect as if the destructor had been invoked.
rw_mmap. unmap ();
// Now create the same mapping, but in read-only mode. Note that calling the
// overload without the offset and file length parameters maps the entire
// file.
mio::mmap_source ro_mmap;
ro_mmap. map (path, error);
if (error) { return handle_error (error); }
const int the_answer_to_everything = ro_mmap[answer_index];
assert (the_answer_to_everything == 42 );
}
int handle_error ( const std::error_code& error)
{
const auto & errmsg = error. message ();
std::printf ( " error mapping file: %s, exiting... n " , errmsg. c_str ());
return error. value ();
}
void allocate_file ( const std::string& path, const int size)
{
std::ofstream file (path);
std::string s (size, ' 0 ' );
file << s;
}
mio::basic_mmap
เป็นแบบย้ายเท่านั้น แต่หากจำเป็นต้องมีสำเนาหลายชุดไปยังการแมปเดียวกัน ให้ใช้ mio::basic_shared_mmap
ซึ่งมีความหมาย std::shared_ptr
และมีอินเทอร์เฟซเดียวกันกับ mio::basic_mmap
# include < mio/shared_mmap.hpp >
mio::shared_mmap_source shared_mmap1 ( " path " , offset, size_to_map);
mio::shared_mmap_source shared_mmap2 (std::move(mmap1)); // or use operator=
mio::shared_mmap_source shared_mmap3 (std::make_shared<mio::mmap_source>(mmap1)); // or use operator=
mio::shared_mmap_source shared_mmap4;
shared_mmap4.map( " path " , offset, size_to_map, error);
เป็นไปได้ที่จะกำหนดประเภทของไบต์ (ซึ่งจะต้องมีความกว้างเท่ากับ char
) แม้ว่าจะมีการระบุนามแฝงสำหรับสิ่งที่พบบ่อยที่สุดตามค่าเริ่มต้น:
using mmap_source = basic_mmap_source< char >;
using ummap_source = basic_mmap_source< unsigned char >;
using mmap_sink = basic_mmap_sink< char >;
using ummap_sink = basic_mmap_sink< unsigned char >;
แต่การกำหนดประเภทของคุณเองอาจมีประโยชน์ เช่น เมื่อใช้ประเภท std::byte
ใหม่ใน C++17:
using mmap_source = mio::basic_mmap_source<std::byte>;
using mmap_sink = mio::basic_mmap_sink<std::byte>;
แม้ว่าโดยทั่วไปจะไม่จำเป็น เนื่องจากผู้ใช้แมป mio ร้องขอออฟเซ็ตกับขอบเขตเพจ คุณสามารถสืบค้นรายละเอียดการจัดสรรเพจของระบบที่สำคัญได้โดยการเรียกใช้ mio::page_size()
ซึ่งอยู่ใน mio/page.hpp
คุณสามารถเพิ่ม Mio ในโครงการของคุณเป็นไฟล์ส่วนหัวเดียวได้ง่ายๆ ด้วยการรวม single_includemiomio.hpp
ไฟล์ส่วนหัวเดี่ยวสามารถสร้างใหม่ได้ตลอดเวลาโดยการเรียกใช้สคริปต์ amalgamate.py
ภายใน third_party
python amalgamate.py -c config.json -s ../include
เนื่องจากเป็นไลบรารีส่วนหัวเท่านั้น mio จึงไม่มีส่วนประกอบที่คอมไพล์แล้ว อย่างไรก็ตาม มีการจัดเตรียมระบบการสร้าง CMake ไว้เพื่อให้สามารถทดสอบ ติดตั้ง และจัดองค์ประกอบโปรเจ็กต์ย่อยบนแพลตฟอร์มและระบบปฏิบัติการต่างๆ ได้อย่างง่ายดาย
Mio แจกจ่ายพร้อมกับชุดการทดสอบและตัวอย่างชุดเล็กๆ เมื่อกำหนดค่า mio เป็นโปรเจ็กต์ CMake ระดับสูงสุด ชุดโปรแกรมปฏิบัติการนี้จะถูกสร้างขึ้นตามค่าเริ่มต้น โปรแกรมทดสอบของ Mio ถูกรวมเข้ากับโปรแกรมไดรเวอร์ทดสอบ CMake, CTest
CMake รองรับแบ็กเอนด์จำนวนหนึ่งสำหรับการคอมไพล์และลิงก์
หากต้องการใช้เครื่องมือสร้างการกำหนดค่าคงที่ เช่น GNU Make หรือ Ninja:
cd < mio source directory >
mkdir build
cd build
# Configure the build
cmake -D CMAKE_BUILD_TYPE= < Debug | Release >
-G < " Unix Makefiles " | " Ninja " > ..
# build the tests
< make | ninja | cmake --build . >
# run the tests
< make test | ninja test | cmake --build . --target test | ctest >
หากต้องการใช้เครื่องมือสร้างการกำหนดค่าแบบไดนามิก เช่น Visual Studio หรือ Xcode:
cd < mio source directory >
mkdir build
cd build
# Configure the build
cmake -G < " Visual Studio 14 2015 Win64 " | " Xcode " > ..
# build the tests
cmake --build . --config < Debug | Release >
# run the tests via ctest...
ctest --build-config < Debug | Release >
# ... or via CMake build tool mode...
cmake --build . --config < Debug | Release > --target test
แน่นอนว่าขั้นตอน การสร้าง และ ทดสอบ สามารถดำเนินการผ่านเป้าหมาย ทั้งหมด และ ทดสอบ ตามลำดับจากภายใน IDE หลังจากเปิดไฟล์โปรเจ็กต์ที่สร้างขึ้นระหว่างขั้นตอนการกำหนดค่า
การทดสอบของ Mio ยังได้รับการกำหนดค่าให้ทำงานเป็นไคลเอ็นต์กับแอปพลิเคชันแดชบอร์ดคุณภาพซอฟต์แวร์ CDash โปรดดูเอกสาร Kitware สำหรับข้อมูลเพิ่มเติมเกี่ยวกับโหมดการทำงานนี้
ระบบบิลด์ของ Mio มีเป้าหมายในการติดตั้งและรองรับการใช้งานดาวน์สตรีมผ่านฟังก์ชันภายใน find_package
ของ CMake CMake อนุญาตให้ติดตั้งในตำแหน่งใดก็ได้ ซึ่งอาจระบุได้โดยการกำหนด CMAKE_INSTALL_PREFIX
ณ เวลากำหนดค่า ในกรณีที่ไม่มีข้อกำหนดเฉพาะของผู้ใช้ CMake จะติดตั้ง mio ไปยังตำแหน่งทั่วไปตามระบบปฏิบัติการของแพลตฟอร์ม
หากต้องการใช้เครื่องมือสร้างการกำหนดค่าคงที่ เช่น GNU Make หรือ Ninja:
cd < mio source directory >
mkdir build
cd build
# Configure the build
cmake [-D CMAKE_INSTALL_PREFIX = " path/to/installation " ]
[-D BUILD_TESTING = False]
-D CMAKE_BUILD_TYPE=Release
-G < " Unix Makefiles " | " Ninja " > ..
# install mio
< make install | ninja install | cmake --build . --target install >
หากต้องการใช้เครื่องมือสร้างการกำหนดค่าแบบไดนามิก เช่น Visual Studio หรือ Xcode:
cd < mio source directory >
mkdir build
cd build
# Configure the project
cmake [-D CMAKE_INSTALL_PREFIX = " path/to/installation " ]
[-D BUILD_TESTING = False]
-G < " Visual Studio 14 2015 Win64 " | " Xcode " > ..
# install mio
cmake --build . --config Release --target install
โปรดทราบว่าคำสั่งสุดท้ายของลำดับการติดตั้งอาจต้องใช้สิทธิ์ผู้ดูแลระบบ (เช่น sudo
) หากไดเร็กทอรีรากการติดตั้งอยู่นอกโฮมไดเร็กตอรี่ของคุณ
การติดตั้งครั้งนี้
include/mio
ของรูทการติดตั้งshare/cmake/mio
ของรูทการติดตั้ง ขั้นตอนหลังนี้อนุญาตให้โปรเจ็กต์ดาวน์สตรีม CMake ใช้ mio ผ่าน find_package
เช่น
find_package ( mio REQUIRED )
target_link_libraries ( MyTarget PUBLIC mio::mio )
ผู้ใช้ WINDOWS : mio::mio
เป้าหมาย #define
s WIN32_LEAN_AND_MEAN
และ NOMINMAX
แบบแรกทำให้แน่ใจได้ว่าพื้นที่ผิวที่นำเข้าของ Win API นั้นน้อยที่สุด และแบบหลังปิดใช้งานมาโคร min
และ max
ของ Windows เพื่อไม่ให้รบกวน std::min
และ std::max
เนื่องจาก mio เป็นไลบรารีส่วนหัวเท่านั้น คำจำกัดความเหล่านี้จะรั่วไหลไปยังบิลด์ CMake ดาวน์สตรีม หากการปรากฏตัวของสิ่งเหล่านี้ทำให้เกิดปัญหากับงานสร้างของคุณ คุณสามารถใช้เป้าหมาย mio::mio_full_winapi
ทางเลือก ซึ่งจะไม่เพิ่มคำจำกัดความเหล่านี้
หากติดตั้ง mio ในตำแหน่งที่ไม่ธรรมดา อาจจำเป็นสำหรับโปรเจ็กต์ดาวน์สตรีมเพื่อระบุไดเร็กทอรีรากของการติดตั้ง mio ผ่านทางอย่างใดอย่างหนึ่ง
CMAKE_PREFIX_PATH
CMAKE_PREFIX_PATH
หรือmio_DIR
โปรดดูเอกสาร Kitware สำหรับข้อมูลเพิ่มเติม
นอกจากนี้ mio ยังรองรับการติดตั้งแบบแพ็คเกจที่สามารถย้ายตำแหน่งได้ผ่าน CPack การกำหนดค่าต่อไปนี้ จากไดเร็กทอรี build ให้เรียกใช้ cpack ดังต่อไปนี้เพื่อสร้างการติดตั้งแบบแพ็กเกจ:
cpack -G < generator name > -C Release
รายชื่อเครื่องกำเนิดไฟฟ้าที่รองรับจะแตกต่างกันไปในแต่ละแพลตฟอร์ม ดูผลลัพธ์ของ cpack --help
สำหรับรายการตัวสร้างทั้งหมดที่รองรับบนแพลตฟอร์มของคุณ
หากต้องการใช้ mio เป็นโปรเจ็กต์ย่อย ให้คัดลอกที่เก็บ mio ไปยังโฟลเดอร์ dependencies/externals ของโปรเจ็กต์ของคุณ หากโปรเจ็กต์ของคุณได้รับการควบคุมเวอร์ชันโดยใช้ git คุณสามารถใช้โมดูลย่อย git หรือทรีย่อย git เพื่อซิงโครไนซ์กับที่เก็บอัปสตรีมได้ การใช้และข้อดีสัมพัทธ์ของเครื่องมือ git เหล่านี้อยู่นอกเหนือขอบเขตของเอกสารนี้ แต่โดยสรุป แต่ละข้ออาจมีการกำหนดไว้ดังนี้:
# via git submodule
cd < my project ' s dependencies directory>
git submodule add -b master https://github.com/mandreyel/mio.git
# via git subtree
cd <my project ' s root directory >
git subtree add --prefix < path/to/dependencies > /mio
https://github.com/mandreyel/mio.git master --squash
เมื่อกำหนดไดเร็กทอรีย่อย mio ในโปรเจ็กต์ เพียงเพิ่มบรรทัดต่อไปนี้ในโปรเจ็กต์ของคุณเพื่อเพิ่มไดเร็กทอรีรวม mio ให้กับพาธรวมเป้าหมายของคุณ
add_subdirectory ( path /to/mio/ )
target_link_libraries ( MyTarget PUBLIC <mio::mio | mio> )
โปรดทราบว่าในฐานะโปรเจ็กต์ย่อย การทดสอบและตัวอย่างของ mio จะไม่ถูกสร้างขึ้น และการรวม CPack จะถูกเลื่อนไปยังโปรเจ็กต์โฮสต์