رفتن به محتوا
بایت

00001001

کالبدشکافی سیستم‌عامل در لحظه

چگونه سیستم‌عامل را در عمیق‌ترین لایه‌ها عیب‌یابی کنیم؟

۵ دقیقه مطالعه

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

اما این فناوری، زیر کاپوت خود از چه جادویی استفاده می‌کند؟ وقتی می‌خواهیم بفهمیم زیر پوست سرور دقیقاً چه اتفاقی در حال رخ دادن است، eBPF از چهار مکانیزم اصلی برای استخراج اطلاعات استفاده می‌کند. آشنایی با این چهار بازوی قدرتمند، دیدگاه شما را نسبت به برای همیشه تغییر خواهد داد.

۱. لنگرگــــــــاه‌های امن کــــــــــرنل (Tracepoints)

توسعه‌دهندگان لینوکس مجموعه‌ای از نقاط ثابت و از‌ پیش تعریف‌شده را در جاهای حساس کرنل -مانند شروع و پایان فراخوانی‌های سیستمی، درگیری‌های شبکه یا زمان‌بندی پردازنده‌ها- تعبیه کرده‌اند که به آن‌ها Tracepoint می‌گویند. وقتی این نقاط را وارد فرایند دیباگ خود می‌کنید، دقیقاً نقش یک لنگرگاه مطمئن را بازی می‌کنند؛ چرا که دارای یک API کاملاً پایدار هستند و اگر روزی لینوکس خود را آپدیت کنید، کدهای مانیتورینگ شما از کار نمی‌افتند.

نکتۀ شگفت‌انگیز این است که وقتی این نقاط مانیتور نمـــــــی‌شوند،عملاً هیــــــچ بار پردازشی اضافه‌ای روی سیستم ندارند؛ انگار دوربین‌های مداربسته‌ای در سیستم کار گذاشته‌اید که فقط زمان روشن شدن، برق مصرف می‌کنند!

یک سناریوی کاربردی: فرض کنید یک process روی سرور به‌طور غیرعادی دیسک را درگیر کرده و مدام در حال خواندن فایل‌های نامشخصی است. تنها با یک خط اسکریپت می‌توانید به یک Tracepoint مانند sys_enter_openat متصل شوید و به سیستم بگویید: «هر زمان برنامه‌ای قصد باز کردن فایلی را داشت، نام برنامه و مسیر فایل را ثبت کن.» به این ترتیب، بدون ذره‌ای افت کارایی، دقیقاً مچ برنامۀ خطاکار را می‌گیرید. نقطۀ ضعف این روش تنها این است که به نقاطی محدود هستید که از قبل توسط توسعه‌دهندگان لینوکس کدنویسی شده‌اند.

۲. چاقــــــوی سوئیسـی پــــــویا (kprobes و kretprobes)

اگر باگی در یک تابع خاص و لبۀ سیستم رخ دهد که هیچ Tracepoint ای برای آن وجود نداشته باشد، چاره چیست؟ این‌جاست که ابزار دوم یعنی kprobes وارد معماری می‌شود. این مکانیزم به شما اجازه می‌دهد به‌صورت کاملاً پویا به هر تابعی از کرنل که می‌خواهید شوید و آرگومان‌های ورودی آن را در لحظه استخراج کنید. برادر دوقلوی این مکانیزم یعنی kretprobes هم اجازه می‌دهد خروجی و نتیجۀ اجرای همان تابع را ببینید.

یک سناریوی کاربردی: تصور کنید با یک مشکل عجیب در شبکه مواجه هستید؛ اتصال‌های TCP به‌صورت تصادفی drop می‌شوند. با استفاده از kprobes می‌توانید به‌طور مستقیم روی تابعی مانند tcp_drop هوک کنید و دستور دهید هر زمان لینوکس تصمیم به دور انداختن یک بستۀ TCP گرفت، آدرس IP و دلیل drop شدن را استخراج کند. در همان لحظه‌ای که کرنل پکت را حذف می‌کند، شما دلیل آن را در ترمینال خود می‌بینید!

البته باید احتیاط کرد؛ kprobes یک رابط ناپایدار است. با یک آپدیت لینوکس ممکن است نــــــام توابـــــــع کرنل تغییــــــــر کنند و اسکریپت‌های شما از کار بیفتند. به همین دلیل معمولاً از آن به‌عنوان «اسلحۀ آخر» استفاده می‌شود.

۳. نفوذ به فضــــــــای کاربـــــــری (uprobes)

