
مرحبا ، habrozhiteli! لقد كتبنا بالفعل عن كتاب مايكل كيريسك
"Linux API. دليل شامل " . قررنا الآن نشر مقتطف من كتاب "إدارة ملف I / O التخزين المؤقت المنفذ في Kernel"
يمكن إعادة تعيين ذاكرة التخزين المؤقت kernel لملفات الإخراج. يكون هذا ضروريًا في بعض الأحيان إذا كان التطبيق ، قبل الاستمرار في العمل (على سبيل المثال ، تغيير قاعدة بيانات تسجيل عملية) ، يجب أن يضمن أن الإخراج مكتوب فعليًا على القرص (أو على الأقل إلى ذاكرة التخزين المؤقت لقرص الجهاز).
قبل النظر في مكالمات النظام المستخدمة للتحكم في التخزين المؤقت لـ kernel ، سيكون من المفيد مراعاة العديد من التعريفات ذات الصلة من SUSv3.
تزامن I / O مع البيانات وتكامل الملففي SUSv3 ، يعني مفهوم إكمال الإدخال / الإخراج المتزامن "عملية إدخال / إخراج أدت إما إلى نقل ناجح للبيانات [إلى القرص] أو تم تشخيصها على أنها غير ناجحة."
يعرّف SUSv3 نوعين مختلفين من عمليات إنهاء الإدخال / الإخراج المتزامنة. يتعلق الفرق بين الأنواع بالبيانات الوصفية ("بيانات حول البيانات") التي تصف الملف. تخزنهم النواة مع بيانات الملف نفسه. ستتم مناقشة تفاصيل البيانات الأولية للملف في القسم 14.4 عند فحص رموز الملف. في غضون ذلك ، سيكون يكفي أن نلاحظ أن البيانات الوصفية للملفات تتضمن معلومات مثل معلومات عن صاحب الملف ومجموعته ، وحقوق الوصول إلى الملف ، وحجم الملف ، وعدد الروابط الثابتة للملف ، وطوابع الوقت التي تظهر وقت آخر مرة تم فيها الوصول إلى الملف ، وآخر مرة تم فيها تعديل آخر مرة ووقت آخر بيانات تعريف تتغير ، وكذلك مؤشرات إلى كتل البيانات.
النوع الأول من إكمال الإدخال / الإخراج المتزامن في SUSv3 هو إكمال تكامل البيانات. عند تحديث بيانات الملف ، يجب ضمان نقل المعلومات الكافية للسماح لمزيد من الاستخراج من هذه البيانات بالعمل.
- بالنسبة لعملية القراءة ، هذا يعني أنه تم نقل بيانات الملف المطلوبة (من القرص) إلى العملية. في حالة وجود عمليات كتابة معلقة قد تؤثر على البيانات المطلوبة ، سيتم نقل البيانات إلى القرص قبل القراءة.
- بالنسبة لعملية الكتابة ، هذا يعني أن البيانات المحددة في طلب الكتابة قد تم نقلها (إلى القرص) ، مثل جميع بيانات تعريف الملف المطلوبة لاستخراج هذه البيانات. النقطة الأساسية التي يجب الانتباه إليها: لضمان استخراج البيانات من الملف المعدل ، ليس من الضروري نقل جميع ملفات medaten. مثال على سمة بيانات التعريف لملف معدل يحتاج إلى ترحيل هو حجمه (إذا زادت عملية الكتابة من حجم الملف). في المقابل ، لن يلزم نقل الطوابع الزمنية للملف المراد تعديلها إلى القرص قبل حدوث استرجاع البيانات اللاحق.
النوع الثاني من إكمال الإدخال / الإخراج المتزامن المحدد في SUSv3 هو إكمال تكامل الملف. هذا هو خيار متقدم لإكمال I / O متزامنة مع تكامل البيانات. الفرق بين هذا الوضع هو أنه أثناء تحديث الملف يتم نقل جميع بيانات التعريف الخاصة به إلى القرص ، حتى لو لم يكن ذلك مطلوبًا لاستخراج بيانات الملف لاحقًا.
يستدعي النظام للتحكم في التخزين المؤقت kernel أثناء ملف I / Oاستدعاء النظام fsync () يعيد تعيين كافة البيانات المخزنة مؤقتاً وكافة بيانات التعريف المرتبطة بملف مفتوح يحتوي على واصف fd. استدعاء fsync () يضع الملف في حالة تكامل (ملف) بعد الانتهاء من الإدخال / الإخراج المتزامن.
إرجاع استدعاء fsync () التحكم فقط بعد اكتمال نقل البيانات إلى جهاز القرص (أو على الأقل إلى ذاكرة التخزين المؤقت الخاصة به).
#include <unistd.h> int fsync(int fd);
إرجاع عند النجاح 0 أو -1 على خطأ
تعمل استدعاء النظام fdatasync () تمامًا مثل fsync () ، ولكن تضع الملف في حالة تكامل (بيانات) بعد الانتهاء من الإدخال / الإخراج المتزامن.
#include <unistd.h> int fdatasync(int fd);
إرجاع عند النجاح 0 أو -1 على خطأ
قد يؤدي استخدام fdatasync () إلى تقليل عدد عمليات القرص من عمليتين مطلوبتين بواسطة استدعاء نظام fsync () إلى واحد. على سبيل المثال ، إذا تم تغيير بيانات الملف ، ولكن يبقى الحجم كما هو ، فإن استدعاء fdatasync () يفرض فقط تحديث البيانات. (سبق أن لوحظ أعلاه أنه من أجل إكمال عملية إدخال / إخراج متزامنة مع تكامل البيانات ، ليست هناك حاجة لنقل التغييرات إلى سمات مثل الوقت الذي تم فيه تعديل الملف آخر مرة.) في المقابل ، فإن استدعاء fsync () يفرض أيضًا نقل البيانات الأولية إلى القرص.
سيكون هذا التخفيض في عدد عمليات إدخال / إخراج القرص مفيدًا للتطبيقات الفردية التي يلعب فيها الأداء والتحديث الدقيق لبيانات تعريف محددة (على سبيل المثال ، طوابع الوقت) دورًا حاسمًا. يمكن أن يؤدي هذا إلى تحسينات كبيرة في الأداء للتطبيقات التي تنتج تحديثات ملفات متعددة في وقت واحد. نظرًا لأن بيانات الملف وبيانات التعريف موجودة عادةً في أجزاء مختلفة من القرص ، فإن تحديث كليهما سيتطلب إجراء عمليات بحث متكررة للأمام والخلف على القرص.
على نظام Linux 2.2 والإصدارات الأقدم ، يتم تطبيق fdatasync () على أنه استدعاء fsync () ، لذلك لا يعطي أي زيادة في الأداء.
بدءًا من إصدار kernel 2.6.17 ، يوفر Linux استدعاء نظام غير قياسي sync_file_range (). يتيح لك التحكم بدقة في عملية مسح بيانات الملف على القرص من fdatasync (). عند الاتصال ، يمكنك تحديد المنطقة التي سيتم إسقاطها في الملف وتعيين العلامات التي تحدد شروط حظر هذه المكالمة. راجع صفحة الدليل sync_file_range (2) لمزيد من التفاصيل.
تتسبب استدعاء النظام sync () في تخزين كافة المخازن المؤقتة لـ kernel التي تحتوي على معلومات محدثة للملفات (مثل كتل البيانات أو كتل المؤشر أو البيانات الأولية وما إلى ذلك) على القرص.
#include <unistd.h> void sync(void);
في تطبيق Linux ، تقوم وظيفة sync () بإرجاع التحكم فقط بعد نقل جميع البيانات إلى جهاز القرص (أو على الأقل إلى ذاكرة التخزين المؤقت الخاصة به). ولكن في SUSv3 ، يُسمح أن تقوم المزامنة () ببساطة بتقديم نقل البيانات لعملية الإدخال / الإخراج في الخطة وإرجاع التحكم حتى يكتمل النقل.
مسح مؤشر ترابط kernel تنفيذها بشكل مستمر المخازن المؤقتة kernel المعدلة إلى القرص إذا لم تتم مزامنتها بشكل صريح لمدة 30 ثانية. يتم ذلك لمنع مزامنة مخازن البيانات المؤقتة مع ملف القرص المقابل لفترات طويلة من الزمن (وليس لتعريضها لخطر الخسارة في حالة فشل النظام). على Linux 2.6 ، يتم تنفيذ هذه المهمة بواسطة مؤشر ترابط kernel pdflush. (في نظام Linux 2.4 ، تم تنفيذه بواسطة خيط kernel kupdated.)
يتم تعريف الفترة الزمنية (بمئات من الثانية) التي يجب بعدها مسح المخزن المؤقت المعدل إلى القرص بواسطة رمز دفق pdflush في ملف / proc / sys / vm / dirty_expire_centisecs. تتحكم الملفات الإضافية في نفس الدليل في الميزات الأخرى للعملية التي يؤديها دفق pdflush.
قم بتشغيل وضع المزامنة لجميع السجلات: O_SYNCيؤدي تحديد علامة O_SYNC عند استدعاء open () إلى إجراء جميع عمليات الإخراج التالية في الوضع المتزامن:
fd = open(pathname, O_WRONLY | O_SYNC);
بعد هذه المكالمة لفتح () ، تقوم كل عملية كتابة () تتم على ملف تلقائيًا بمسح البيانات وبيانات التعريف للملف إلى القرص (أي ، يتم تنفيذ عمليات الكتابة كعمليات كتابة متزامنة مع تكامل الملف).
في الإصدارات القديمة من نظام BSD ، تم استخدام علامة O_FSYNC لتوفير الوظيفة المضمنة مع علامة O_SYNC. في glibc ، يتم تعريف علامة O_FSYNC كمرادف لـ O_SYNC.
تأثير أداء علامة O_SYNCيمكن أن يؤثر استخدام علامة O_SYNC (أو المكالمات المتكررة إلى fsync () أو fdatasync () أو sync ()) بشكل كبير على الأداء. في الجدول. يوضح الشكل 13.3 الوقت اللازم لكتابة مليون بايت لملف تم إنشاؤه للتو (في نظام الملفات ext2) لمختلف أحجام المخزن المؤقت مع وضع علامة O_SYNC بدون تحديد. تم الحصول على النتائج (باستخدام برنامج filebuff / write_bytes.c المتوفر في الكود المصدري للكتاب) باستخدام إصدار kernel "vanilla" 2.6.30 ونظام الملفات ext2 بحجم كتلة 4096 بايت. يحتوي كل سطر على متوسط القيمة التي تم الحصول عليها بعد 20 بداية لحجم المخزن المؤقت المحدد.
الجدول 13.3. تأثير علامة O_SYNC على سرعة كتابة 1 مليون بايت
كما ترى ، يؤدي تحديد علامة O_SYNC إلى زيادة هائلة في الوقت الذي يقضيه عند استخدام مخزن مؤقت يبلغ 1 بايت أكثر من 1000 مرة. يرجى ملاحظة الفرق الكبير الذي يحدث عند تنفيذ السجلات التي تحمل علامة O_SYNC بين الوقت المنقضي ووقت استخدام وحدة المعالجة المركزية. هو نتيجة حظر تنفيذ البرنامج عندما يتم مسح المحتويات الفعلية لكل المخزن المؤقت إلى القرص.
في النتائج الموضحة في الجدول. 13.3 ، لا يؤخذ في الاعتبار عامل آخر يؤثر على الأداء عند استخدام O_SYNC. تحتوي محركات الأقراص الحديثة على ذاكرة تخزين مؤقت داخلية كبيرة ، وبوضع افتراضي ، تقوم علامة O_SYNC ببساطة بنقل البيانات إلى ذاكرة التخزين المؤقت هذه. إذا قمت بتعطيل التخزين المؤقت للقرص (باستخدام الأمر hdparm –W0) ، فسيصبح تأثير أداء O_SYNC أكثر أهمية. مع حجم المخزن المؤقت 1 بايت ، سيزيد الوقت المنقضي من 1030 ثانية إلى حوالي 16000 ثانية. مع حجم المخزن المؤقت 4096 بايت ، سيزيد الوقت المنقضي من 0.34 ثانية إلى 4 ثوان. نتيجة لذلك ، إذا كنت بحاجة إلى إجبار مخازن kernel على التدفق إلى القرص ، فيجب أن تفكر فيما إذا كان يمكنك تصميم التطبيق الخاص بك باستخدام مخازن مؤقتة أكبر للكتابة () أو التفكير في استخدام مكالمات دورية fsync () أو fdatasync () بدلاً من علامة O_SYNC.
إشارات O_DSYNC و O_RSYNCيعرّف SUSv3 علامتي حالة فتح ملفين إضافيين تتعلق بـ I / O المتزامنة: O_DSYNC و O_RSYNC.
ينتج عن علامة O_DSYNC عمليات كتابة متزامنة لاحقة مع تكامل بيانات الإدخال / الإخراج المنتهي (على غرار استخدام fdatasync ()). يختلف تأثير التشغيل عن التأثير الناتج عن علامة O_SYNC ، والذي يؤدي استخدامه إلى عمليات كتابة متزامنة تالية مع تكامل الملف (مثل fsync ()).
يتم تحديد علامة O_RSYNC مع O_SYNC أو O_DSYNC ويؤدي إلى امتداد للسلوك المرتبطة بهذه العلامات أثناء عمليات القراءة. يؤدي تحديد علامتي O_RSYNC و O_DSYNC عند فتح الملف إلى عمليات قراءة متزامنة تالية مع تكامل البيانات (أي قبل اكتمال القراءة ، يتم إكمال جميع إدخالات الملفات المعلقة بسبب وجود O_DSYNC). يؤدي تحديد علامتي O_RSYNC و O_SYNC عند فتح الملف إلى عمليات قراءة متزامنة تالية مع تكامل الملف (أي قبل اكتمال القراءة ، يتم إكمال جميع إدخالات الملفات المعلقة بسبب وجود O_SYNC).
قبل إصدار kernel الإصدار 2.6.33 ، لم يتم تطبيق علامتي O_DSYNC و O_RSYNC على نظام Linux ، وتم تعريف هذه الثوابت في ملفات رأس glibc على أنها إعداد علامة O_SYNC. (في حالة O_RSYNC ، لم يكن هذا صحيحًا ، لأن O_SYNC لا يؤثر على أي ميزات وظيفية لعمليات القراءة.)
بدءًا من إصدار kernel 2.6.33 ، ينفذ Linux علامة O_DSYNC ، ومن المرجح أن يتم إضافة تطبيق O_RSYNC في إصدارات kernel المستقبلية.
قبل إصدار 2.6.33 kernel على نظام Linux ، لم يكن هناك أي تطبيق كامل لدلالات O_SYNC. بدلاً من ذلك ، تم تطبيق علامة O_SYNC كـ O_DSYNC. في التطبيقات المرتبطة بالإصدارات القديمة من مكتبة GNU C للنواة القديمة ، على إصدارات Linux 2.6.33 والإصدارات الأحدث ، لا تزال علامة O_SYNC تتصرف مثل O_DSYNC. يتم ذلك للحفاظ على السلوك المألوف لهذه البرامج. (للحفاظ على التوافق الثنائي للخلف في kernel 2.6.33 ، تم تعيين علامة O_DSYNC على علامة O_SYNC القديمة ، وتتضمن علامة O_SYNC الجديدة علامة O_DSYNC (04010000 و 010000 على التوالي على أحد الأجهزة). وهذا يسمح للتطبيقات المترجمة باستخدام ملفات رأس جديدة ، احصل على دلالات O_DSYNC على الأقل في النوى التي تم إصدارها قبل الإصدار 2.6.33.)
13.4. I / O نظرة عامة التخزين المؤقت
في التين. يوضح الشكل 13.1 مخطط التخزين المؤقت المستخدم (لملفات الإخراج) بواسطة مكتبة stdio و kernel ، كما يوضح آليات التحكم في كل نوع من أنواع التخزين المؤقت. إذا قمت بالنزول من المخطط إلى منتصفه ، فسترى نقل بيانات المستخدم بواسطة وظائف مكتبة stdio إلى المخزن المؤقت stdio ، والذي يعمل في مساحة ذاكرة المستخدم. عندما يكون هذا المخزن المؤقت ممتلئًا ، تلجأ مكتبة stdio إلى استدعاء نظام الكتابة () ، الذي ينقل البيانات إلى ذاكرة التخزين المؤقت لـ kernel (الموجودة في ذاكرة kernel). نتيجة لذلك ، يبدأ kernel عملية قرص لنقل البيانات إلى القرص.
في الجزء الأيسر من الدائرة في التين. يعرض 13.1 المكالمات التي يمكن استخدامها في أي وقت لفرض تدفق أي من المخازن المؤقتة بشكل صريح. يُظهر الجزء الأيمن المكالمات التي يمكن استخدامها لإجراء إعادة تعيين تلقائيًا إما عن طريق إيقاف تشغيل التخزين المؤقت في مكتبة stdio أو عن طريق تشغيل إخراج الملف من وضع التنفيذ المتزامن لمكالمات النظام بحيث يتم مسح كل مكالمة للكتابة () على الفور إلى القرص.
13.5. نواة I / O الإخطار
يسمح استدعاء النظام posix_fadvise () للعملية بإبلاغ kernel بالطريقة المفضلة لديه للوصول إلى بيانات الملف.
يمكن لـ (kernel) (ولكن ليس من الضروري) استخدام المعلومات التي يوفرها استدعاء نظام posix_fadvise () لتحسين استخدامه لذاكرة التخزين المؤقت المخزن المؤقت ، وبالتالي زيادة أداء I / O للعملية وللنظام ككل. استدعاء posix_fadvise () لا يؤثر على دلالات البرنامج.
#define _XOPEN_SOURCE 600 #include <fcntl.h> int posix_fadvise(int fd, off_t offset, off_t len, int advice);
إرجاع عند النجاح 0 أو رقم خطأ موجب عند حدوثه
وسيطة fd هي واصف ملف يحدد الملف الذي يجب الاتصال به kernel. تحدد الوسيطة offset and len مساحة الملف التي يرتبط بها الإشعار: تشير الإزاحة إلى الإزاحة الأولية للمنطقة ، ويشير len إلى حجمها بالبايت. تعيين len إلى 0 يعني أن جميع وحدات البايت تعني ، بدءًا من الإزاحة وتنتهي بنهاية الملف. (في إصدارات kernel السابقة إلى 2.6.6 ، تم تفسير القيمة 0 لـ len حرفيًا على أنها 0 بايت.)
توضح وسيطة المشورة الطبيعة المقصودة لوصول العملية إلى الملف. يتم تعريفه بأحد القيم التالية.
POSIX_FADV_NORMAL - لا تحتوي العملية على إشعار خاص بشأن أنماط العلاج. هذا هو السلوك الافتراضي إذا لم يتم تقديم إعلامات للملف. على نظام Linux ، تقوم هذه العملية بتعيين النافذة لقراءة البيانات بشكل استباقي من ملف إلى حجمه الأصلي (128 كيلو بايت).
POSIX_FADV_SEQUENTIAL - تتضمن العملية قراءة البيانات بالتتابع من الإزاحات الأصغر إلى الإزاحات الأكبر. على نظام Linux ، تقوم هذه العملية بتعيين النافذة لقراءة البيانات بشكل استباقي من ملف لمضاعفة قيمته الأصلية.
POSIX_FADV_RANDOM - تتضمن العملية الوصول إلى البيانات بترتيب عشوائي. على Linux ، يعطل هذا الخيار القراءة الاستباقية للبيانات من ملف.
POSIX_FADV_WILLNEED - تتضمن العملية الوصول إلى المنطقة المحددة من الملف في المستقبل القريب. يقوم kernel بقراءة البيانات بشكل استباقي لملء ذاكرة التخزين المؤقت للمخزن المؤقت ببيانات الملف في النطاق المحدد بواسطة وسيطات الإزاحة و len. لا تحظر مكالمات القراءة () اللاحقة على الملف القرص I / O ، ولكن ببساطة استرداد البيانات من ذاكرة التخزين المؤقت المخزن المؤقت. لا تقدم kernel أي ضمانات فيما يتعلق بطول الفترة الزمنية التي يتم فيها استرداد البيانات من الملف في ذاكرة التخزين المؤقت المخزن المؤقت. إذا كانت هناك حاجة خاصة للذاكرة أثناء تشغيل عملية أخرى أو kernel ، فسيتم إعادة استخدام الصفحة في النهاية. بمعنى آخر ، إذا كانت هناك حاجة كبيرة للذاكرة ، نحتاج إلى ضمان وجود فجوة زمنية صغيرة بين استدعاء posix_fadvise () والمكالمة اللاحقة (أو المكالمات) لقراءة (). (يتم توفير الوظيفة المكافئة لعملية POSIX_FADV_WILLNEED بواسطة استدعاء نظام readahead () الخاص بنظام Linux.)
POSIX_FADV_DONTNEED - لا تتضمن العملية مكالمات إلى منطقة الملفات المحددة في المستقبل القريب. وبهذه الطريقة ، يتم إخطار النواة أنه يمكن تحرير صفحات ذاكرة التخزين المؤقت المقابلة (إن وجدت). على نظام Linux ، يتم تنفيذ هذه العملية على مرحلتين. أولاً ، إذا لم تكن قائمة انتظار الكتابة على الجهاز المضيف ممتلئة بسلسلة من الطلبات ، يتجاهل kernel أي صفحات مخبأ معدلة في المنطقة المحددة. ثم يحاول kernel تحرير كافة صفحات ذاكرة التخزين المؤقت من المنطقة المحددة. بالنسبة للصفحات المعدلة في هذه المنطقة ، سيتم إكمال المرحلة الثانية بنجاح فقط إذا تم تسجيلها على الجهاز الأساسي خلال المرحلة الأولى ، أي أن قائمة انتظار التسجيل على الجهاز ليست ممتلئة. نظرًا لأن التطبيق لا يمكنه التحقق من حالة قائمة الانتظار على الجهاز ، يمكنك ضمان تحرير صفحات ذاكرة التخزين المؤقت عن طريق استدعاء fsync () أو fdatasync () على مؤشر fd قبل تطبيق POSIX_FADV_DONTNEED.
POSIX_FADV_NOREUSE - تتضمن العملية وصولاً لمرة واحدة إلى البيانات في المنطقة المحددة من الملف ، دون إعادة استخدامها. وبالتالي ، يتم إخطار النواة أنه يمكن تحرير الصفحات بعد وصول واحد إليها. على نظام Linux ، يتم تجاهل هذه العملية حاليًا.
ظهرت مواصفات posix_fadvise () فقط في SUSv3 ، وهذه الواجهة غير مدعومة من قبل جميع تطبيقات UNIX. على نظام Linux ، تم توفير استدعاء posix_fadvise () منذ إصدار kernel 2.6.
13.6. تجاوز سعة المخزن المؤقت المؤقت: I / O المباشر
بدءًا من الإصدار 2.4 من kernel ، يسمح Linux لأحد التطبيقات بتجاوز ذاكرة التخزين المؤقت للمخزن المؤقت عند تنفيذ إدخال / إخراج القرص عن طريق نقل البيانات مباشرة من مساحة ذاكرة المستخدم إلى ملف أو إلى جهاز قرص. في بعض الأحيان يسمى هذا الوضع I / O المباشر أو غير المجهزة.
المعلومات المقدمة هنا هي لنظام Linux فقط وليست موحدة في SUSv3. ومع ذلك ، يتم توفير بعض خيارات الوصول المباشر للإدخال / الإخراج للأجهزة أو الملفات بواسطة معظم تطبيقات UNIX.
أحيانًا يتم إساءة فهم I / O المباشر كوسيلة لتحقيق أداء I / O عالي. ولكن بالنسبة لمعظم التطبيقات ، يمكن أن يؤدي استخدام الإدخال / الإخراج المباشر إلى تقليل الأداء بشكل كبير. والحقيقة هي أن النواة تنفذ العديد من التحسينات لزيادة أداء الإدخال / الإخراج من خلال استخدام ذاكرة التخزين المؤقت المؤقتة ، بما في ذلك قراءة البيانات الاستباقية المتسلسلة ، وأداء الإدخال / الإخراج في مجموعات كتل القرص ، والسماح للعمليات بالوصول إلى نفس وحدة التخزين نفس الملف ، مشاركة المخازن المؤقتة في ذاكرة التخزين المؤقت. يتم فقد كل هذه الأنواع من التحسين عند استخدام الإدخال / الإخراج المباشر. الغرض منه هو فقط للتطبيقات ذات متطلبات I / O المتخصصة ، على سبيل المثال ، أنظمة إدارة قواعد البيانات التي تقوم بإجراء التخزين المؤقت وتحسين I / O ، والتي لا تحتاج إلى kernel لإضاعة وقت وحدة المعالجة المركزية والذاكرة لتنفيذ المهام نفسها.
يمكن إجراء الإخراج المباشر للإدخال إما فيما يتعلق بملف واحد ، أو فيما يتعلق بجهاز كتلة (على سبيل المثال ، قرص). للقيام بذلك ، عند فتح ملف أو جهاز باستخدام المكالمة open () ، يتم تحديد علامة O_DIRECT.
تعمل علامة O_DIRECT منذ إصدار kernel 2.4.10. استخدام هذه العلامة غير مدعوم من قبل جميع أنظمة الملفات والإصدارات من Linux kernel. تدعم معظم أنظمة الملفات الأساسية علامة O_DIRECT ، لكن العديد من أنظمة الملفات غير التابعة لـ UNIX (مثل VFAT) لا تدعمها. يمكنك اختبار دعم هذه الميزة عن طريق اختبار نظام الملفات المحدد (إذا كان نظام الملفات لا يدعم O_DIRECT ، سيفشل الاتصال open () بسبب خطأ EINVAL) أو عن طريق فحص شفرة مصدر kernel لذلك.
إذا فتحت إحدى العمليات الملف بعلامة O_DIRECT ، والآخر بالطريقة المعتادة (أي باستخدام ذاكرة التخزين المؤقت المخزن المؤقت) ، فلا يوجد تناسق بين محتويات ذاكرة التخزين المؤقت المخزن المؤقت والبيانات التي يتم قراءتها أو كتابتها من خلال الإدخال / الإخراج المباشر. مثل هذا التطور يجب تجنبه.
يمكن العثور على معلومات حول الطريقة القديمة (غير الموصى بها الآن) للحصول على وصول أولي إلى جهاز قرص على صفحة الدليل الخام (8).
قيود المحاذاة للإدخال / الإخراج المباشرنظرًا لأن الإدخال / الإخراج المباشر (سواء على أجهزة القرص أو فيما يتعلق بالملفات) ينطوي على وصول مباشر إلى القرص ، يجب مراعاة بعض القيود عند تنفيذ I / O.
- يجب محاذاة المخزن المؤقت للبيانات المحمولة على حدود الذاكرة ، مضاعفات حجم الكتلة.
- يجب أن يكون الإزاحة في الملف أو في الجهاز الذي تبدأ منه البيانات المنقولة من مضاعفات حجم الكتلة.
- يجب أن يكون طول البيانات المنقولة مضاعفًا لحجم الكتلة.
سيؤدي عدم الامتثال لأي من هذه القيود إلى حدوث خطأ EINVAL. في القائمة أعلاه ، يشير حجم الكتلة إلى حجم الكتلة الفعلية للجهاز (عادةً 512 بايت).
عند تنفيذ I / O المباشر في Linux 2.4 ، يتم فرض قيود أكثر من Linux 2.6: يجب أن تكون المحاذاة والطول والإزاحة مضاعفة لحجم الكتلة المنطقية لنظام الملفات المستخدم. (عادةً ما تكون أحجام الكتل المنطقية في نظام الملفات 1024 أو 2048 أو 4096 بايت.)
مثال البرنامج13.1 O_DIRECT . , ( ) , , , , , , , read(). 4096 .
, :
$ ./direct_read /test/x 512 512 0 Read 512 bytes $ ./direct_read /test/x 256 ERROR [EINVAL Invalid argument] read 512 $ ./direct_read /test/x 512 1 ERROR [EINVAL Invalid argument] read 512 $ ./direct_read /test/x 4096 8192 512 Read 4096 bytes $ ./direct_read /test/x 4096 512 256 ERROR [EINVAL Invalid argument] read 512
13.1 , , , memalign(). memalign() 7.1.4.
#define _GNU_SOURCE /* O_DIRECT <fcntl.h> */ #include <fcntl.h> #include <malloc.h> #include "tlpi_hdr.h" int main(int argc, char *argv[]) { int fd; ssize_t numRead; size_t length, alignment; off_t offset; void *buf; if (argc < 3 || strcmp(argv[1], "–help") == 0) usageErr("%s file length [offset [alignment]]\n", argv[0]); length = getLong(argv[2], GN_ANY_BASE, "length"); offset = (argc > 3) ? getLong(argv[3], GN_ANY_BASE, "offset") : 0; alignment = (argc > 4) ? getLong(argv[4], GN_ANY_BASE, "alignment") : 4096; fd = open(argv[1], O_RDONLY | O_DIRECT); if (fd == -1) errExit("open"); /* memalign() , , . 'buf' , 'alignment', . , , , , 256 , , 512- . '(char *)' ( 'void *', memalign(). */ buf = (char *) memalign(alignment * 2, length + alignment) + alignment; if (buf == NULL) errExit("memalign"); if (lseek(fd, offset, SEEK_SET) == -1) errExit("lseek"); numRead = read(fd, buf, length); if (numRead == -1) errExit("read"); printf("Read %ld bytes\n", (long) numRead); exit(EXIT_SUCCESS); } _______________________________________________________________filebuff/direct_read.c
»
»
المحتويات»
مقتطفات20% —
Linux