منابع آموزشی و پژوهشی، برای دانشگاه و مدرسه
پشتیبانیانتشار محتوا
دورهٔ آموزشیرایگان

لینوکس ۳؛ آزمایشگاه خطاهای برنامه، فایل و شبکه

۸ درس در ۴ فصل۹ ساعت و ۵۵ دقیقهدربارهٔ دوره
ردگیری و تحویل رخداددرس ۷ از ۸

strace روی برنامهٔ خودمان؛ پیوند نشانه با فراخوانی سیستم

۷۵ دقیقه آموزش متنی
در این درس ۸ بخش

در درس‌های قبل، خطا را از خروجی برنامه خواندیم. اکنون می‌خواهیم ببینیم برنامه در مرز با هسته چه عملیاتی انجام داده است. strace فراخوانی‌های سیستم و سیگنال‌های مشاهده‌شده را ثبت می‌کند؛ بنابراین می‌تواند فاصلهٔ میان پیام کلی برنامه و عملی مانند connect را کم کند. آزمایش فقط فرزند تازهٔ خودمان را ردگیری می‌کند و به سرویس موجود یا نشست SSH دیگری متصل نمی‌شود.

چه چیزی دیده می‌شود و چه چیزی نه؟

یک برنامهٔ سطح بالا برای کارهایی مانند بازکردن فایل یا اتصال شبکه از کتابخانه و سرانجام رابط هسته استفاده می‌کند. strace نام فراخوانی، آرگومان‌های قابل نمایش و نتیجه را در همان مرز نشان می‌دهد. این داده، تمام منطق برنامه یا معنای کسب‌وکار را نمایش نمی‌دهد. مثلاً دیده‌شدن یک نوشتن موفق ثابت نمی‌کند گزارش از نظر محاسبات درست است؛ برای آن، قرارداد و آزمون سطح برنامه لازم داریم.

ردگیری با اتصال به PID موجود با اجرای برنامه زیر نظر ابزار یکسان نیست. در این درس، strace خود فرمان آزمایش را آغاز می‌کند؛ مالکیت و دامنه روشن می‌ماند. گزینهٔ -f فرزندان ساخته‌شده را نیز دنبال می‌کند. این گزینه مجوز دسترسی به پردازه‌های نامرتبط نیست. #linux-strace و #linux-syscall کمک می‌کنند لایهٔ مشاهده را از تفسیر علت جدا نگه داریم.

پیش‌نیاز ابزار را از نتیجهٔ آزمایش جدا کنیم

این بخش اختیاری است و به strace نصب‌شده از بستهٔ رسمی اوبونتو و اجازهٔ ptrace در محیط آزمایشی نیاز دارد. با حساب عادی و پوشهٔ project کار کنید. اگر ابزار وجود ندارد، مرحلهٔ ردگیری را انجام‌نشده ثبت کنید؛ برای نصب در ماشین شخصی از روش معمول مدیر بسته استفاده می‌شود. روی سرور مشترک، صرفاً برای این تمرین بسته و سیاست سراسری را تغییر نمی‌دهیم.

bash
command -v strace
strace --version
# فقط خواندن، اگر این فایل در محیط وجود دارد:
if [ -r /proc/sys/kernel/yama/ptrace_scope ]; then
  cat /proc/sys/kernel/yama/ptrace_scope
fi

وجود فایل ptrace_scope و مقدار آن به پیکربندی هسته و محیط وابسته است. نبود آن را معادل اجازهٔ نامحدود ندانید؛ یک لایهٔ دیگر هم ممکن است محدود کند. بعضی تنظیمات اجازهٔ رابطهٔ والد و فرزند را می‌دهند و بعضی سخت‌گیرانه‌ترند. هدف خواندن وضعیت است، نه تغییر آن. اگر ابزار خطای مجوز داد، متن واقعی را نگه دارید و از خاموش‌کردن Yama، AppArmor یا محدودیت محیط برای عبور از تمرین خودداری کنید.

