محتويات الدورة

C / المتقدم: حدود اللغة والأنظمة / الدرس 17

قابلية النقل وواجهات النظام

حدد نواة قابلة للنقل واعزل عقود النظام والترميزات الخارجية وافتراضات ABI عند حدود صريحة.

وقت القراءة التقريبي: 5 دقيقةتاريخ مراجعة المحتوى
في هذه الصفحة

أهداف الدرس

  • فصل ISO C وPOSIX وامتدادات المترجم.
  • فك الحقول الخارجية دون الاعتماد على ترتيب المضيف.
  • معالجة نتائج استدعاء النظام بأنواعها الموقعة الفعلية.

قابل للنقل إلى أية بيئات؟

قابلية النقل مجموعة بيئات وافتراضات مدعومة ومعلنة، وليست غياب كل اعتماد منصي. توفر ISO C17 المستضافة المكتبة الأساسية؛ وقد توفر البيئات الحرة مجموعة أصغر وبداية مختلفة. تضيف POSIX واجهات مثل read وواصفات الملفات ومرافق العمليات. وامتداد المترجم طبقة أخرى موثقة منفصلة.

اكتب عقد نقل يحدد المعيار والميزات الاختيارية المطلوبة وترميز المحارف وأدنى المديات واعتمادات النظام. تضيف C23 ميزات مثل nullptr وstrdup القياسية، لكنها ليست من أساس C17. توافر الميزة في وضع مترجم متساهل لا يجعلها جزءا من إصدار ISO المختار.

جعل المتطلبات قابلة للفحص

تستطيع _Static_assert رفض افتراضات ترجمة غير مدعومة في C11 وC17. ينبغي أن تعبر عن مطلب فعلي لا قيد منسوخ بلا حاجة من آلة المؤلف. تكشف limits.h وstdint.h القدرات؛ أنواع العرض الدقيق اختيارية، بينما توفر عائلات أدنى عرض مدى لا يقل عن المطلوب.

يفحص المثال الضمانات اللازمة لقيمتي حقل صغيرتين فقط ولا يطبع أحجام آلة. إذا تطلب تنسيق شبكة بايتات معنونة من ثمانية بتات فاشترط CHAR_BIT == 8 ووثق السبب. رفض بيئة غير مدعومة بوضوح أفضل من قراءة تمثيل مختلف على القرص بصمت.

c فحص متطلبات التمثيل الفعلية
#include <limits.h>
#include <stdio.h>

_Static_assert(CHAR_BIT >= 8, "at least eight bits per byte required");
_Static_assert(UINT_MAX >= 65535u, "sixteen value bits required");

int main(void)
{
    puts("required ranges available");
    return 0;
}
الناتج المتوقع
required ranges available

يفي التنفيذ المطابق لـC17 بالشرطين؛ وقد تحتاج التنسيقات الأشد إلى فحوص أشد.

فك حقول عددية محددة

يجب أن يحدد الحقل الخارجي معنى وحداته. لقيمة من ثمانيتين بترتيب الأكبر أولا اضرب الأولى في 256 وأضف الثانية. لا يثبت تحويل المؤشر إلى unsigned short ذلك الترتيب ولا تمثيل الكائن المطلوب. فك إلى نوع بمدى كاف ثم افحص مجال التطبيق.

يمثل البرنامج التالي قيم الثمانيات بعنصري unsigned char معروف أن قيمتيهما بين صفر و255. يلائم الحساب غير الموقع ستة عشر بت قيمة على الأقل. يوضح فك الحقل، ولا يدعي أن كل بايت C ثمانية أو أن كتابة الأعداد الأصلية العشوائية تنتج تنسيق نقل محمولا.

c فك قيمة من ثمانيتين بالأكبر أولا
#include <stdio.h>

int main(void)
{
    const unsigned char octets[2] = {0x12, 0x34};
    unsigned value = (unsigned)octets[0] * 256u + (unsigned)octets[1];
    printf("value=%u\n", value);
    return 0;
}
الناتج المتوقع
value=4660

تعني 0x12 العدد 18 و0x34 العدد 52؛ و18 مضروبا في 256 مع 52 يساوي 4660.

معاملة استدعاءات النظام كعقد مستقل

تُصرح POSIX read في unistd.h وتعيد ssize_t، بعدد بايتات غير سالب عند النجاح و-1 عند الخطأ. قد تكون القراءة القصيرة نقلا جزئيا معتادا؛ ويعني الصفر نهاية الملف للطلب غير الصفري المعتاد. لا تحول إلى حجم غير موقع إلا بعد فحص النتيجة السالبة. هذه قواعد POSIX وليست قواعد دفقات ISO C.

