syscall هي الآلية التي تتفاعل من خلالها برامج المستخدم مع Linux kernel ، ويعد strace أداة قوية لتتبعها. لفهم كيفية عمل نظام التشغيل بشكل أفضل ، من المفيد فهم كيفية عملها.
يمكن تقسيم نظام التشغيل إلى وضعين للتشغيل:
- وضع Kernel هو الوضع المميز الذي يستخدمه نظام التشغيل kernel.
- وضع المستخدم هو الوضع الذي يتم فيه تشغيل معظم تطبيقات المستخدم.
يستخدم المستخدمون عادةً أدوات مساعدة لسطر الأوامر وواجهة رسومية (GUI) لعملهم اليومي. في الوقت نفسه ، يستدعي النظام العمل بشكل غير مرئي في الخلفية ، مشيرًا إلى النواة للقيام بالعمل.
تشبه استدعاءات النظام إلى حد كبير استدعاءات الوظائف بمعنى أنه يتم تمرير الوسيطات وإرجاع القيم. الاختلاف الوحيد هو أن استدعاءات النظام تعمل على مستوى النواة ، لكن الوظائف لا تعمل. يتم التبديل من وضع المستخدم إلى وضع kernel باستخدام آلية مقاطعة خاصة .
معظم هذه التفاصيل مخفية عن المستخدم في مكتبات النظام (glibc على أنظمة Linux). تعتبر مكالمات النظام عامة بطبيعتها ، ولكن على الرغم من ذلك ، فإن آليات تنفيذها تعتمد إلى حد كبير على الأجهزة.
تستكشف هذه المقالة العديد من الأمثلة العملية لتحليل استدعاءات النظام باستخدام
strace. تستخدم الأمثلة Red Hat Enterprise Linux ، ولكن يجب أن تعمل جميع الأوامر على توزيعات Linux الأخرى أيضًا:
[root@sandbox ~]# cat /etc/redhat-release
Red Hat Enterprise Linux Server release 7.7 (Maipo)
[root@sandbox ~]#
[root@sandbox ~]# uname -r
3.10.0-1062.el7.x86_64
[root@sandbox ~]#
أولاً ، تأكد من تثبيت الأدوات اللازمة على نظامك. يمكنك التحقق مما إذا
straceكان مثبتًا باستخدام الأمر أدناه. لعرض الإصدار ، قم straceبتشغيله باستخدام المعلمة -V:
[root@sandbox ~]# rpm -qa | grep -i strace
strace-4.12-9.el7.x86_64
[root@sandbox ~]#
[root@sandbox ~]# strace -V
strace -- version 4.12
[root@sandbox ~]#
إذا
straceلم يكن مثبتًا ، فثبته عن طريق التشغيل:
yum install strace
على سبيل المثال ، قم بإنشاء دليل اختبار في
/tmpوملفين باستخدام الأمر touch:
[root@sandbox ~]# cd /tmp/
[root@sandbox tmp]#
[root@sandbox tmp]# mkdir testdir
[root@sandbox tmp]#
[root@sandbox tmp]# touch testdir/file1
[root@sandbox tmp]# touch testdir/file2
[root@sandbox tmp]#
(أنا أستخدم الدليل
/tmpفقط لأن كل شخص لديه حق الوصول إليه ، ولكن يمكنك استخدام أي دليل آخر .) تحقق من أن الملفات قد تم إنشاؤها في الدليل
باستخدام الأمر :
lstestdir
[root@sandbox tmp]# ls testdir/
file1 file2
[root@sandbox tmp]#
من المحتمل أنك تستخدم الأمر
lsكل يوم دون أن تدرك أن مكالمات النظام تعمل تحت الغطاء. هذا هو المكان الذي يلعب فيه التجريد. هذه هي الطريقة التي يعمل بها هذا الأمر:
-> (glibc) ->
lsيستدعي
الأمر الوظائف من مكتبات نظام Linux (glibc). هذه المكتبات ، بدورها ، تستدعي مكالمات النظام ، والتي تقوم بمعظم العمل.
إذا كنت تريد معرفة الوظائف التي تم استدعاؤها من مكتبة glibc ، فاستخدم الأمر
ltraceمتبوعًا بالأمر ls testdir/:
ltrace ls testdir/
إذا
ltraceلم يكن مثبتًا ، فقم بتثبيت:
yum install ltrace
سيكون هناك الكثير من المعلومات على الشاشة ، لكن لا تقلق - سنغطي ذلك لاحقًا. فيما يلي بعض وظائف المكتبة المهمة من الإخراج
ltrace:
opendir("testdir/") = { 3 }
readdir({ 3 }) = { 101879119, "." }
readdir({ 3 }) = { 134, ".." }
readdir({ 3 }) = { 101879120, "file1" }
strlen("file1") = 5
memcpy(0x1665be0, "file1\0", 6) = 0x1665be0
readdir({ 3 }) = { 101879122, "file2" }
strlen("file2") = 5
memcpy(0x166dcb0, "file2\0", 6) = 0x166dcb0
readdir({ 3 }) = nil
closedir({ 3 })
من خلال فحص هذا الناتج ، ربما يمكنك فهم ما يحدث. يتم
testdirفتح الدليل المحدد باستخدام وظيفة مكتبة opendir، متبوعة باستدعاءات للوظائف readdirالتي تقرأ محتويات الدليل. أخيرًا ، يتم استدعاء الوظيفة closedirالتي تغلق الدليل المفتوح مسبقًا. في الوقت الحالي ، تجاهل الدالات الأخرى مثل strlenو memcpy.
كما ترى ، من السهل رؤية وظائف المكتبة التي يتم استدعاؤها ، ولكن في هذه المقالة سنركز على استدعاءات النظام التي تستدعيها وظائف مكتبة النظام.
لعرض مكالمات النظام ، استخدم
straceالأمر ls testdirكما هو موضح أدناه. ومرة أخرى تحصل على مجموعة من المعلومات غير المترابطة:
[root@sandbox tmp]# strace ls testdir/
execve("/usr/bin/ls", ["ls", "testdir/"], [/* 40 vars */]) = 0
brk(NULL) = 0x1f12000
<<< truncated strace output >>>
write(1, "file1 file2\n", 13file1 file2
) = 13
close(1) = 0
munmap(0x7fd002c8d000, 4096) = 0
close(2) = 0
exit_group(0) = ?
+++ exited with 0 +++
[root@sandbox tmp]#
نتيجة للتنفيذ ،
straceستتلقى قائمة باستدعاءات النظام التي تم تنفيذها أثناء تنفيذ الأمر ls. يمكن تقسيم جميع مكالمات النظام إلى الفئات التالية:
- ادارة العمليات
- إدارة الملفات
- إدارة نظام الدليل والملفات
- آخر
هناك طريقة مناسبة لتحليل المعلومات المستلمة - اكتب الإخراج إلى ملف باستخدام الخيار
-o.
[root@sandbox tmp]# strace -o trace.log ls testdir/
file1 file2
[root@sandbox tmp]#
هذه المرة ، لن تكون هناك بيانات على الشاشة - سيعمل الأمر
lsكما هو متوقع ، ويعرض قائمة بالملفات ويكتب كل المخرجات straceفي ملف trace.log. لأمر بسيط ، lsيحتوي الملف على ما يقرب من 100 سطر:
[root@sandbox tmp]# ls -l trace.log
-rw-r--r--. 1 root root 7809 Oct 12 13:52 trace.log
[root@sandbox tmp]#
[root@sandbox tmp]# wc -l trace.log
114 trace.log
[root@sandbox tmp]#
ألق نظرة على السطر الأول في الملف
trace.log:
execve("/usr/bin/ls", ["ls", "testdir/"], [/* 40 vars */]) = 0
- في بداية السطر يوجد اسم استدعاء النظام الذي يتم تنفيذه - execve.
- النص الموجود بين قوسين هو الوسيطات التي تم تمريرها إلى استدعاء النظام.
- الرقم بعد علامة = (في هذه الحالة ، 0) هو القيمة التي يتم إرجاعها بواسطة استدعاء النظام.
الآن النتيجة لا تبدو مخيفة للغاية ، أليس كذلك؟ ويمكنك تطبيق نفس المنطق على الأسطر الأخرى أيضًا.
انتبه للأمر الوحيد الذي اتصلت به -
ls testdir. أنت تعرف اسم الدليل الذي يستخدمه الأمر ls، فلماذا لا تستخدم grepلـ testdirفي الملف trace.logوترى ما سيجده؟ انظر عن كثب إلى النتيجة:
[root@sandbox tmp]# grep testdir trace.log
execve("/usr/bin/ls", ["ls", "testdir/"], [/* 40 vars */]) = 0
stat("testdir/", {st_mode=S_IFDIR|0755, st_size=32, ...}) = 0
openat(AT_FDCWD, "testdir/", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 3
[root@sandbox tmp]#
بالعودة إلى التحليل أعلاه
execve، هل يمكنك معرفة ما تفعله مكالمة النظام التالية؟
execve("/usr/bin/ls", ["ls", "testdir/"], [/* 40 vars */]) = 0
لا يتعين عليك تذكر جميع مكالمات النظام وماذا يفعلون: كل شيء موجود في الوثائق. صفحات الرجل في عجلة من أمرها للمساعدة! تأكد من تثبيت الحزمة قبل تشغيل الأمر man
man-pages:
[root@sandbox tmp]# rpm -qa | grep -i man-pages
man-pages-3.53-5.el7.noarch
[root@sandbox tmp]#
تذكر أنك تحتاج إلى إضافة "2" بين الأمر
manواسم syscall. إذا قرأت manعن man( man man) ، فسترى أن القسم 2 محجوز لمكالمات النظام. وبالمثل ، إذا كنت بحاجة إلى معلومات حول وظائف المكتبة ، فأنت بحاجة إلى إضافة 3 manواسم وظيفة المكتبة.
فيما يلي أرقام الأقسام
man:
1. .
2. (, ).
3. ( ).
4. ( /dev).
لعرض وثائق مكالمة النظام ، قم بتشغيل رجل باسم مكالمة النظام تلك.
man 2 execve
وفقًا للوثائق ،
execveيقوم استدعاء النظام بتنفيذ البرنامج الذي يتم تمريره إليه في المعلمات (في هذه الحالة يكون كذلك ls). يتم أيضًا تمرير معلمات إضافية لـ ls إليه. في هذا المثال ، هو testdir. وبالتالي، هذا استدعاء نظام يعمل ببساطة lsمع testdirكمعلمة:
'execve - execute program'
'DESCRIPTION
execve() executes the program pointed to by filename'
statيتم تمرير معلمة
استدعاء النظام التالية testdir:
stat("testdir/", {st_mode=S_IFDIR|0755, st_size=32, ...}) = 0
لعرض الوثائق استخدم
man 2 stat. يقوم استدعاء نظام stat بإرجاع معلومات حول الملف المحدد. تذكر أن كل شيء في Linux عبارة عن ملف ، بما في ذلك الأدلة.
بعد ذلك ، يتم
openatفتح مكالمة النظام testdir. لاحظ أن القيمة المرجعة هي 3. هذا هو واصف الملف الذي سيتم استخدامه في استدعاءات النظام اللاحقة:
openat(AT_FDCWD, "testdir/", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 3
افتح الملف الآن
trace.logولاحظ الخط الذي يلي مكالمة النظام openat. سترى مكالمة نظام getdentsتقوم بمعظم العمل المطلوب لتنفيذ الأمر ls testdir. الآن دعنا ننفذ grep getdentsللملف trace.log:
[root@sandbox tmp]# grep getdents trace.log
getdents(3, /* 4 entries */, 32768) = 112
getdents(3, /* 0 entries */, 32768) = 0
[root@sandbox tmp]#
تشير التوثيق (
man getdents) إلى أنه getdentsيقرأ إدخالات الدليل ، وهو ما نحتاجه بالفعل. لاحظ أن الوسيطة لـ getdent3 هي واصف الملف الذي تم الحصول عليه مسبقًا من استدعاء النظام openat.
الآن وقد تم استلام محتويات الدليل ، نحتاج إلى طريقة لعرض المعلومات في الجهاز. لذلك ، نقوم
grepباستدعاء نظام آخر write، والذي يستخدم لإخراج الجهاز:
[root@sandbox tmp]# grep write trace.log
write(1, "file1 file2\n", 13) = 13
[root@sandbox tmp]#
في الوسائط ، يمكنك رؤية أسماء الملفات المراد إخراجها:
file1و file2. بالنسبة إلى الوسيطة الأولى (1) ، تذكر أنه في نظام Linux ، يتم فتح ثلاثة واصفات ملفات افتراضيًا لأي عملية:
- 0 - تيار الإدخال القياسي
- 1 - تيار الإخراج القياسي
- 2 - تيار الخطأ القياسي
وهكذا، فإن استدعاء النظام
writeيأخذ file1و file2الإخراج القياسي، وهو المحطة، يدل الرقم 1.
الآن عليك أن تعرف ما يسميه النظام القيام به أكثر من عمل للفريق
ls testdir/. ولكن ماذا عن مكالمات النظام الأخرى التي يزيد عددها عن 100 في الملف trace.log؟
يقوم نظام التشغيل بالكثير من الأشياء الداعمة لبدء العملية ، لذا فإن الكثير مما تراه في الملف
trace.logهو تهيئة العملية وتنظيفها. ألق نظرة كاملة على ملف trace.log وحاول فهم ما يحدث عند تشغيل الأمر ls.
يمكنك الآن تحليل مكالمات النظام لأي برنامج. توفر الأداة المساعدة strace أيضًا العديد من خيارات سطر الأوامر المفيدة ، والتي يتم وصف بعضها أدناه.
بشكل افتراضي
strace، لا يعرض جميع المعلومات حول مكالمات النظام. ومع ذلك ، فإنه يحتوي على خيار -v verboseيعرض معلومات إضافية حول كل استدعاء للنظام:
strace -v ls testdir
من الممارسات الجيدة استخدام معلمة
-fلتتبع العمليات الفرعية التي تم إنشاؤها بواسطة عملية قيد التشغيل:
strace -f ls testdir
ماذا لو كنت تريد فقط أسماء مكالمات النظام وعدد مرات تشغيلها ونسبة الوقت المستغرق في التنفيذ؟ يمكنك استخدام الخيار
-cللحصول على هذه الإحصائيات:
strace -c ls testdir/
إذا كنت ترغب في تتبع مكالمة نظام معينة ، على سبيل المثال ،
openوتجاهل الآخرين ، فيمكنك استخدام الخيار -eمع اسم مكالمة النظام:
[root@sandbox tmp]# strace -e open ls testdir
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libselinux.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libcap.so.2", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libacl.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libpcre.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libdl.so.2", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libattr.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libpthread.so.0", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
file1 file2
+++ exited with 0 +++
[root@sandbox tmp]#
ماذا لو كنت بحاجة إلى التصفية حسب مكالمات النظام المتعددة؟ لا تقلق ، يمكنك استخدام نفس الخيار
-eوفصل مكالمات النظام المطلوبة بفاصلة. على سبيل المثال ، لـ writeو getdent:
[root@sandbox tmp]# strace -e write,getdents ls testdir
getdents(3, /* 4 entries */, 32768) = 112
getdents(3, /* 0 entries */, 32768) = 0
write(1, "file1 file2\n", 13file1 file2
) = 13
+++ exited with 0 +++
[root@sandbox tmp]#
حتى الآن ، قمنا فقط بتتبع عمليات تشغيل الأوامر الصريحة. ولكن ماذا عن الأوامر التي تم تشغيلها في وقت سابق؟ ماذا لو كنت تريد تعقب الشياطين؟ لهذا ،
straceلديك خيار خاص -pيمكنك من خلاله تمرير معرف العملية.
لن نبدأ البرنامج الخفي ، لكننا نستخدم أمرًا
catيعرض محتويات الملف الذي تم تمريره إليه كوسيطة. ولكن إذا لم تحدد وسيطة ، فسينتظر الأمر catببساطة وصول إدخال من المستخدم. بعد إدخال النص ، سيعرض النص المدخل على الشاشة. وهكذا حتى ينقر المستخدم Ctrl+Cللخروج.
قم بتشغيل الأمر
catعلى محطة واحدة.
[root@sandbox tmp]# cat
في محطة طرفية أخرى ، ابحث عن معرف العملية (PID) باستخدام الأمر
ps:
[root@sandbox ~]# ps -ef | grep cat
root 22443 20164 0 14:19 pts/0 00:00:00 cat
root 22482 20300 0 14:20 pts/1 00:00:00 grep --color=auto cat
[root@sandbox ~]#
ابدأ الآن
straceبالخيار -pو PID الذي وجدته باستخدام ps. بعد البدء ، straceسيعرض معلومات حول العملية التي اتصل بها ، بالإضافة إلى PID الخاص به. straceيراقب الآن مكالمات النظام التي أجراها الأمر cat. تتم قراءة أول مكالمة نظام ستراها ، في انتظار الإدخال من مؤشر الترابط 0 ، أي من الإدخال القياسي ، وهو الآن المحطة التي يعمل الأمر عليها cat:
[root@sandbox ~]# strace -p 22443
strace: Process 22443 attached
read(0,
عد الآن إلى المحطة حيث تركت الأمر قيد التشغيل
catوأدخل بعض النص. للتظاهر ، دخلت x0x0. يرجى ملاحظة أنني catكررت ما قمت بإدخاله x0x0وستظهر الشاشة مرتين.
[root@sandbox tmp]# cat
x0x0
x0x0
ارجع إلى المحطة حيث قمت
straceبالاتصال بالعملية cat. الآن ترى مكالمتين جديدتين للنظام: السابقة read، التي تمت قراءتها الآن x0x0، وواحدة أخرى للكتابة write، والتي x0x0تعيد الكتابة إلى الجهاز ، ومرة أخرى ، مكالمة جديدة readتنتظر القراءة من الجهاز. لاحظ أن الإدخال القياسي (0) والإخراج القياسي (1) موجودان على نفس الجهاز:
[root@sandbox ~]# strace -p 22443
strace: Process 22443 attached
read(0, "x0x0\n", 65536) = 5
write(1, "x0x0\n", 5) = 5
read(0,
تخيل فوائد إطلاق
straceالشياطين: يمكنك رؤية كل ما يحدث في الخلفية. أكمل الأمرcatبالضغط Ctrl+C... سيؤدي هذا أيضًا إلى إنهاء الجلسةstraceمنذ إنهاء العملية المراقبة.
لعرض الطوابع الزمنية لمكالمات النظام ، استخدم الخيار
-t:
[root@sandbox ~]#strace -t ls testdir/
14:24:47 execve("/usr/bin/ls", ["ls", "testdir/"], [/* 40 vars */]) = 0
14:24:47 brk(NULL) = 0x1f07000
14:24:47 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f2530bc8000
14:24:47 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
14:24:47 open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
ماذا لو كنت تريد معرفة الوقت المستغرق بين مكالمات النظام؟ هناك خيار مفيد
-rيعرض الوقت المستغرق لإكمال كل مكالمة نظام. مفيد جدا ، أليس كذلك؟
[root@sandbox ~]#strace -r ls testdir/
0.000000 execve("/usr/bin/ls", ["ls", "testdir/"], [/* 40 vars */]) = 0
0.000368 brk(NULL) = 0x1966000
0.000073 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb6b1155000
0.000047 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
0.000119 open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
خاتمة
الأداة
straceمفيدة جدًا لتعلم مكالمات النظام في Linux. للحصول على خيارات سطر الأوامر الأخرى ، راجع الوثائق البشرية والمتصلة بالإنترنت.
