
مهمتي الرئيسية هي ، في معظم الأحيان ، نشر أنظمة البرمجيات ، أي أقضي الكثير من الوقت في محاولة للإجابة على هذه الأسئلة:
- هذا البرنامج يعمل لصالح المطور ، ولكن ليس بالنسبة لي. لماذا؟
- بالأمس كان هذا البرنامج يعمل بالنسبة لي ، ولكن ليس اليوم. لماذا؟
هذا نوع من التصحيح يختلف قليلاً عن تصحيح البرامج العادي. تصحيح الأخطاء العادي هو حول منطق الكود ، لكن تصحيح النشر يتعلق بالكود والتفاعل البيئي. حتى إذا كان جذر المشكلة هو خطأ منطقي ، فإن حقيقة أن كل شيء يعمل على جهاز واحد وليس على جهاز آخر يعني أن الأمر في البيئة بطريقة أو بأخرى.
لذا بدلاً من أدوات تصحيح الأخطاء المعتادة مثل gdb ، لدي مجموعة مختلفة من الأدوات لتصحيح النشر. وأداة المفضلة للتعامل مع مشكلة مثل "لماذا لا يحرث هذا البرنامج؟" دعا strace .
ما هو الشرائط؟
strace هي أداة لتتبع مكالمة النظام. تم إنشاؤه في البداية في نظام Linux ، ولكن يمكن تدوير نفس شرائح تصحيح الأخطاء باستخدام أدوات للأنظمة الأخرى ( DTrace أو ktrace ).
التطبيق الرئيسي بسيط جدا. تحتاج فقط إلى تشغيل strace باستخدام أي أمر ، وسوف ترسل جميع مكالمات النظام إلى تفريغ (على الرغم من ذلك ، أولاً ، ربما عليك تثبيت strace نفسه):
$ strace echo Hello ...Snip lots of stuff... write(1, "Hello\n", 6) = 6 close(1) = 0 close(2) = 0 exit_group(0) = ? +++ exited with 0 +++
ما هي هذه المكالمات النظام؟ إنه نوع من واجهة برمجة التطبيقات لنواة نظام التشغيل. ذات مرة ، كان للبرنامج وصول مباشر إلى الأجهزة التي يعمل عليها. على سبيل المثال ، إذا كنت بحاجة إلى عرض شيء ما على الشاشة ، يتم تشغيله باستخدام المنافذ و / أو سجلات الذاكرة لأجهزة الفيديو. عندما أصبحت أنظمة الكمبيوتر متعددة المهام شائعة ، سادت الفوضى لأن العديد من التطبيقات كانت تقاتل من أجل الأجهزة. قد تؤدي الأخطاء في أحد التطبيقات إلى إيقاف عمل الآخرين ، إن لم يكن النظام بأكمله. ثم ظهرت أوضاع الامتياز (أو "حماية الحلقة") في وحدة المعالجة المركزية. أصبح kernel هو الأكثر امتيازًا: فقد حصل على حق الوصول الكامل إلى الأجهزة ، مما أدى إلى إنشاء تطبيقات أقل امتيازًا والتي كان عليها بالفعل طلب الوصول من kernel للتفاعل مع الأجهزة - من خلال مكالمات النظام.
على المستوى الثنائي ، تختلف مكالمة النظام قليلاً عن استدعاء دالة بسيطة ، ومع ذلك تستخدم معظم البرامج مجمّعًا في المكتبة القياسية. أي تحتوي المكتبة القياسية POSIX C على استدعاء دالة write () ، والتي تحتوي على جميع التعليمات البرمجية الخاصة بالعمارة لاستدعاء نظام الكتابة .