در محیط تهیهٔ این بسته، تلاش واقعی برای ردگیری با خطای EPERM از رابط ptrace رد شد؛ تنظیمات امنیتی تغییر نکرده و فایل trace موفقی به‌عنوان شاهد ارائه نشده است. فرمان‌های این درس روش اجرا و تفسیر را برای ماشین آزمایشیِ دارای مجوز توضیح می‌دهند. بنابراین شکل‌های تحلیلی بعدی، خروجی ثبت‌شدهٔ همین محیط نیستند و نتیجهٔ اجرای شما باید جداگانه بررسی شود.

همان اتصال ردشده را این بار ردگیری کنید

ابتدا سناریوی refused را بدون trace اجرا کنید تا خروجی معمول آن را بشناسید. سپس بلوک بعدی، خروجی trace را در پوشهٔ موقت خصوصی می‌نویسد. فایل JSON برنامه همچنان روی stdout می‌آید؛ فایل trace جریان مستقلی است. آدرس 127.0.0.1 و پورت انتخاب‌شدهٔ همان اجرا را از JSON بخوانید. هیچ نام دامنه یا مقصد اینترنتی در این آزمایش نیست.

bash
python3 lab.py refused
umask 077
trace_dir=$(mktemp -d) || exit 1
if strace -f -e trace=openat,close,socket,bind,listen,connect,getsockopt,poll,ppoll \
  -o "$trace_dir/trace.log" python3 lab.py refused; then
  trace_status=0
else
  trace_status=$?
fi
printf 'trace_status=%s\n' "$trace_status"
if [ "$trace_status" -eq 0 ]; then
  grep -F ECONNREFUSED "$trace_dir/trace.log"
fi
printf 'Trace: %s\n' "$trace_dir/trace.log"

نام‌های openat و close به مشاهدهٔ فایل و دسکریپتور کمک می‌کنند. socket، bind، listen، connect و getsockopt بخش شبکه و poll یا ppoll انتظارِ آماده‌شدن را روشن‌تر می‌کنند. گزینهٔ -o مسیر ذخیره را تعیین می‌کند. این فیلتر عمداً همهٔ فراخوانی‌ها را ثبت نمی‌کند؛ نبود عملی که از فیلتر حذف شده، دلیل اجرا نشدن آن نیست. وضعیت خود فرمان را نیز از trace_status بخوانید.

در حالت دارای مهلتِ پایتون، سوکت برای این کار می‌تواند غیرمسدود باشد: connect ابتدا EINPROGRESS بدهد، برنامه منتظر آماده‌شدن بماند و سپس با getsockopt گزینهٔ SO_ERROR را بخواند. ECONNREFUSED ممکن است مقدارِ همان وضعیت باشد، نه خطای مستقیمِ connect. بازگشت صفرِ getsockopt موفقیتِ خواندن وضعیت را می‌گوید؛ وقتی مقدار SO_ERROR خطاست، اتصال موفق نشده است. زنجیره را با مقصد و JSON همان اجرا تطبیق دهید.

plaintext
الگوی تفسیر ممکن؛ خروجی ثبت‌شدهٔ این محیط نیست:
connect → EINPROGRESS
poll یا ppoll → بررسی آماده‌شدن سوکت
getsockopt(SO_ERROR) → مقدار خطا ECONNREFUSED
بازگشت صفرِ getsockopt: خواندن وضعیت موفق بوده، نه اتصال
مقصد و شمارهٔ دسکریپتور: از trace واقعیِ همان اجرا خوانده شوند

خواندن خط خطا با توجه به مسیر برنامه

همهٔ نتیجه‌های ناموفق، خرابی نهایی نیستند. بارگذار Python ممکن است برای یافتن کتابخانه چند مسیر را امتحان کند؛ بعضی ناموفق و مسیر بعدی موفق می‌شود. همین رفتار را هنگام بازکردن فایل پیکربندی اختیاری هم می‌بینیم. نخست ارتباط خط با عملیات مورد پرسش را اثبات کنید، سپس ببینید برنامه خطا را چگونه مدیریت کرده است. انتخاب اولین واژهٔ error در یک فایل بلند، روش تشخیص علت نیست.

