برنامج تعليمي حول مكالمات نظام Linux مع C

Linux System Call Tutorial With C



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

في هذه المقالة ، سنستخدم مكالمات النظام الفعلية للقيام بعمل حقيقي في برنامج سي. أولاً ، سنراجع ما إذا كنت بحاجة إلى استخدام مكالمة نظام ، ثم نقدم مثالاً باستخدام استدعاء sendfile () الذي يمكن أن يحسن أداء نسخ الملف بشكل كبير. أخيرًا ، سنستعرض بعض النقاط التي يجب تذكرها أثناء استخدام مكالمات نظام Linux.







على الرغم من أنه أمر لا مفر منه ، ستستخدم مكالمة نظام في مرحلة ما من حياتك المهنية في تطوير C ، إلا إذا كنت تستهدف الأداء العالي أو وظيفة من نوع معين ، فإن مكتبة glibc والمكتبات الأساسية الأخرى المضمنة في توزيعات Linux الرئيسية ستهتم بأغلبية احتياجاتك.



توفر مكتبة glibc القياسية إطار عمل عبر الأنظمة الأساسية تم اختباره جيدًا لتنفيذ الوظائف التي قد تتطلب بخلاف ذلك استدعاءات نظام خاصة بالنظام. على سبيل المثال ، يمكنك قراءة ملف باستخدام fscanf () أو fread () أو getc () وما إلى ذلك ، أو يمكنك استخدام استدعاء نظام Linux read (). توفر وظائف glibc المزيد من الميزات (مثل معالجة الأخطاء بشكل أفضل ، IO المنسق ، وما إلى ذلك) وستعمل على أي نظام يدعم glibc.



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





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

الآن بعد أن قرأت بيانات إخلاء المسؤولية والتحذيرات والمنعطفات المحتملة ، دعنا الآن نتعمق في بعض الأمثلة العملية.



ما هي وحدة المعالجة المركزية لدينا؟

سؤال ربما لا تفكر معظم البرامج في طرحه ، ولكنه سؤال صالح رغم ذلك. هذا مثال لاستدعاء النظام الذي لا يمكن تكراره مع glibc ولا يتم تغطيته بغلاف glibc. في هذا الكود ، سنقوم باستدعاء getcpu () مباشرة عبر وظيفة syscall (). تعمل وظيفة syscall على النحو التالي:

syscall(SYS_callوarg1وarg2و...)؛

الوسيطة الأولى ، SYS_call ، هي تعريف يمثل رقم استدعاء النظام. عند تضمين sys / syscall.h ، يتم تضمينها. الجزء الأول هو SYS_ والجزء الثاني هو اسم استدعاء النظام.

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

example1.c

#يشمل
#يشمل
#يشمل
#يشمل