يعرض المقتطف تصنيف النتيجة فقط وهو غير قابل للتشغيل عمدا لأن المحول المنصي يوفر descriptor وbuffer وcount وسياسة الإعادة. يتطلب EINTR سياسة إعادة مقصودة، ويتطلب EAGAIN غير الحاجب انتظار الجاهزية بدلا من حلقة إعادة مشغولة. أبق ملكية الواصف وسلوك الإلغاء ضمن عقد المحول.

c تصنيف نتيجة قراءة POSIX
/* POSIX adapter fragment: surrounding objects and policy are required. */
ssize_t received = read(descriptor, buffer, count);
if (received < 0) {
    /* Inspect errno before converting to an unsigned size. */
} else if (received == 0) {
    /* End of input for this nonzero request. */
} else {
    size_t available = (size_t)received;
    /* Process exactly available bytes. */
}

يوضح المقتطف فحص النتيجة الموقعة ويحتاج دالة محيطة خاصة بالمنصة.

عزل تفاصيل ABI ونظام التشغيل

يضبط ABI اصطلاحات الاستدعاء والتخطيط وأسماء الرموز والتوافق الثنائي على منصة. لا يجعل تطابق تصريحات C الملفات الكائنية من معماريات أو إعدادات ABI غير متوافقة آمنة للربط. أعد بناء المكونات التابعة باتساق عند تغير الإعدادات. وعند حدود اللغات حدد الملكية وترميز النص واصطلاحات الأخطاء أيضا.

أبق المحلل ونموذج البيانات المحمولين مستقلين عن اكتشاف المسارات والساعات والمقابس وإنشاء العمليات وبروتوكولات الحفظ الدائم. اختبر النواة بمدخلات اصطناعية ثم كل محول وفق عقد منصته الرسمي. يجب أن يختار كشف الميزات تنفيذا موثقا أو يبلغ غياب القدرة بوضوح، لا يسقط سلوكا مطلوبا بصمت.

الأخطاء الشائعة وإصلاحها

يتحول خطأ POSIX إلى عدد بايتات هائل.

لماذا يحدث؟
حُولت ssize_t السالبة إلى size_t مبكرا.
كيف تصلحه؟
افحص received < 0 قبل التحويل.
كيف تتجنب تكراره؟
احفظ أنواع نتائج النظام حتى تصنيف الخطأ.

تتغير قيمة الشبكة بين المضيفين.

لماذا يحدث؟
استُخدم التخطيط أو الترتيب الأصلي كترميز.
كيف تصلحه؟
فك الوحدات المحددة صراحة إلى نوع غير موقع كاف.
كيف تتجنب تكراره؟
اختبر متجهات بروتوكول ثابتة على كل هدف مدعوم.

يرفض بناء C17 دالة مألوفة.

لماذا يحدث؟
اعتُبر مرفق POSIX أو C23 جزءا من C17.
كيف تصلحه؟
استخدم محولا موثقا أو تنفيذا للعملية في C17.
كيف تتجنب تكراره؟
حدد المعيار الحاكم لكل واجهة خارجية.

اختبر فهمك

1. يتطلب تطبيق بايتات من ثمانية بتات وحقولا دقيقة من 32 بتا. حدد عقدا مبررا للبناء أو البداية.

اعرض الحل المشروح

اشترط CHAR_BIT == 8 عند الترجمة وتحقق من توفير uint32_t قبل استعماله. حدد ترتيب البايتات الخارجي مستقلا عن تمثيله الأصلي. ارفض الأهداف غير المدعومة بتشخيص واضح أو وفر ترميزا موثقا بأنواع أخرى كافية. فحص sizeof(int) == 4 وحده لا يثبت جميع هذه الشروط.

2. نجح طلب قراءة 100 بايت بأربعين فقط. ما الحالة التي يحفظها محلل الإطارات؟

اعرض الحل المشروح

عالج الأربعين بايت المهيأة فقط واحتفظ بطول الإطار المتبقي كحالة إذا تطلب التنسيق مزيدا. لا تفحص الستين الأخرى. قد توفر قراءة لاحقة مزيدا أو تبلغ النهاية أو تفشل؛ وعلى المحلل تمييز الإطار الناقص من الكامل. ولا ينبغي للمحول اعتبار كل قراءة قصيرة ناجحة خطأ تلقائيا.

أهم ما تعلمته

  • سم المعيار والمنصة وراء كل واجهة خارجية.
  • حدد الترميزات مستقلة عن التخطيط الأصلي وABI.
  • احفظ نتائج الخطأ الموقعة حتى تصنيفها.

المراجع الرسمية