برنامه نویسی

راهنمای مدیریت سیگنال در Rust

نوشته شده توسط Eze Sunday✏️

سیگنال یک وقفه نرم افزاری است که توسط سیستم عامل یا فرآیند دیگری برای اطلاع از یک رویداد به یک فرآیند ارسال می شود. به عنوان مثال، وقتی سعی می کنید فشار دهید Control+C در حالی که برنامه شما روی یک ترمینال اجرا می شود، فرآیند را خاتمه می دهد، درست است؟ این یکی از رایج ترین سیگنال ها و کنترل سیگنال است که می توانید در عمل مشاهده کنید. ما نحوه کنترل آن سیگنال و سایر سیگنال ها را در Rust بررسی خواهیم کرد.

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

در این مقاله، هدف سیگنال ها و نحوه مدیریت سیگنال ها در زبان برنامه نویسی Rust را کشف خواهیم کرد. بیایید شروع کنیم، درست است؟ 🦀

پرش به جلو:

مقدمه ای بر سیگنال ها و مدیریت سیگنال در Rust

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

برای مثال، سیگنال‌ها می‌توانند یک فرآیند در حال اجرا را متوقف یا متوقف کنند، کاربر را از یک خطا مانند استثناء ممیز شناور مطلع کنند، یا اطلاعاتی مانند زنگ هشدار سیستم را ارائه دهند. وقتی چنین سیگنال‌هایی دریافت می‌شوند، ممکن است یک برنامه نیاز به بستن دستگیره‌های باز داشته باشد تا منابع سیستم را آزاد کند یا هر فعالیتی را که رویداد ممکن است تحت تأثیر قرار دهد خاتمه دهد. چنین موردی است که یک برنامه زمانی که کاربر فشار می دهد، خارج می شود Control+C.

بررسی انواع سیگنال ها

چندین نوع سیگنال وجود دارد. برخی را می توان مدیریت کرد، در حالی که برخی دیگر نمی توانند. جدول زیر برخی از انواع سیگنال را با کدهای موجود بر اساس استانداردهای POSIX نشان می دهد. این استاندارد مجموعه ای از استانداردها است که API ها را برای سیستم عامل های مشابه یونیکس، از جمله لینوکس، macOS و انواع مختلف یونیکس تعریف می کند. به جدول زیر مراجعه کنید:

نوع سیگنال استفاده کنید
SIGHUP، کد: 1 این سیگنال زمانی که ترمینال کنترل کننده آن بسته یا قطع می شود به یک فرآیند ارسال می شود
SIGINT، کد: 2 هنگامی که کاربر فشار می دهد این سیگنال به یک فرآیند ارسال می شود Control+C تا اجرای آن قطع شود
SIGQUIT، کد: 3 این سیگنال مشابه است SIGINT اما برای شروع یک dump اصلی فرآیند استفاده می شود که برای اشکال زدایی مفید است
SIGILL، کد: 4 این سیگنال زمانی به یک فرآیند ارسال می شود که سعی می کند یک دستور غیرقانونی را اجرا کند
SIGABRT، کد 6 این سیگنال هنگام فراخوانی فرآیند به آن ارسال می شود abort() تابع
SIGFPE، کد: 8 این سیگنال زمانی به فرآیندی فرستاده می شود که تلاش می کند یک عملیات حسابی غیرمجاز مانند تقسیم بر صفر انجام دهد.
SIGKILL، کد: 9 این سیگنال برای خاتمه فوری یک فرآیند استفاده می شود و نمی توان آن را گرفت یا نادیده گرفت
SIGSEGV، کد: 11 این سیگنال زمانی به یک فرآیند ارسال می شود که سعی می کند به حافظه ای که به آن اختصاص داده نشده است دسترسی پیدا کند
SIGTERM این سیگنال به فرآیندی ارسال می‌شود تا درخواست شود که به‌خوبی خاتمه یابد. کد: 15
SIGUSR1، کد: 10 این سیگنال ها را می توان توسط یک فرآیند برای اهداف سفارشی استفاده کرد
SIGUSR2، کد: 12 مثل SIGUSR1، کد: 10

قبل از بحث در مورد کنترل سیگنال ها در Rust، اجازه دهید در مورد تنظیمات سیگنال صحبت کنیم.

درک شرایط سیگنال