intالأساسية() {

غير موقعةوحدة المعالجة المركزيةوالعقدة؛

// احصل على نواة وحدة المعالجة المركزية الحالية وعقدة NUMA عبر استدعاء النظام
// لاحظ أن هذا لا يحتوي على غلاف glibc لذا يجب أن نسميه مباشرة
syscall(SYS_getcpuو &وحدة المعالجة المركزيةو &العقدةوباطل)؛

// عرض المعلومات
printf (يعمل هذا البرنامج على وحدة المعالجة المركزية الأساسية٪ u وعقدة NUMA٪ u.نن'ووحدة المعالجة المركزيةوالعقدة)؛

إرجاع 0؛

}

لتجميع وتشغيل:

مثال 1 دول مجلس التعاون الخليجي.ج -س المثال 1
./مثال 1

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

Sendfile: أداء فائق

يوفر Sendfile مثالاً ممتازًا لتحسين الأداء من خلال استدعاءات النظام. تقوم الدالة sendfile () بنسخ البيانات من واصف ملف إلى آخر. بدلاً من استخدام دالات fread () و fwrite () ، ينفذ Sendfile النقل في مساحة kernel ، مما يقلل الحمل وبالتالي يزيد الأداء.

في هذا المثال ، سنقوم بنسخ 64 ميغابايت من البيانات من ملف إلى آخر. في أحد الاختبارات ، سنستخدم طرق القراءة / الكتابة القياسية في المكتبة القياسية. في الجانب الآخر ، سنستخدم مكالمات النظام واستدعاء sendfile () لتفجير هذه البيانات من موقع إلى آخر.

test1.c (glibc)

#يشمل
#يشمل
#يشمل
#يشمل

#define BUFFER_SIZE 67108864
#define BUFFER_1 'المخزن 1'
#define BUFFER_2 'المخزن المؤقت 2'

intالأساسية() {

ملف*خاطئو *نهاية؛

printf ('ناختبار الإدخال / الإخراج مع وظائف glibc التقليدية.نن')؛

// احصل على مخزن مؤقت BUFFER_SIZE.
// سيحتوي المخزن المؤقت على بيانات عشوائية فيه ولكننا لا نهتم بذلك.
printf ('تخصيص ذاكرة تخزين مؤقت سعة 64 ميغا بايت:')؛
شار *متعادل= (شار *) مالوك (حجم المخزن المؤقت)؛
printf ('انتهىن')؛

// اكتب المخزن المؤقت ل fOut
printf ('كتابة البيانات إلى المخزن المؤقت الأول':)؛
خاطئ= fopen (BUFFER_1و 'wb')؛
fwrite (متعادلو حجم(شار)وحجم المخزن المؤقتوخاطئ)؛
fclose (خاطئ)؛
printf ('انتهىن')؛

printf ('نسخ البيانات من الملف الأول إلى الثاني:')؛
نهاية= fopen (BUFFER_1و 'rb')؛
خاطئ= fopen (BUFFER_2و 'wb')؛
fread (متعادلو حجم(شار)وحجم المخزن المؤقتونهاية)؛
fwrite (متعادلو حجم(شار)وحجم المخزن المؤقتوخاطئ)؛
fclose (نهاية)؛
fclose (خاطئ)؛
printf ('انتهىن')؛

printf ('تحرير المخزن المؤقت:')؛
مجانا (متعادل)؛
printf ('انتهىن')؛

printf ('حذف الملفات:')؛
إزالة (BUFFER_1)؛
إزالة (BUFFER_2)؛
printf ('انتهىن')؛

إرجاع 0؛

}

test2.c (مكالمات النظام)

#يشمل
#يشمل
#يشمل
#يشمل
#يشمل
#يشمل
#يشمل
#يشمل
#يشمل

#define BUFFER_SIZE 67108864

intالأساسية() {

intخاطئونهاية؛

printf ('ناختبار الإدخال / الإخراج باستخدام sendfile () واستدعاءات النظام ذات الصلة.نن')؛

// احصل على مخزن مؤقت BUFFER_SIZE.
// سيحتوي المخزن المؤقت على بيانات عشوائية فيه ولكننا لا نهتم بذلك.
printf ('تخصيص ذاكرة تخزين مؤقت سعة 64 ميغا بايت:')؛
شار *متعادل= (شار *) مالوك (حجم المخزن المؤقت)؛
printf ('انتهىن')؛


// اكتب المخزن المؤقت ل fOut
printf ('كتابة البيانات إلى المخزن المؤقت الأول':)؛
خاطئ=افتح('عازلة 1'وO_RDONLY)؛
اكتب(خاطئو &متعادلوحجم المخزن المؤقت)؛
أغلق(خاطئ)؛
printf ('انتهىن')؛

printf ('نسخ البيانات من الملف الأول إلى الثاني:')؛
نهاية=افتح('عازلة 1'وO_RDONLY)؛
خاطئ=افتح('عازلة 2'وO_RDONLY)؛
إرسال ملف(خاطئونهايةو 0وحجم المخزن المؤقت)؛
أغلق(نهاية)؛
أغلق(خاطئ)؛
printf ('انتهىن')؛

printf ('تحرير المخزن المؤقت:')؛
مجانا (متعادل)؛
printf ('انتهىن')؛

printf ('حذف الملفات:')؛
فك الارتباط('عازلة 1')؛
فك الارتباط('عازلة 2')؛
printf ('انتهىن')؛

إرجاع 0؛

}

تجميع وتشغيل الاختبارات 1 و 2

لإنشاء هذه الأمثلة ، ستحتاج إلى تثبيت أدوات التطوير على التوزيع الخاص بك. على Debian و Ubuntu ، يمكنك تثبيت هذا باستخدام:

ملائمتثبيتأساسيات البناء

ثم قم بالتجميع باستخدام:

مجلس التعاون الخليجيtest1.c-أواختبار 1&& مجلس التعاون الخليجيtest2.c-أواختبار 2

لتشغيل كليهما واختبار الأداء ، قم بتشغيل:

زمن./اختبار 1&& زمن./اختبار 2

يجب أن تحصل على نتائج مثل هذا:

اختبار الإدخال / الإخراج مع وظائف glibc التقليدية.

تخصيص ذاكرة تخزين مؤقت بسعة 64 ميغا بايت: تم
كتابة البيانات إلى المخزن المؤقت الأول: تم
نسخ البيانات من الملف الأول إلى الثاني: تم
تحرير المخزن المؤقت: تم
حذف الملفات: تم
0m0.397s حقيقي
المستخدم 0m0.000s
0 م 0.203 ث
اختبار الإدخال / الإخراج باستخدام sendfile () واستدعاءات النظام ذات الصلة.
تخصيص ذاكرة تخزين مؤقت بسعة 64 ميغا بايت: تم
كتابة البيانات إلى المخزن المؤقت الأول: تم
نسخ البيانات من الملف الأول إلى الثاني: تم
تحرير المخزن المؤقت: تم
حذف الملفات: تم
0m0.019s حقيقي
المستخدم 0m0.000s
0m0.016 ثانية

كما ترى ، فإن الكود الذي يستخدم مكالمات النظام يعمل بشكل أسرع بكثير من المكافئ glibc.

أشياء للذكرى

يمكن أن تؤدي مكالمات النظام إلى زيادة الأداء وتوفير وظائف إضافية ، ولكنها لا تخلو من عيوبها. سيتعين عليك الموازنة بين الفوائد التي توفرها مكالمات النظام مقابل نقص إمكانية نقل النظام الأساسي وانخفاض الوظائف في بعض الأحيان مقارنة بوظائف المكتبة.

عند استخدام بعض استدعاءات النظام ، يجب أن تحرص على استخدام الموارد التي يتم إرجاعها من مكالمات النظام بدلاً من وظائف المكتبة. على سبيل المثال ، بنية FILE المستخدمة لوظائف glibc's fopen () و fread () و fwrite () و fclose () ليست هي نفسها رقم واصف الملف من استدعاء النظام open () (يتم إرجاعه كعدد صحيح). خلط هذه يمكن أن يؤدي إلى مشاكل.

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

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

أتمنى أن تكون قد استمتعت بهذا الغوص العميق في أرض مكالمات نظام Linux. للحصول على قائمة كاملة بمكالمات نظام Linux ، راجع قائمتنا الرئيسية.