Product SiteDocumentation Site

9.11. اتصال سریع: hotplug

9.11.1. مقدمه

The hotplug kernel subsystem dynamically handles the addition and removal of devices, by loading the appropriate drivers and by creating the corresponding device files (with the help of udevd). With modern hardware and virtualization, almost everything can be “hotplugged“: from the usual USB/PCMCIA/IEEE 1394 peripherals to SATA hard drives, but also the CPU and the memory.
کرنل شامل پایگاه‌داده‌ای است که هر شناسه دستگاه را به درایور مخصوص به آن ثبت می‌کند. این پایگاه‌داده در زمان راه‌اندازی اولیه به منظور بارگیری تمام درایورهای مورد نیاز برای دستگاه‌های شناخته شده در خطوط ارتباطی مختلف استفاده می‌شود، همچنین در زمانی که یک دستگاه جانبی به سیستم متصل می‌گردد. زمانی که دستگاه آماده استفاده باشد یک پیام به udevd ارسال می‌شود تا فایل مورد نظر دستگاه در /dev/ را ایجاد کند.

9.11.2. مشکل نامگذاری

قبل از ظهور قابلیت اتصال، انتساب یک نام ثابت به یک دستگاه کار ساده‌ای بود. اینکار بر اساس موقعیت دستگاه‌ها روی گذرگاه سیستم انجام می‌شد. اما این امکان برای دستگاه‌هایی که روی این گذرگاه وارد و خارج می‌شوند وجود ندارد. مورد متداول هم کاربرد دوربین دیجیتال و حافظه جانبی USB است، که هر دو در رایانه به عنوان هارد درایو ظاهر می‌شوند. اولی به نام /dev/sdb و دومی به نام /dev/sdc (به همراه /dev/sda که نام هارد درایو اصلی رایانه است). نام دستگاه ثابت نیست؛ در حقیقت به ترتیبی که دستگاه‌ها متصل می‌شوند ارتباط دارد.
علاوه بر این، درایورهای بیشتری از شماره‌گذاری ماژور/مینور دستگاه‌ها استفاده می‌کنند که امکان نامگذاری ثابت دستگاه‌ها را از بین می‌برد چرا که این خصوصیات ضروری در هر مرتبه راه‌اندازی سیستم از بین می‌روند.
udev دقیقا به منظور حل این مشکل ایجاد شد.

9.11.3. چگونگی کارکرد udev

زمانی که کرنل udev را از وجود یک دستگاه جدید آگاه می‌کند، این برنامه به جمع‌آوری اطلاعات مختلف در رابطه با دستگاه از طریق /sys/ می‌پردازد، به خصوص اطلاعاتی که منجر به شناسایی دستگاه می‌شوند (نشانی MAC برای یک کارت شبکه، شماره سریال برخی دستگاه‌های USB و از این قبیل).
هنگامی که این اطلاعت بدست آمد، udev به مجموعه قواعد موجود در /etc/udev/rules.d/ و /lib/udev/rules.d/ مراجعه می‌کند. در این فرآیند تصمیم می‌گیرد که چه نامی به دستگاه اختصاص دهد، از چه پیوندهای نمادین برای ایجاد کردنش استفاده کند (برای اختصاص نام مستعار) و چه دستوراتی را اجرا کند. تمام این فایل‌ها مورد بررسی قرار می‌گیرند و قواعد موجود در آن‌ها به ترتیب ارزیابی می‌گردند (بجز حالتی که یک فایل از عبارت “GOTO” استفاده کند). پس، ممکن است چندین قاعده درباره یک رخداد بررسی شوند.
شیوه نگارش این قواعد تقریبا ساده است: هر سطر شامل شرایط انتخاب و انتساب متغیرها است. اولی برای انتخاب رویدادها جهت پاسخ دادن و دومی اقدام مورد نظر در پاسخ به آن رویداد است. تمام این قواعد با کاما از یکدیگر جدا شده‌اند و عملگر بین شرایط انتخاب (با عملگرهای مقایسه‌ای، مانند == یا !=) یا شرایط انتساب (با عملگرهایی نظیر =، += یا :=) تفاوت قائل می‌شود.
عملگرهای مقایسه‌ای روی متغیرهای زیر اعمال می‌شوند:
  • KERNEL: نامی که کرنل به دستگاه اختصاص داده است؛
  • ACTION: پاسخ متناظر به رویداد (“add” زمانی که دستگاه اضافه شده یا “remove” زمانی که دستگاه حذف شده باشد)؛
  • DEVPATH: مسیر دستگاه در ساختار /sys/؛
  • SUBSYSTEM: زیرسیستم کرنل که درخواست را ایجاد کرده است (گزینه‌های زیادی وجود دارند اما برخی عبارتند از “usb”، “ide”، “net”، “firmware”، و از این قبیل);
  • ATTR{attribute}: محتوای فایل attribute در دایرکتوری /sys/$devpath/ دستگاه. اینجاست که می‌توانید نشانی MAC و سایر شناسه‌های مخصوص گذرگاه را پیدا کنید.
  • KERNELS، SUBSYSTEMS و ATTRS{attributes} انواع گوناگونی هستند که تلاش دارند گزینه‌های مختلف در رابطه با دستگاه‌های والد با دستگاه فعلی را سازگار سازند.
  • PROGRAM: نماینده آزمون برای برنامه مشخص شده است (در صورت بازگرداندن ۰ برابر با true، در غیر اینصورت false). محتوای خروحی استاندارد برنامه ذخیره می‌شود تا توسط آزمون RESULT قابل استفاده باشد.
  • RESULT: آزمون‌های مورد نظر را روی خروجی استاندارد ذخیره شده از آخرین فراخوانی PROGRAM اجرا می‌کند.