تنظیم سیگنال به عملکرد پیش فرضی اشاره دارد که سیستم عامل زمانی که یک فرآیند سیگنال خاصی را دریافت می کند انجام می دهد. سه حالت احتمالی سیگنال عبارتند از:

  • Terminate: این فرآیند بلافاصله بدون هیچ شانسی برای پاکسازی یا ذخیره وضعیت خاتمه می یابد
  • Ignore: فرآیند در پاسخ به سیگنال کاری انجام نمی دهد
  • Catch: فرآیند یک تابع کنترل کننده سیگنال تعریف شده توسط کاربر را برای کنترل سیگنال اجرا می کند

این بدان معنی است که نمی توان همه سیگنال ها را مدیریت کرد. برنامه شما فقط می تواند سیگنال هایی را که سیستم عامل به آن اجازه می دهد مدیریت کند. تمام سیگنال های از پیش تعریف شده ای که در بالا ذکر کردیم قابل کنترل هستند. با این حال، سیگنال های دیگری مانند SIGKILL، SIGSTOP، و SIGCONT، که قابل رسیدگی نیست. مثلا، SIGKILL برای خاتمه اجباری یک فرآیند استفاده می شود و نمی توان آن را گرفت، مسدود کرد یا نادیده گرفت.

کنترل سیگنال در Rust

اکنون که اصول اولیه سیگنال ها را پوشش دادیم، بیایید به دنیای کنترل سیگنال ها در Rust بپردازیم! برخلاف C، جایی که کنترل سیگنال در ماژول‌های زبان تعبیه شده است، Rust چندین کتابخانه را فراهم می‌کند که توسعه‌دهندگان را قادر می‌سازد تا سیگنال‌ها را به راحتی مدیریت کنند. کتابخانه‌هایی مانند signal_hook، nix، libc و tokio سیگنال‌هایی را کنترل می‌کنند که عمدتاً از اتصالات C استفاده می‌کنند تا کار با سیگنال‌ها را ممکن کنند.

کنترل سیگنال با توکیو

بیایید به مثالی نگاه کنیم تا نشان دهیم چگونه می‌توانیم سیگنال‌ها را در Rust با جعبه توکیو کنترل کنیم. جعبه سیگنال توکیو یک انتخاب عالی برای کنترل سیگنال است زیرا ناهمزمان و ایمن است. ضمناً از libc در پشت صحنه استفاده می کند.

ابتدا یک پروژه Rust با Cargo ایجاد کنید و با اجرای دستور زیر tokio را نصب کنید:

 init && cargo add tokio
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

پس از اتمام نصب، آن را باز کنید cargo.toml توکیو را فایل و فعال کنید full ویژگی ها با به روز رسانی features پرچم با full استدلال مانند این: features=["full"]. \

کد شما باید به شکل زیر باشد:

[package]
name = "practice"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
tokio = { version="1.25.0", features=["full"] }
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

سپس، بیایید یک کد نمونه برای مدیریت آن بنویسیم SIGINT سیگنال – سیگنالی که هنگام فشار دادن فعال می شود Control+C در برابر یک فرآیند در حال اجرا در ترمینال شما. این هم کد:

use tokio::signal::unix::{signal, SignalKind};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut sigint = signal(SignalKind::interrupt())?;

    match sigint.recv().await {
        Some(()) => println!("Received SIGINT signal"),
        None => eprintln!("Stream terminated before receiving SIGINT signal"),
    }

    for num in 0..10000 {
        println!("{}", num)
    }

    Ok(())
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

حالا، فرار کن cargo run در ترمینال خود برای آزمایش کد. در حالی که کد در حال اجرا است، فشار دهید Control+C، و پاسخی مانند این خواهید دید: نمونه ای از کنترل سیگنال در زنگ

در کد بالا نوع سیگنال را با فراخوانی آن مقداردهی اولیه می کنیم signalKind روش. SIGINT تحت این عنوان مورد اشاره قرار گرفته است interrupt()، و SIGTERM تحت این عنوان مورد اشاره قرار گرفته است terminate(). شما می توانید روش های دیگران را در اسناد پیدا کنید. در مورد ما، ما تماس می گیریم interrupt() نوع:

let mut sigint = signal(SignalKind::interrupt())?;
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

پس از فراخوانی متد، شما آماده گوش دادن به آن سیگنال و مدیریت آن با استفاده از آن خواهید بود .recv() روش، همانطور که در زیر نشان داده شده است:

