Product SiteDocumentation Site

9.11. Hetpluggning: hetpluggning

9.11.1. Introduktion

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.
Kärnan har en databas som associerar var enhets-ID med krävd drivrutin. Denna databas används vid uppstart för att läsa in alla drivrutiner för externa enheter som identifieras på olika bussar, men också när en hetpluggad enhet ansluts. När väl enheten är redo för användning skickas ett meddelande till udevd so att det kan skapa motsvarande enhet i /dev/.

9.11.2. Namngivningsproblemet

Innan uppkomsten av hetplugganslutningar var det enkelt att tilldela ett fast namn till en enhet. Det baserades endast på enhetens position på varje buss. Detta är inte möjligt med enheter som kan läggas till och tas bort från på en bus. Det typiska fallet är användningen av en digitalkamera, en USB-sticka, där båda för datorn kommer att tolkas som diskenheter. Den första som ansluts kan vara /dev/sdb och den andra /dev/sdc (med /dev/sda representerande datorns egna hårddisk). Enhetsnamnet är inte fast; det beror på ordningen enheterna är anslutna i.
Utöver det använder fler och fler drivrutiner dynamiska värden för enheters major/minornummer, vilket gör det omöjligt att ha statiska poster för de givna enheterna, eftersom dessa essentiella kännetecken kan variera efter omstart.
udev skapades för att lösa detta problem.

9.11.3. Hur udev fungerar

udev av kärnan aviseras om att en ny enhet identifieras samlar den in information om den givna enheten genom att slå upp motsvarande poster i /sys/, specifikt de som identifierar den unikt (MAC-adressen för ett nätverkskort, serienummer för en del USB-enheter och så vidare.).
Utrustad med all denna information konsulterar udev sedan alla regler i /etc/udev/rules.d/ och /lib/udev/rules.d/. I denna process bestäms hur enheten ska namnges, vilka symboliska länkar att skapa (för att ge den alternativa namn) och vilka kommandon att köra. Alla dessa filer beaktas och reglerna utvärderas sekvensiellt (förutom när en fil använder "GOTO"-direktiv). Det kan därför finnas flera regler som motsvarar en given händelse.
Syntaxen för regelfiler är ganska enkel: varje rad innehåller valkriteria och variabeltilldelningar. Den förstnämnda används för att välja händelser för vilka det finns ett behov att agera och den senare definierar vilken åtgärd som ska utföras. De är alla separerade med komman, och operatorn skiljer implicit mellan ett valkriteria (med jämförelseoperatorer som ==, != eller en tilldelning (med operatorer) som =, += eller :=).
Jämförelseoperatorer används för följande variabler:
  • KERNEL. namnet som kärnan tilldelar enheten;
  • ACTION: åtgärd som svarar mot händelsen (”add” när en enhet har blivit tillagd, ”remove” när den har blivit borttagen);
  • DEVPATH: sökvägen för enhetens /sys/-post;
  • SUBSYSTEM: subsystemet i kärnan som genererade begäran (det finns många, men några exempel är “usb”, “ide”, “net”, “firmware” och så vidare);
  • ATTR{attribute}: filinnehåll av attribute filen i /sys/$devpath/-katalogen för enheten. Det är här du finner MAC-adressen och andra buss-specifika identifierare;
  • KERNELS, SUBSYSTEMS och ATTRS{attributes} är variationer som kommer att försöka att matcha olika alternativ för en av föräldraenheterna till aktuell enhet;
  • PROGRAM: delegerar testet till indikerat program (sant om det returnerar 0, annars falsk). Innehållet från programmets standardutmatning lagras så att det kan återanvändas i testet för RESULT;
  • RESULT: exekvera tester på standarutmatningen lagrad vid senaste körning av PROGRAM.
Högra operanderna kan använda mönstermatchning för att matcha flera värden samtidigt. Till exempel matchar * alla strängar (även en tom sträng); ? matchar varje tecken och [] matchar tecknen listade mellan hakparenteserna (eller tvärtom om första tecknet är ett utropstecken; intervall av tecken indikeras av a-z.
Gällande tilldelningsoperatorerna tilldelar = ett värde (och ersätter det nuvarande värdet); om det gäller en lista töms den och innehåller det tilldelade värdet. := gör likadant men förhindrar senare ändringar till samma variabel. += lägger till i listan. Följande variabler kan ändras:
  • NAME: enhetsfilnamnet att skapas i /dev/. Endast den första tilldelningen beaktas; de andra ignoreras;
  • SYMLINK: listan över symboliska länkar som kommer att peka på samma enhet;
  • OWNER, GROUP och MODE definierar användare och grupp som äger enheten såväl som tillhörande behörighet;
  • RUN: listan över program att exekvera som svar på denna händelse.
Värden tilldelade till dessa variabler kan använda ett antal substitioner:
  • $kernel eller %k: motsvarande KERNEL;
  • $number eller %n: enhetens ordningsnummer, till exempel för sda3, skulle det vara “3”;
  • $devpath eller %p: motsvarande DEVPATH;
  • $attr{attribute} eller %s{attribute}: motsvarande ATTRS{attribut};
  • $major eller %M: kärnans major-version för enheten;
  • $minor eller %m: kärnans minor-version för enheten;
  • $result eller %c: strängutmatningen för de senaste programmet startat med PROGRAM;
  • och slutligen, %% och $$ för procent-- och dollar-tecken.
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. Ett konkret exempel

Låt oss titta närmare på att försöka tilldela ett fast namn till en enkel USB-nyckel. Först måste du hitta elementen som kommer att identifera den på ett unikt sätt. För att göra detta, plugga in den och kör udevadm info -a -n /dev/sdc (ersätt /dev/sdc med namnet tilldelat till nyckeln).
# 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"
[...]
För att skapa en ny regel kan du använda tester på enhetens variabler såväl som för föräldraenheterna. Fallet ovan låter oss skapa två regler:
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"
När dessa regler är listade i en fil, exempelvis /etc/udev/rules.d/010_local.rules kan du ta bort och återansluta USB-stickan. Du kan sedan se att /dev/usb_key/disk representerar disken associerade med USB-stickan och /dev/usb_key/part1/ dess första partition.