ممکن است یک فراخوانی ناتمام و ادامهٔ آن در خط بعد نشان داده شود، یا چند فرایند با پیشوند PID دیده شوند. خط‌ها را فقط براساس نزدیک‌بودن ظاهری به هم نسبت ندهید. فرزند و رشتهٔ مرتبط، نام عملیات و آرگومان‌ها را بررسی کنید. برای شروع همین آزمایش تک‌مقصدی مناسب‌تر از ردگیری یک سرویس بزرگ است؛ دادهٔ کوچک اجازه می‌دهد فرضیه را با خروجی واقعی مقایسه کنیم.

ردگیری، ابزار بنچمارک بی‌اثر نیست

مشاهدهٔ مرز سیستم می‌تواند روی سرعت و زمان‌بندی برنامه اثر بگذارد؛ بعضی مشکلاتِ رقابتی نیز ممکن است زیر ابزار رفتار دیگری نشان دهند. از زمان‌های این اجرا رتبه‌بندی عمومیِ روش‌ها نسازید. اگر مسئلهٔ اصلی کارایی است، دادهٔ بدون trace و روش اندازه‌گیریِ متناسب هم لازم است. در این درس سؤال ما «کدام عملیات چه نتیجه‌ای داد» است، نه «این برنامه در حالت عادی دقیقاً چند میلی‌ثانیه طول می‌کشد».

فایل trace ممکن است مسیر فایل‌ها، مقصد شبکه و آرگومان‌های عملیاتی را در خود داشته باشد. فیلترکردن نوع فراخوانی، پاک‌سازی اطلاعات حساس را تضمین نمی‌کند. سناریوی ما از دادهٔ ساختگی و مقصد محلی استفاده می‌کند، بااین‌حال پیش از اشتراک خروجی هر برنامهٔ واقعی باید همان فایل بازبینی شود. فایل خام سرویس واقعی را بی‌بررسی به مخزن یا گفت‌وگوی عمومی نفرستید؛ گزارش محدودِ شواهد معمولاً خواناتر هم هست.

این شواهد کجای مسیر قبلی قرار می‌گیرند؟

پیش‌تر لاگ ساخت‌یافتهٔ سرویس را در این درس خواندیم:
https://www.darsavand.com/courses/linux-advanced-administration?lesson=32265068-21de-4ef1-86c7-6ae5d7f00827
برای تفاوت محدودیت امنیتی و مجوز معمول فایل، این درس زمینهٔ لازم را می‌دهد:
https://www.darsavand.com/courses/linux-advanced-administration?lesson=4b70b1db-07bf-4fb6-b08a-b226473f91be

در گزارش تحویل، فرمان، نسخهٔ ابزار، عملیات و مقصد، خط مربوط به شکست و برداشت محدود خود را بنویسید. برای مرور، #linux-errno، #linux-file-descriptor و #linux-strace را دنبال کنید. اگر مرحلهٔ اختیاری ردگیری مجاز نبود، همان محدودیت بخشی از گزارش درست است؛ می‌توانید خروجی برنامه و مستندات عملیات را بررسی کنید، اما انجام‌نشدن trace را با انجام‌شدن آن جایگزین نکنید.

تمرین و پاسخ تشریحی

پرسش 1: در trace چند ENOENT مربوط به فایل‌های کتابخانه دیده‌اید، اما برنامه موفق تمام می‌شود. آیا نخستین ENOENT حتماً علت خرابی است؟

پاسخ تشریحی: خیر. برنامه یا بارگذار ممکن است چند مسیر نامزد را امتحان کند و پس از ناموفق‌بودن یکی، مسیر معتبر دیگری بیابد. باید فراخوانی مرتبط با عملیات مورد بررسی، آرگومان، نتیجه و رفتار بعدی برنامه را کنار هم ببینید. در آزمایش ما نتیجهٔ نهاییِ مرحلهٔ اتصال، ECONNREFUSED است؛ ممکن است آن را در مقدار SO_ERROR پس از connect ناتمام ببینیم. هر خطای دیگری الزاماً علتِ آن سناریو نیست.