عملگر سمت راست می‌تواند به شیوه‌ای استفاده شود که امکان انتخاب چند مقدار در یک لحظه را داشته باشد. برای نمونه، * هر رشته‌ای را شامل می‌شود (حتی رشته خالی)؛ ? هر کاراکتری را شامل می‌شود و [] مجموعه از کاراکترهای محدود را شامل می‌شود (یا خلاف آن، در صورتی که اولین کاراکتر برابر با ! باشد و بازه پیوسته کاراکترها به صورت a-z بیان شود).
با توجه به عملگرهای انتسابی، = یک مقدار را نسبت می‌دهد (که جایگزین مقدار فعلی می‌شود)؛ در مورد یک فهرست، خالی می‌شود و تنها مقدار انتسابی را شامل می‌گردد. := نیز همین کار را کرده، اما از تغییرات بعدی آن متغیر جلوگیری می‌کند. همینطور += که یک گزینه به فهرست اضافه می‌کند. متغیرهای زیر می‌توانند تغییر کنند:
  • NAME: نام دستگاه که در مسیر /dev/ ایجاد می‌شود. تنها اولین انتساب به حساب می‌آید؛ باقی انتساب‌ها نادیده گرفته می‌شوند؛
  • SYMLINK: فهرستی از پیوندهای نمادین که به یک دستگاه اشاره می‌کنند؛
  • Owner، Group و MODE کاربر و گروه و مجوزهای مورد نیاز دستگاه را تعریف می‌کنند؛
  • RUN: فهرست برنامه‌هایی که در پاسخ به این رویداد باید اجرا شوند.
مقدارهای انتسابی به این متغیرها می‌توانند از جایگزین‌های زیر استفاده کنند:
  • $kernel یا %k: معادل با KERNEL؛
  • $number یا %n: شماره ترتیبی برای دستگاه، برای نمونه در sda3 برابر با “3”؛
  • $devpath یا %p: معادل با DEVPATH؛
  • $attr{attribute} یا %s{attribute}: معادل با ATTRS{attribute}؛
  • $major یا %M: شماره ماژور کرنل برای دستگاه؛
  • $minor یا %m: شماره مینور کرنل برای دستگاه؛
  • $result یا %c: رشته خروجی آخرین برنامه‌ای که توسط PROGRAM فراخوانی شده است؛
  • و در نهایت، %% و $$ برای علامت‌های درصد و دلار.
The above lists are not complete (they include only the most important parameters), but the udev(7) manual page should be exhaustive.

9.11.4. یک مثال کامل

بیایید یک حافظه جانبی USB را در نظر بگیریم که می‌خواهیم نامی ثابت برایش ایجاد کنیم. ابتدا، باید شیوه‌ای برای شناسایی منحصربفرد آن بیابید. به این منظور، دستگاه را متصل کرده و دستور udevadm info -a -n /dev/sdc را اجرا کنید (/dev/sdc را با نام انتسابی به دستگاه جایگزین کنید).
# udevadm info -a -n /dev/sdc
[...]
  looking at device '/devices/pci0000:00/0000:00:10.0/usb2/2-1/2-1:1.0/host4/target4:0:0/4:0:0:0/block/sdc':
    KERNEL=="sdc"
    SUBSYSTEM=="block"
    DRIVER==""
    ATTR{hidden}=="0"
    ATTR{events}=="media_change"
    ATTR{ro}=="0"
    ATTR{discard_alignment}=="0"
    ATTR{removable}=="1"
    ATTR{events_async}==""
    ATTR{alignment_offset}=="0"
    ATTR{capability}=="51"
    ATTR{events_poll_msecs}=="-1"
    ATTR{stat}=="130  0  6328  435  0  0  0  0  0  252  252  0  0  0  0"
    ATTR{size}=="15100224"
    ATTR{range}=="16"
    ATTR{ext_range}=="256"
    ATTR{inflight}=="0  0"
[...]

  looking at parent device '/devices/pci0000:00/0000:00:10.0/usb2/2-1/2-1:1.0/host4/target4:0:0/4:0:0:0':
[...]
    ATTRS{max_sectors}=="240"
[...]
  looking at parent device '/devices/pci0000:00/0000:00:10.0/usb2/2-1':
    KERNELS=="2-1"
    SUBSYSTEMS=="usb"
    DRIVERS=="usb"
    ATTRS{bDeviceProtocol}=="00"
    ATTRS{bNumInterfaces}==" 1"
    ATTRS{busnum}=="2"
    ATTRS{quirks}=="0x0"
    ATTRS{authorized}=="1"
    ATTRS{ltm_capable}=="no"
    ATTRS{speed}=="480"
    ATTRS{product}=="TF10"
    ATTRS{manufacturer}=="TDK LoR"
[...]
    ATTRS{serial}=="07032998B60AB777"
[...]
برای ایجاد یک قانون جدید، می‌توانید از آزمون‌های روی متغیرهای دستگاه استفاده کنید، یا هر یک از دستگاه‌های والد آن. مورد بالا به ما اجازه ایجاد دو قانون به صورت زیر را می‌دهد:
KERNEL=="sd?", SUBSYSTEM=="block", ATTRS{serial}=="07032998B60AB777", SYMLINK+="usb_key/disk"
KERNEL=="sd?[0-9]", SUBSYSTEM=="block", ATTRS{serial}=="07032998B60AB777", SYMLINK+="usb_key/part%n"
زمانی که این قوانین در یک فایل تنظیم شوند، برای نمونه /etc/udev/rules.d/010_local.rules، به سادگی می‌توانید حافظه را جدا کرده و از نو نصب کنید. مشاهده خواهید کرد که /dev/usb_key/disk نشان‌دهنده حافظه USB و /dev/usb_key/part1 نشان‌دهنده اولین پارتیشن آن است.