9.11. اتصال سریع: hotplug
زیرسیستم hotplug از کرنل به صورت پویا اضافه و حذف شدن دستگاهها را مدیریت میکند، با بارگیری درایورهای مناسب و ایجاد فایلهای مخصوص به هر دستگاه (با کمک دستور udevd
). با وجود سختافزار جدید و عملیات مجازیسازی، تقریبا هر چیزی قابلیت اتصال سریع را دارد: از دستگاههای متداول USB/PCMCIA/IEEE 1394 گرفته تا هارد درایوهای SATA، همچنین پردازنده و حافظه اصلی.
کرنل شامل پایگاهدادهای است که هر شناسه دستگاه را به درایور مخصوص به آن ثبت میکند. این پایگاهداده در زمان راهاندازی اولیه به منظور بارگیری تمام درایورهای مورد نیاز برای دستگاههای شناخته شده در خطوط ارتباطی مختلف استفاده میشود، همچنین در زمانی که یک دستگاه جانبی به سیستم متصل میگردد. زمانی که دستگاه آماده استفاده باشد یک پیام به udevd
ارسال میشود تا فایل مورد نظر دستگاه در /dev/
را ایجاد کند.
قبل از ظهور قابلیت اتصال، انتساب یک نام ثابت به یک دستگاه کار سادهای بود. اینکار بر اساس موقعیت دستگاهها روی گذرگاه سیستم انجام میشد. اما این امکان برای دستگاههایی که روی این گذرگاه وارد و خارج میشوند وجود ندارد. مورد متداول هم کاربرد دوربین دیجیتال و حافظه جانبی 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
فراخوانی شده است؛
و در نهایت، %%
و $$
برای علامتهای درصد و دلار.
فهرست بالا کامل نیست (تنها شامل مهمترین پارامترها است)، اما صفحه راهنمای udev(7) تمام پارامترها را شامل میشود.
بیایید یک حافظه جانبی USB را در نظر بگیریم که میخواهیم نامی ثابت برایش ایجاد کنیم. ابتدا، باید شیوهای برای شناسایی منحصربفرد آن بیابید. به این منظور، دستگاه را متصل کرده و دستور udevadm info -a -n /dev/sdc
را اجرا کنید (/dev/sdc را با نام انتسابی به دستگاه جایگزین کنید).
#
udevadm info -a -n /dev/sdc
[...]
looking at device '/devices/pci0000:00/0000:00:10.3/usb1/1-2/1-2.2/1-2.2:1.0/host9/target9:0:0/9:0:0:0/block/sdc':
KERNEL=="sdc"
SUBSYSTEM=="block"
DRIVER==""
ATTR{range}=="16"
ATTR{ext_range}=="256"
ATTR{removable}=="1"
ATTR{ro}=="0"
ATTR{size}=="126976"
ATTR{alignment_offset}=="0"
ATTR{capability}=="53"
ATTR{stat}==" 51 100 1208 256 0 0 0 0 0 192 25 6"
ATTR{inflight}==" 0 0"
[...]
looking at parent device '/devices/pci0000:00/0000:00:10.3/usb1/1-2/1-2.2/1-2.2:1.0/host9/target9:0:0/9:0:0:0':
KERNELS=="9:0:0:0"
SUBSYSTEMS=="scsi"
DRIVERS=="sd"
ATTRS{device_blocked}=="0"
ATTRS{type}=="0"
ATTRS{scsi_level}=="3"
ATTRS{vendor}=="I0MEGA "
ATTRS{model}=="UMni64MB*IOM2C4 "
ATTRS{rev}==" "
ATTRS{state}=="running"
[...]
ATTRS{max_sectors}=="240"
[...]
looking at parent device '/devices/pci0000:00/0000:00:10.3/usb1/1-2/1-2.2':
KERNELS=="9:0:0:0"
SUBSYSTEMS=="usb"
DRIVERS=="usb"
ATTRS{configuration}=="iCfg"
ATTRS{bNumInterfaces}==" 1"
ATTRS{bConfigurationValue}=="1"
ATTRS{bmAttributes}=="80"
ATTRS{bMaxPower}=="100mA"
ATTRS{urbnum}=="398"
ATTRS{idVendor}=="4146"
ATTRS{idProduct}=="4146"
ATTRS{bcdDevice}=="0100"
[...]
ATTRS{manufacturer}=="USB Disk"
ATTRS{product}=="USB Mass Storage Device"
ATTRS{serial}=="M004021000001"
[...]
برای ایجاد یک قانون جدید، میتوانید از آزمونهای روی متغیرهای دستگاه استفاده کنید، یا هر یک از دستگاههای والد آن. مورد بالا به ما اجازه ایجاد دو قانون به صورت زیر را میدهد:
KERNEL=="sd?", SUBSYSTEM=="block", ATTRS{serial}=="M004021000001", SYMLINK+="usb_key/disk"
KERNEL=="sd?[0-9]", SUBSYSTEM=="block", ATTRS{serial}=="M004021000001", SYMLINK+="usb_key/part%n"
زمانی که این قوانین در یک فایل تنظیم شوند، برای نمونه /etc/udev/rules.d/010_local.rules
، به سادگی میتوانید حافظه را جدا کرده و از نو نصب کنید. مشاهده خواهید کرد که /dev/usb_key/disk
نشاندهنده حافظه USB و /dev/usb_key/part1
نشاندهنده اولین پارتیشن آن است.