باختصار ، يتم أي تفاعل بين التطبيق وبيئته (أنظمة الكمبيوتر) من خلال مكالمات النظام. لذلك ، عندما يعمل البرنامج على جهاز واحد وليس على جهاز آخر ، سيكون من الجميل النظر إلى نتائج مكالمات نظام التتبع. لتكون أكثر تحديدًا ، فيما يلي قائمة بالنقاط النموذجية التي يمكن تحليلها باستخدام تتبع مكالمات النظام:
- وحدة التحكم I / O
- شبكة الإدخال / الإخراج
- الوصول إلى نظام الملفات وملف I / O
- عملية / إدارة موضوع العمر
- إدارة الذاكرة منخفضة المستوى
- الوصول إلى برامج تشغيل جهاز معين
متى تستخدم الشرائط؟
من الناحية النظرية ، يتم استخدام strace مع أي برامج في مساحة المستخدم ، لأن أي برنامج في مساحة المستخدم يجب أن يقوم بإجراء مكالمات النظام. إنه يعمل بكفاءة أكبر مع البرامج المترجمة ذات المستوى المنخفض ، لكنه يعمل أيضًا مع لغات المستوى الأعلى مثل Python إذا كان بإمكانك الحصول على ضوضاء إضافية من وقت التشغيل والمترجم الفوري.
في كل أشكاله الرائعة ، يظهر الشريط الثابت أثناء تصحيح أخطاء البرامج التي تعمل بشكل جيد على جهاز واحد ، ولكنه يتوقف فجأة عن العمل على جهاز آخر ، مع إعطاء رسائل مضللة حول الملفات أو الأذونات أو المحاولات الفاشلة لتنفيذ بعض الأوامر أو شيء ما ... إنه أمر مؤسف ، ولكنه ليس جيدًا. يتم دمجها مع مشكلات عالية المستوى مثل أخطاء التحقق من الشهادة. يتطلب هذا عادةً مزيجًا من أدوات الضبط ، وأحيانًا ltrace ، والأدوات ذات المستوى الأعلى (مثل أداة سطر الأوامر openssl لتصحيح أخطاء الشهادة).
على سبيل المثال ، نأخذ العمل على خادم مستقل ، لكن غالبًا ما يمكن إجراء مكالمات نظام التتبع على منصات نشر أكثر تعقيدًا. تحتاج فقط إلى اختيار مجموعة الأدوات الصحيحة.
مثال تصحيح بسيط
لنفترض أنك تريد تشغيل تطبيق خادم فو رائع ، ولكن اتضح أن هذا:
$ foo Error opening configuration file: No such file or directory
من الواضح أنه لم يتمكن من العثور على ملف التكوين الذي كتبته. يحدث هذا لأنه في بعض الأحيان ، عندما يقوم مديرو الحزم بتجميع تطبيق ما ، فإنهم يتخطون الموقع المتوقع للملفات. وإذا اتبعت دليل التثبيت لأحد التوزيعات ، في ملف آخر ، يمكنك العثور على الملفات بالكامل وليس حيث كنت أتوقع. سيكون من الممكن حل المشكلة في بضع ثوان إذا كانت رسالة الخطأ تشير إلى مكان البحث عن ملف التكوين ، لكنها لا تذكر. لذلك أن ننظر فيها؟
إذا كان لديك وصول إلى شفرة المصدر ، يمكنك قراءتها ومعرفة. خطة احتياطية جيدة ، ولكن ليس الحل الأسرع. يمكنك اللجوء إلى مصحح الأخطاء خطوة بخطوة مثل gdb ومعرفة ما يفعله البرنامج ، لكن استخدام أداة مصممة خصيصًا لإظهار التفاعل مع البيئة هي الأكثر فعالية بكثير .
قد يبدو استنتاج الضيق غير ضروري ، لكن الخبر السار هو أنه يمكن تجاهل معظمه بأمان. من المفيد غالبًا استخدام عامل التشغيل -o لحفظ نتائج التتبع في ملف منفصل:
$ strace -o /tmp/trace foo Error opening configuration file: No such file or directory $ cat /tmp/trace execve("foo", ["foo"], 0x7ffce98dc010 /* 16 vars */) = 0 brk(NULL) = 0x56363b3fb000 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=25186, ...}) = 0 mmap(NULL, 25186, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f2f12cf1000 close(3) = 0 openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\260A\2\0\0\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=1824496, ...}) = 0 mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f2f12cef000 mmap(NULL, 1837056, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f2f12b2e000 mprotect(0x7f2f12b50000, 1658880, PROT_NONE) = 0 mmap(0x7f2f12b50000, 1343488, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x22000) = 0x7f2f12b50000 mmap(0x7f2f12c98000, 311296, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x16a000) = 0x7f2f12c98000 mmap(0x7f2f12ce5000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1b6000) = 0x7f2f12ce5000 mmap(0x7f2f12ceb000, 14336, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f2f12ceb000 close(3) = 0 arch_prctl(ARCH_SET_FS, 0x7f2f12cf0500) = 0 mprotect(0x7f2f12ce5000, 16384, PROT_READ) = 0 mprotect(0x56363b08b000, 4096, PROT_READ) = 0 mprotect(0x7f2f12d1f000, 4096, PROT_READ) = 0 munmap(0x7f2f12cf1000, 25186) = 0 openat(AT_FDCWD, "/etc/foo/config.json", O_RDONLY) = -1 ENOENT (No such file or directory) dup(2) = 3 fcntl(3, F_GETFL) = 0x2 (flags O_RDWR) brk(NULL) = 0x56363b3fb000 brk(0x56363b41c000) = 0x56363b41c000 fstat(3, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0x8), ...}) = 0 write(3, "Error opening configuration file"..., 60) = 60 close(3) = 0 exit_group(1) = ? +++ exited with 1 +++
حول الصفحة الأولى بأكملها من إخراج شرائط عادة ما يكون إعداد منخفض المستوى للإطلاق. (هناك العديد من دعوات mmap و mprotect و brk لأشياء مثل اكتشاف الذاكرة منخفضة المستوى وعرض المكتبات الديناميكية.) في الواقع ، أثناء تصحيح الأخطاء ، من الأفضل قراءة مخرجات الأشرطة من النهاية. في الجزء السفلي ، هناك مكالمة للكتابة ، والتي تعرض رسالة خطأ. ننظر أعلاه ونرى أول استدعاء للنظام الخاطئ - مكالمة openat التي تلقي خطأ ENOENT ("لم يتم العثور على ملف أو دليل") ، في محاولة لفتح /etc/foo/config.json . هنا ، هنا يجب أن يكمن ملف التكوين.
كان مجرد مثال ، لكنني أود أن أقول إن 90٪ من الوقت الذي أستخدم فيه الضيق ، ليس هناك ما هو أكثر صعوبة ولا يجب القيام به. يوجد أدناه دليل تصحيح خطوة بخطوة كامل:
- بالاحباط من رسالة خطأ ص- النظام من برنامج
- أعد تشغيل البرنامج بإحكام
- العثور على رسالة خطأ في نتائج التتبع
- انتقل أعلى حتى تصطدم أول مكالمة النظام الفاشلة
من المحتمل جدًا أن يُظهر استدعاء النظام في الخطوة 4 الخطأ الذي حدث.
نصائح
قبل عرض مثال على تصحيح الأخطاء الأكثر تعقيدًا ، سأخبرك ببعض الحيل لاستخدام الشرائط بشكل فعال:
الرجل صديقك
في العديد من أنظمة * nix ، يمكن الحصول على قائمة كاملة بمكالمات نظام kernel عن طريق تشغيل أنظمة syscalls الخاصة بالرجل . سترى أشياء مثل brk (2) ، مما يعني أنه يمكنك الحصول على مزيد من المعلومات عن طريق تشغيل man 2 brk .
أشعل النار صغيرًا: يُظهر لي الرجل الثاني شوكة صفحة لشوكة الشوكة () في GNU libc ، والتي تبين أنها تنفذ باستخدام استدعاء clone () . تظل دلالات دلالة اتصال المكالمة على حالها إذا كتبت برنامجًا يستخدم fork () وبدء التتبع - لن أجد مكالمات fork ، فبدلاً من ذلك سيتم استنساخها () . يتم الخلط بين مثل هذا أشعل النار فقط إذا بدأت مقارنة المصدر مع إخراج الشرائط .
استخدم -o لحفظ الإخراج إلى ملف
يمكن للكتلة أن تولد مخرجات كبيرة ، لذلك من المفيد تخزين نتائج التتبع في ملفات منفصلة (كما في المثال أعلاه). ويساعد على عدم الخلط بين إخراج البرنامج والإخراج الدقيق في وحدة التحكم.
استخدم -s لعرض المزيد من بيانات الوسيطة
ربما لاحظت أن النصف الثاني من رسالة الخطأ لا يظهر في مثال التتبع أعلاه. وهذا بسبب إظهار strace فقط 32 بايت الأولى من وسيطة السلسلة بشكل افتراضي. إذا كنت ترغب في رؤية المزيد ، فأضف شيئًا مثل -s 128 إلى المكالمة القصيرة .
- يسهل تتبع ملفات \ المقابس \ وهلم جرا.
"كل شيء ملف" يعني أن أنظمة * nix تؤدي جميع I / O باستخدام واصفات الملفات ، سواء كان ذلك ينطبق على ملف أو شبكة ، أو على قنوات المعالجة. يعد هذا مناسبًا للبرمجة ، ولكنه يجعل من الصعب تتبع ما يحدث بالفعل عندما ترى القراءة والكتابة العامة في نتائج التتبع لمكالمة النظام.
عن طريق إضافة عامل التشغيل -u ، يجب عليك فرض تعليق توضيحي على كل واصف ملف في الإخراج مع ملاحظة ما يشير إليه.
إرفاق عملية قيد التشغيل بالفعل مع -p **
كما سيتضح من المثال أدناه ، في بعض الأحيان تحتاج إلى تتبع برنامج قيد التشغيل بالفعل. إذا كنت تعرف أنه يعمل كعملية 1337 (على سبيل المثال ، من استنتاجات ps ) ، فيمكنك تتبعها كما يلي:
$ strace -p 1337 ...system call trace output...
ربما تحتاج إلى امتيازات الجذر.
استخدم -f لمراقبة العمليات الفرعية
strace افتراضيا يتتبع عملية واحدة فقط. إذا ولدت هذه العملية عمليات تابعة ، فبإمكانك رؤية استدعاء النظام لتوليد العملية التابعة ، ولكن لن يتم عرض مكالمات النظام الخاصة بالعملية الفرعية.
إذا كنت تعتقد أن الخطأ يقع في العملية الفرعية ، فاستخدم عامل التشغيل -f ، فسيمكن ذلك التتبع الخاص به. الجانب السلبي لهذا هو أن الخلاصة سوف تربك أكثر. عندما يتتبع الشريط عملية واحدة أو خيط واحد ، فإنه يعرض دفقًا واحدًا من أحداث الاتصال. عندما يتتبع عدة عمليات في وقت واحد ، فمن المحتمل أن تشاهد بداية المكالمة التي تمت مقاطعتها بواسطة الرسالة <غير المكتملة ...> ، ثم مجموعة من المكالمات لفروع التنفيذ الأخرى ، وعندها فقط يتم استئناف نهاية الأولى مع <... foocall> . أو ، قم بفصل كل نتائج التتبع إلى ملفات مختلفة ، باستخدام عامل التشغيل -ff أيضًا ( انظر دليل التثبيت لمعرفة التفاصيل).
تصفية التتبع مع -e
كما ترون ، نتيجة التتبع هي مجموعة حقيقية من جميع مكالمات النظام الممكنة. باستخدام علامة -e ، يمكنك تصفية التتبع (انظر دليل الشريط ). الميزة الرئيسية هي أن تشغيل تتبع مع التصفية أسرع من القيام بعملية تتبع كاملة ، ثم grep . أن نكون صادقين ، وأنا دائما تقريبا لا أهتم.
ليست كل الأخطاء سيئة
مثال بسيط وشائع هو برنامج يبحث عن ملف في عدة أماكن في وقت واحد ، مثل shell الذي يبحث عنه ، حيث تحتوي السلة / الدليل على ملف قابل للتنفيذ:
$ strace sh -c uname ... stat("/home/user/bin/uname", 0x7ffceb817820) = -1 ENOENT (No such file or directory) stat("/usr/local/bin/uname", 0x7ffceb817820) = -1 ENOENT (No such file or directory) stat("/usr/bin/uname", {st_mode=S_IFREG|0755, st_size=39584, ...}) = 0 ...
يعتبر "آخر طلب فاشل قبل رسالة الخطأ" أمرًا مفيدًا في العثور على الأخطاء ذات الصلة. مهما كان الأمر ، فمن المنطقي أن نبدأ من النهاية.
دليل البرمجة C يساعد في فهم مكالمات النظام
الاستدعاءات القياسية إلى مكتبات C ليست استدعاءات النظام ، ولكن فقط طبقة سطحية رقيقة. لذا ، إذا فهمت قليلاً على الأقل كيف وماذا تفعل في C ، فسيكون من الأسهل بالنسبة لك فهم نتائج تتبع مكالمة النظام. على سبيل المثال ، إذا كنت تواجه مشكلة في تصحيح المكالمات إلى الأنظمة المتصلة بالشبكة ، فتحقق من "دليل برمجة الشبكة" الكلاسيكي في بيجا .
مثال تصحيح أكثر تعقيدًا
لقد سبق أن قلت أن مثال تصحيح الأخطاء البسيط هو مثال لشيء يجب علي ، في معظم الحالات ، التعامل معه بدقة . ومع ذلك ، في بعض الأحيان يتطلب الأمر إجراء تحقيق حقيقي ، لذلك إليك مثال حقيقي لتصحيح الأخطاء أكثر تعقيدًا.
bcron هو جدولة معالجة المهام ، تطبيق آخر من البرنامج الخفي * nix cron . تم تثبيته على الخادم ، ولكن عندما يحاول شخص ما تعديل الجدول ، هذا ما يحدث:
# crontab -e -u logs bcrontab: Fatal: Could not create temporary file
حسنًا ، حاول bcron كتابة ملف معين ، لكنه لم ينجح ولا يعترف بالسبب. كشف الشرائط :
# strace -o /tmp/trace crontab -e -u logs bcrontab: Fatal: Could not create temporary file # cat /tmp/trace ... openat(AT_FDCWD, "bcrontab.14779.1573691864.847933", O_RDONLY) = 3 mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f82049b4000 read(3, "#Ansible: logsagg\n20 14 * * * lo"..., 8192) = 150 read(3, "", 8192) = 0 munmap(0x7f82049b4000, 8192) = 0 close(3) = 0 socket(AF_UNIX, SOCK_STREAM, 0) = 3 connect(3, {sa_family=AF_UNIX, sun_path="/var/run/bcron-spool"}, 110) = 0 mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f82049b4000 write(3, "156:Slogs\0#Ansible: logsagg\n20 1"..., 161) = 161 read(3, "32:ZCould not create temporary f"..., 8192) = 36 munmap(0x7f82049b4000, 8192) = 0 close(3) = 0 write(2, "bcrontab: Fatal: Could not creat"..., 49) = 49 unlink("bcrontab.14779.1573691864.847933") = 0 exit_group(111) = ? +++ exited with 111 +++
في النهاية ، توجد رسالة خطأ للكتابة ، لكن هذه المرة هناك شيء مختلف. أولاً ، لا يوجد خطأ في مكالمة النظام ذات الصلة يحدث عادة قبل هذا. ثانياً ، من الواضح أن شخصًا ما قد قرأ بالفعل رسالة الخطأ. يبدو أن المشكلة الحقيقية تكمن في مكان آخر ، ويقوم bcrontab فقط بتشغيل الرسالة.
إذا نظرت إلى قراءة الرجل 2 ، يمكنك أن ترى أن الوسيطة الأولى (3) هي واصف الملف الذي يستخدمه * nix لجميع عمليات الإدخال / الإخراج. كيفية معرفة ما يمثل واصف الملف 3؟ في هذه الحالة بالذات ، يمكنك الركض باستخدام عامل التشغيل -u (انظر أعلاه) ، وسيخبرك تلقائيًا ، ومع ذلك ، من أجل حساب مثل هذه الأشياء ، من المفيد معرفة كيفية قراءة نتائج التتبع وتحليلها.
يمكن أن يكون مصدر واصف الملف واحدًا من العديد من استدعاءات النظام (كل هذا يتوقف على ما هو الواصف لوحدة التحكم أو مقبس الشبكة أو الملف نفسه أو أي شيء آخر) ، ولكن على الرغم من ذلك ، نحن نبحث عن مكالمات ترجع 3 (t ابحث عن "= 3" في نتائج التتبع). نتيجة لذلك ، هناك 2 منهم: openat في الأعلى ومقبس في الوسط. openat يفتح الملف ، ولكن إغلاق (3) بعد ذلك سوف يظهر أنه يغلق مرة أخرى. (أشعل النار: يمكن إعادة استخدام واصفات الملفات عند فتحها وإغلاقها). مكالمة مأخذ التوصيل () مناسبة ، لأنها الأخيرة قبل القراءة () ، وتبين أن bcrontab يعمل مع شيء ما عبر مأخذ التوصيل. يوضح السطر التالي أن واصف الملف مرتبط بمقبس مجال يونكس على طول المسار / var / run / bcron-spool .
لذلك ، تحتاج إلى العثور على العملية المرتبطة بمقبس يونيكس من ناحية أخرى. هناك بعض الحيل أنيق لهذا الغرض ، وكلاهما مفيد في تصحيح نشر الخادم. الأول هو استخدام netstat أو أحدث ss (حالة مأخذ التوصيل). يعرض كلا الأمرين اتصالات شبكة الاتصال النشطة للنظام واتخاذ عامل التشغيل - l لوصف مآخذ الاستماع ، ومشغل -p لعرض البرامج المتصلة بالمقبس كعميل. (هناك العديد من الخيارات المفيدة ، لكن هذين الخيارين كافيين لهذه المهمة.)
# ss -pl | grep /var/run/bcron-spool u_str LISTEN 0 128 /var/run/bcron-spool 1466637 * 0 users:(("unixserver",pid=20629,fd=3))
يشير هذا إلى أن المستمع هو أمر inixserver يعمل بمعرف العملية 20629. (ومن قبيل الصدفة ، يستخدم واصف الملف 3 كمقبس.)
أما الأداة الثانية المفيدة حقًا للعثور على نفس المعلومات فتسمى lsof . يسرد جميع الملفات المفتوحة (أو واصفات الملفات) في النظام. أو يمكنك الحصول على معلومات حول ملف واحد محدد:
# lsof /var/run/bcron-spool COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME unixserve 20629 cron 3u unix 0x000000005ac4bd83 0t0 1466637 /var/run/bcron-spool type=STREAM
عملية 20629 هي خادم طويل العمر ، لذلك يمكنك إرفاق شريط به باستخدام شيء مثل strace -o / tmp / trace -p 20629 . إذا قمنا بتحرير مهمة cron في محطة أخرى ، فسنحصل على ناتج نتائج التتبع بخطأ. وهنا النتيجة:
accept(3, NULL, NULL) = 4 clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7faa47c44810) = 21181 close(4) = 0 accept(3, NULL, NULL) = ? ERESTARTSYS (To be restarted if SA_RESTART is set) --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=21181, si_uid=998, si_status=0, si_utime=0, si_stime=0} --- wait4(0, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], WNOHANG|WSTOPPED, NULL) = 21181 wait4(0, 0x7ffe6bc36764, WNOHANG|WSTOPPED, NULL) = -1 ECHILD (No child processes) rt_sigaction(SIGCHLD, {sa_handler=0x55d244bdb690, sa_mask=[CHLD], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7faa47ab9840}, {sa_handler=0x55d244bdb690, sa_mask=[CHLD], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7faa47ab9840}, 8) = 0 rt_sigreturn({mask=[]}) = 43 accept(3, NULL, NULL) = 4 clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7faa47c44810) = 21200 close(4) = 0 accept(3, NULL, NULL) = ? ERESTARTSYS (To be restarted if SA_RESTART is set) --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=21200, si_uid=998, si_status=111, si_utime=0, si_stime=0} --- wait4(0, [{WIFEXITED(s) && WEXITSTATUS(s) == 111}], WNOHANG|WSTOPPED, NULL) = 21200 wait4(0, 0x7ffe6bc36764, WNOHANG|WSTOPPED, NULL) = -1 ECHILD (No child processes) rt_sigaction(SIGCHLD, {sa_handler=0x55d244bdb690, sa_mask=[CHLD], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7faa47ab9840}, {sa_handler=0x55d244bdb690, sa_mask=[CHLD], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7faa47ab9840}, 8) = 0 rt_sigreturn({mask=[]}) = 43 accept(3, NULL, NULL
(لن يتم قبول آخر قبول () عند التتبع.) ومرة أخرى ، للأسف ، لا تحتوي هذه النتيجة على الخطأ الذي نبحث عنه. لا نرى أي رسائل ترسلها bcrontag إلى مأخذ توصيل أو تتلقاها. بدلاً من ذلك ، يمكنك التحكم الكامل في العملية ( استنساخ أو انتظار 4 أو SIGCHLD أو ما إلى ذلك). تؤدي هذه العملية إلى إنشاء عملية تابعة ، والتي ، كما تعتقد ، تقوم بالعمل الحقيقي. وإذا كنت بحاجة لالتقاط أثرها ، فأضفها إلى المكالمة. إليك ما نعثر عليه من خلال البحث عن رسالة الخطأ في النتيجة الجديدة باستخدام strace -f -o / tmp / trace -p 20629 :
21470 openat(AT_FDCWD, "tmp/spool.21470.1573692319.854640", O_RDWR|O_CREAT|O_EXCL, 0600) = -1 EACCES (Permission denied) 21470 write(1, "32:ZCould not create temporary f"..., 36) = 36 21470 write(2, "bcron-spool[21470]: Fatal: logs:"..., 84) = 84 21470 unlink("tmp/spool.21470.1573692319.854640") = -1 ENOENT (No such file or directory) 21470 exit_group(111) = ? 21470 +++ exited with 111 +++
الآن ، هذا شيء. تتلقى العملية 21470 خطأ "تم رفض الوصول" عند محاولة إنشاء ملف على المسار tmp / spool.21470.1573692319.854640 (بالإشارة إلى دليل العمل الحالي). إذا عرفنا ببساطة دليل العمل الحالي ، لكنا عرفنا المسار الكامل ويمكننا معرفة سبب عدم تمكن العملية من إنشاء ملفها المؤقت الخاص بها. لسوء الحظ ، تم إنهاء العملية بالفعل ، لذلك لا يمكنك فقط استخدام lsof -p 21470 للعثور على الدليل الحالي ، ولكن يمكنك العمل في الاتجاه المعاكس - ابحث عن مكالمات نظام PID 21470 التي تغير الدليل. (إذا لم يكن هناك أي شيء ، يجب أن يكون PID 21470 قد ورثها من الأصل ، ولا يمكن معرفة ذلك من خلال lsof -p .) استدعاء النظام هذا هو chdir (والذي يسهل اكتشافه بمساعدة محركات بحث الشبكة الحديثة). وإليكم نتيجة عمليات البحث العكسي استنادًا إلى نتائج التتبع ، حتى خادم PID 20629 نفسه:
20629 clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7faa47c44810) = 21470 ... 21470 execve("/usr/sbin/bcron-spool", ["bcron-spool"], 0x55d2460807e0 /* 27 vars */) = 0 ... 21470 chdir("/var/spool/cron") = 0 ... 21470 openat(AT_FDCWD, "tmp/spool.21470.1573692319.854640", O_RDWR|O_CREAT|O_EXCL, 0600) = -1 EACCES (Permission denied) 21470 write(1, "32:ZCould not create temporary f"..., 36) = 36 21470 write(2, "bcron-spool[21470]: Fatal: logs:"..., 84) = 84 21470 unlink("tmp/spool.21470.1573692319.854640") = -1 ENOENT (No such file or directory) 21470 exit_group(111) = ? 21470 +++ exited with 111 +++
(إذا فقدت ، فقد ترغب في قراءة مشاركتي السابقة على * nix control control and shells .) لذلك ، لم يحصل خادم PID 20629 على إذن لإنشاء ملف على طول المسار /var/spool/cron/tmp/spool.21470.1573692319.854640 . , — . :
# ls -ld /var/spool/cron/tmp/ drwxr-xr-x 2 root root 4096 Nov 6 05:33 /var/spool/cron/tmp/ # ps u -p 20629 USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND cron 20629 0.0 0.0 2276 752 ? Ss Nov14 0:00 unixserver -U /var/run/bcron-spool -- bcron-spool
! cron, root /var/spool/cron/tmp/ . chown cron /var/spool/cron/tmp/ bcron . ( , — SELinux AppArmor, dmesg .)
في المجموع
, , , , — . , bcron , .
, , , strace , , . , strace . , .