پرسش 2: strace برای فرزند خودتان Operation not permitted می‌دهد. آیا تغییر ptrace_scope به صفر بخشی از تحویل این درس است؟

پاسخ تشریحی: خیر. پیام، سیاست موجود محیط و پیش‌نیاز اجرا را نشان می‌دهد. ممکن است محدودیت از Yama، کنترل دسترسی یا محیط محصور باشد. همان خطا و محیط را ثبت می‌کنیم و مرحلهٔ اختیاری trace را انجام‌نشده گزارش می‌دهیم. تغییر سراسری سیاست امنیتی برای تمام‌کردن تمرین لازم نیست؛ باقی آزمایش‌های مجاز را می‌توان مستقل بررسی کرد.

پرسش 3: با -e trace=connect فقط اتصال را ثبت کرده‌اید. آیا فایل خروجی دیگر نمی‌تواند اطلاعات حساس داشته باشد و آیا زمان آن معیار سرعت برنامه است؟

پاسخ تشریحی: هیچ‌کدام تضمین نمی‌شود. آرگومان مقصد و محتوای سایر بخش‌های مجاز می‌توانند اطلاعات عملیاتی بدهند؛ انتخاب فراخوانی، ابزار پاک‌سازی اطلاعات نیست. همچنین خود trace سربار و اثر زمان‌بندی دارد و فیلترکردن آن را به بنچمارک خنثی تبدیل نمی‌کند. خروجی این درس برای ارتباط‌دادن عملیات و خطاست، نه ادعای سرعت یا بی‌خطر بودن انتشار فایل.

منابع رسمی برای بررسی و مطالعهٔ بیشتر

strace — Official project: https://strace.io/
strace(1) — Official manual linked by project: https://man7.org/linux/man-pages/man1/strace.1.html
Linux Kernel — Yama: https://www.kernel.org/doc/html/latest/admin-guide/LSM/Yama.html
Python 3.12 — errno: https://docs.python.org/3.12/library/errno.html

متن، سناریوها و پرسش‌های این درس تألیف مستقل تحریریه درس‌آوند است. منابع برای بررسی رفتار ابزار و ادامهٔ مطالعه معرفی شده‌اند؛ این آموزش ترجمهٔ یک منبع واحد نیست.

فایل آزمایشگاه، راهنمای اجرا، آزمون‌ها و فرم گزارش رخداد:
https://www.darsavand.com/resources/linux-application-errors-practice-kit
برای دیدن ترتیب دوره‌های مقدماتی، پیشرفته و این مسیر، نقشهٔ راه لینوکس را بخوانید:
https://www.darsavand.com/resources/linux-learning-roadmap
تألیف و مثال‌های اختصاصی: تحریریه درس‌آوند.

ورود برای ثبت پیشرفت

متن درس‌های عمومی را بدون ورود بخوانید.

یادگیری‌ات را دنبال کن

برای نگهداری پیشرفت و ادامهٔ یادگیری از حساب خود استفاده کن.

ورود و ثبت‌نام
دربارهٔ این دوره

دورهٔ رایگان لینوکس ۳ در ۸ درس؛ آزمایش عملی خطاهای فایل، پورت، اتصال، Timeout و سیگنال، با Python، strace، تمرین و پاسخ. ادامهٔ لینوکس پیشرفته و استقرار بک‌اند؛ تألیف تحریریه درس‌آوند.

پرسشی دربارهٔ این درس داری؟گفت‌وگو با پشتیبانی در حساب کاربری
یکجا انتخاب کن، راحت‌تر دریافت کن

انتخاب‌های من ۰ منبع

مجموعهٔ مطالعه‌ات را بساز.

کنار مقاله‌ها «انتخاب» را بزنید تا چند فایل را با هم دریافت کنید.

انتخاب مقاله‌های رایگان