گاهی اوقات مشکل اصلاً در هستۀ لینوکس نیست، بلکه ریشه در کدهای برنامۀ خودتان یا یک کتاب‌خانۀ سیستمی (مانند libc) دارد. این‌جا uprobes وارد میدان می‌شود که دقیقاً همان کار kprobes را برای فضای کاربری انجام می‌دهد.

یک سناریوی کاربردی: فرض کنید می‌خواهید ترافیک برنامه‌ای که با پروتکل HTTPS ارتباط برقرار می‌کند را رصد کنید. در حالت عادی، ابزاری مثل tcpdump فقط دادۀ رمزنگاری‌شده و نامفهوم به شما نشان می‌دهد. اما با uprobes می‌توانید مثل یک نینجا به‌طور مستقیم روی تابع SSL_write در کتاب‌خانۀ OpenSSL هوک کنید. از آن‌جا که داده قبل از رمزنگاری شدن به این تابع پاس داده می‌شود، می‌توانید متن خام و شفاف درخواست‌ها را در همان لحظه بخوانید!

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

۴. تلفیق پایـــــــداری و اپلیـــــکیشن (USDT)

برای حل مشکل پایداری و سربار پردازشی بسیار بالای ابزار uprobes، مکانیزم چهارمی به نام شکل گرفته‌است. این مکانیزم دقیقاً معادل Tracepoint هاست، اما برای اپلیکیشن‌های کاربری. در این روش، توسعه‌دهندگان از قبل نقاط ثابتی را در کد خود تعریف کرده‌اند. جالب است بدانید بسیاری از فناوری‌هایی که روزمره استفاده می‌کنیم (مثل MySQL ،Python ،Node.js و Java) این نقاط را در خود دارند، اما در حالت عادی خاموش هستند.

یک سناریوی کاربردی: اگر یک پایگاه دادۀ PostgreSQL دارید که گاهی اوقات بی‌دلیل کند می‌شود، می‌توانید به‌طور مستقیم به نقاطی مثل query__start در خود موتور postgres متصل شوید. با این کار نه‌تنها متن دقیق کوئری SQL را می‌بینید، بلکه از همه جذاب‌تر، می‌توانید این دادۀ سطح اپلیکیشن را با دادۀ کرنل ترکیب کنید. در نتیجه، به‌راحتی می‌فهمید که آیا کند شدن این کوئری به‌خاطر درگیری دیسک بوده یا برنامه منتظر آزاد شدن یک قفل در فایل‌سیستم لینوکس مانده‌است.

از کجا شــــــــروع کنیم؟ (ابــــــــزارهای کاربــــــــردی)

برای استفاده از این ۴ مکانیزم، نیازی نیست برنامه‌نویس حرفه‌ای کرنل به زبان C باشید. جامعۀ متن‌باز ابزارهای فوق‌العاده‌ای توسعه داده‌است که کار را به‌شدت راحت می‌کنند.

ابزار bpftrace: یک زبان اسکریپت‌نویسی ساده (شبیه به زبان AWK) است که با دستورات یک‌خطی به شما اجازه می‌دهد در کم‌تر از چند ثانیه از مکانیزم‌هایی که بررسی کردیم استفاده کنید و دادۀ مورد نظرتان را بیرون بکشید. : اگر به دنبال راه‌حل‌های آماده هستید، BCC حکم یک جعبه‌ابزار جادویی را برای شما دارد. این مجموعه، علاوه‌بر این‌که اجازه می‌دهد کدهای BPF را با زبان‌های آشنایی مثل Python بنویسید، شامل ده‌ها ابزار از پیش نوشته‌شده (معروف به bcc-tools) است. این ابزارها بدون نیاز به نوشتن حتی یک خط کد، برای مانیتورینگ دیسک، شبکه، مموری و پردازنده آمادۀ اجرا هستند. به‌عنوان مثال، با اجرای ابزار execsnoop (یکی از ابزارهای BCC) می‌توانید تمام دستوراتی که در کل سیستم‌عامل در حال اجرا هستند را به شکل زنده ببینید! یا ابزار opensnoop دقیقاً همان سناریوی پیدا کردن فایل‌های بازشده توسط برنامه‌ها را تنها با تایپ یک کلمه در ترمینال برایتان انجام می‌دهد. این یعنی قدرت دیباگ در سطح حرفه‌ای، اما با کاربری به‌شدت آسان.

مطالب مرتبط