Product SiteDocumentation Site

9.11. Connectar en calent: hotplug

9.11.1. Introducció

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.
El nucli té una base de dades que associa cada ID de dispositiu amb el controlador necessari. Aquesta base de dades s'utilitza durant l'arrencada per carregar tots els controladors dels dispositius perifèrics detectats en els diferents busos, però també quan es connecta un dispositiu addicional en calent. Un cop el dispositiu està preparat per al seu ús, s'envia un missatge a udevd de manera que podrà crear l'entrada corresponent a /dev/.

9.11.2. El problema dels noms

Abans de l'aparició de les connexions en calent era fàcil assignar un nom fix a un dispositiu. Es basava simplement en la posició dels dispositius en el seu bus respectiu. Però això no és possible quan aquests dispositius poden aparèixer i desaparèixer del bus. El cas típic és l'ús d'una càmera digital i un llapis USB, ja que ambdós apareixen a l'ordinador com a unitats de disc. El primer connectat pot ser /dev/sdb i el segon /dev/sdc (amb /dev/sda representant el propi disc dur de l'ordinador). El nom del dispositiu no és fix: depèn de l'ordre en què els dispositius es connectin.
Addicionalment, cada vegada més controladors utilitzen valors dinàmics per als números major/menor dels dispositius, la qual cosa fa impossible tenir entrades estàtiques per als dispositius donats, ja que aquestes característiques essencials poden variar després d'un reinici.
udev fou creat precisament per solucionar aquest problema.

9.11.3. Com funciona udev

Quan udev és notificat pel nucli de l'aparició d'un nou dispositiu, recopila informació sobre el dispositiu donat consultant les entrades corresponents a /sys/, especialment les que l'identifiquen de manera única (adreça MAC per a una targeta de xarxa, número de sèrie per a alguns dispositius USB, etc.).
Amb tota aquesta informació, udev consulta totes les regles contingudes a /etc/udev/rules.d/ i a /lib/udev/rules.d/. En aquest procés decideix com anomenar el dispositiu, quins enllaços simbòlics crear (per donar-li noms alternatius), i quines ordres executar. Tots aquests arxius són consultats, i les regles s'avaluen seqüencialment (excepte quan un arxiu utilitza directives «GOTO»). Per tant, pot haver-hi diverses regles que corresponen a un esdeveniment determinat.
La sintaxi dels fitxers de regles és força simple: cada línia conté criteris de selecció i assignacions variables. Els primers s'utilitzen per seleccionar esdeveniments per als quals hi ha necessitat de reaccionar, i els segons defineixen l'acció a dur a terme. Tots estan simplement separats amb comes, i un operador diferencia implícitament entre un criteri de selecció (amb operadors de comparació, com ara == o !=) o una directiva d'assignació (amb operadors com ara =, += o :=).
Els operadors de comparació s'utilitzen amb les següents variables:
  • KERNEL: el nom que el nucli assigna al dispositiu;
  • ACTION: l'acció corresponent a l'esdeveniment («add» quan s'ha afegit un dispositiu, «remove» quan s'ha eliminat);
  • DEVPATH: el camí a l'entrada /sys/ del dispositiu;
  • SUBSYSTEM: el subsistema del nucli que ha generat la sol·licitud (n'hi ha molts, però alguns exemples són «usb», «ide», «net», «firmware», etc.);
  • ATTR{atribut}: contingut del fitxeratribut al directori del dispositiu /sys/$camí_al_dispositiu/. Aquí és on trobareu l'adreça MAC i altres identificadors específics del bus;
  • KERNELS, SUBSYSTEMS i ATTRS{atributs}són variacions que intentaran fer coincidir les diferents opcions en un dels dispositius pare del dispositiu actual;
  • PROGRAM: delega la prova al programa indicat (cert si retorna 0, fals altrament). El contingut de la sortida estàndard del programa s'emmagatzema de manera que pugui ser reutilitzat pel test RESULT;
  • RESULT: executa proves en la sortida estàndard emmagatzemada durant l'última crida a PROGRAM.
Els operands a la dreta poden utilitzar expressions de patró per coincidir amb diversos valors al mateix temps. Per exemple, * coincideix amb qualsevol cadena (fins i tot buida); ? coincideix amb qualsevol caràcter, i [] coincideix amb el conjunt de caràcters enumerats entre els claudàtors (o el contrari si el primer caràcter és un signe d'exclamació; i rangs contigus de caràcters s'indiquen com a a-z).
Quant als operadors d'assignació, = assigna un valor (i substitueix el valor actual); en el cas d'una llista, serà buidada i només contindrà el valor assignat. := fa el mateix, però evita canvis posteriors a la mateixa variable. Pel que fa a +=, afegeix un element a una llista. Les següents variables es poden canviar:
  • NAME: el nom del fitxer del dispositiu que es crearà a /dev/. Només es tindrà en compte la primera assignació; la resta s'ignoren;
  • SYMLINK: la llista d'enllaços simbòlics que apuntaran al mateix dispositiu;
  • OWNER, GROUP i MODE defineixen l'usuari i el grup propietaris del dispositiu, així com el permís associat;
  • RUN: la llista de programes a executar en resposta a aquest esdeveniment.
Els valors assignats a aquestes variables poden utilitzar una sèrie de substitucions:
  • $kernel o %k: equivalent a KERNEL;
  • $number o %n: el número d'ordre del dispositiu, per exemple, per sda3 seria “3”;
  • $devpath o %p: equivalent a DEVPATH;
  • $attr{atribut} o %s{atribut}: equivalent a ATTRS{atribut};
  • $major o %M: el número major del nucli pel dispositiu;
  • $minor o %m: el número menor del nucli pel dispositiu;
  • $result o %c: la cadena de sortida de l'últim programa invocat per PROGRAM;
  • i, finalment, %% i $$ pels signes de percentatge i de dòlar, respectivament.
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. Un exemple concret

Considerem el cas d'una simple clau USB i intentem assignar-li un nom fix. En primer lloc, cal trobar els elements que l'identifiquin de manera única. Per a això, connecteu-lo i executeu udevadm info -a -n /dev/sdc (reemplaçant /dev/sdc amb el nom real assignat a la clau).
# 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"
[...]
Per crear una nova regla, es poden utilitzar proves en les variables del dispositiu, així com en les d'un dels dispositius pare. El cas anterior ens permet crear dues normes com aquestes:
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"
Una vegada que aquestes regles s'estableixen en un fitxer, anomenat per exemple /etc/udev/rules.d/010_local.rules, es pot simplement eliminar i tornar a connectar la clau USB. Llavors podeu veure que /dev/usb_key/disk representa el disc associat amb la clau USB, i /dev/usb_key/part1 n'és la primera partició.