match sigint.recv().await {
    Some(()) => println!("Received SIGINT signal"),
    None => eprintln!("Stream terminated before receiving SIGINT signal"),
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

این اساساً نحوه کنترل سیگنال ها در Rust تنها در چند خط کد است.

کاوش پوشش سیگنال در Rust

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

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

مسدود کردن و رفع انسداد سیگنال ها با nix

بیایید به نمونه ای از نحوه مسدود کردن و رفع انسداد سیگنال ها با استفاده از جعبه nix نگاه کنیم. برای این مثال، از جعبه libc استفاده می کنیم. بنابراین، با نصب آن شروع کنید cargo add libc در ترمینال شما سپس، این را به خود اضافه کنید src/main.rs file:

use libc::{sigaddset, sigemptyset, sigprocmask, SIGINT, SIG_BLOCK, SIG_UNBLOCK};
use std::thread;
use std::time::Duration;
fn main() {
    unsafe {
        // Create an empty signal mask
        let mut mask: libc::sigset_t = std::mem::zeroed();
        sigemptyset(&mut mask);
        // Add the SIGINT signal to the signal mask
        sigaddset(&mut mask, SIGINT);
        // Block the SIGINT signal using the signal mask
        sigprocmask(SIG_BLOCK, &mask as *const libc::sigset_t, std::ptr::null_mut());
    }
    println!("Blocked SIGINT signal for 5 seconds");
    thread::sleep(Duration::from_secs(5));
    unsafe {
        // Unblock the SIGINT signal using the signal mask
        let mut mask: libc::sigset_t = std::mem::zeroed();
        sigemptyset(&mut mask);
        sigaddset(&mut mask, SIGINT);
        sigprocmask(SIG_UNBLOCK, &mask as *const libc::sigset_t, std::ptr::null_mut());
    }
    println!("Unblocked SIGINT signal");
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

توجه داشته باشید که تابع را به عنوان علامت گذاری می کنیم unsafe. ما این کار را انجام می دهیم زیرا شامل تعامل مستقیم با مکانیسم های مدیریت سیگنال سیستم عامل از طریق کتابخانه استاندارد C است. libc رابط. همانطور که می بینید، ما در حال حذف ارجاع هستیم sigset_t اشاره گر خام *const libc::sigset_t زیرا آن قسمت از کد ناامن است.

در کد بالا، ما ارسال را مسدود می کنیم SIGINT سیگنال تا بعد 5 ثانیه در درون آن ها 5 ثانیه، اگر فشار دهید Control+C، هیچ اتفاقی نخواهد افتاد. با این حال SIGINT سیگنال پس از راه اندازی خواهد شد 5 ثانیه می گذرد

اگر این کد را با cargo run و فشار دهید Control+C و همچنین بدون فشار دادن آن را اجرا کنید Control+C، چیزی شبیه به این دریافت خواهید کرد: نتیجه نهایی کنترل سیگنال در زنگ برای اولین فرمان اجرا شده در تصویر بالا، سیگنال بعد از آن اجرا شد 5 ثانیه چون فشار دادیم Control+C. اما برای دومی اینطور نشد. همانطور که می بینید کد تا انتها اجرا شد.

نتیجه

کنترل سیگنال ها در Rust بسیار ساده است. علی‌رغم اسناد محدود موجود برای جعبه‌های سیگنال Rust، من اطمینان دارم که بینش‌های ارائه‌شده در اینجا به عنوان نقطه شروع مفیدی برای پیاده‌سازی مدیریت سیگنال با استفاده از Rust خواهد بود.

هک مبارک!


LogRocket: دید کامل در صفحات وب برای برنامه های Rust

اشکال زدایی برنامه های Rust می تواند دشوار باشد، به خصوص زمانی که کاربران مشکلاتی را تجربه می کنند که بازتولید آن دشوار است. اگر به نظارت و ردیابی عملکرد برنامه‌های Rust، نمایش خودکار خطاها و پیگیری درخواست‌های شبکه و زمان بارگذاری کند علاقه دارید، LogRocket را امتحان کنید.

بنر آزمایشی رایگان داشبورد LogRocket

LogRocket مانند یک DVR برای برنامه های وب است که به معنای واقعی کلمه هر چیزی را که در برنامه Rust شما اتفاق می افتد ضبط می کند. به جای حدس زدن چرایی مشکلات، می توانید در مورد وضعیتی که برنامه شما در هنگام بروز مشکل در آن قرار داشت، جمع آوری کرده و گزارش دهید. LogRocket همچنین بر عملکرد برنامه شما نظارت می کند، معیارهایی مانند بار CPU مشتری، استفاده از حافظه مشتری و موارد دیگر را گزارش می دهد.

نحوه اشکال زدایی برنامه های Rust خود را مدرن کنید – نظارت را به صورت رایگان شروع کنید.

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا