Product SiteDocumentation Site

9.11. Connectar en calent: hotplug

9.11.1. Introducció

El subsistema del nucli hotplug gestiona dinàmicament l'addició i eliminació de dispositius carregant els controladors apropiats i creant els fitxers corresponents de dispositiu (amb l'ajuda d'udevd). Amb el maquinari modern i la virtualització, gairebé tot es pot “connectar en calent”: des dels perifèrics USB/PCMCIA/IEEE 1394 a discs durs SATA, però també la CPU i la memòria.
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.
Les llistes anteriors no són completes (inclouen només els paràmetres més importants), però la pàgina del manual udev(7) hauria de ser exhaustiva.

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ó.