Lorsque vous apprenez le système d'exploitation Linux, vous êtes-vous déjà posé cette question : pourquoi nos ordinateurs peuvent-ils reconnaître et utiliser différents périphériques USB ? Pourquoi le système Linux n’utilise-t-il pas la souris comme clavier ? Discutons du processus d'identification et de chargement des périphériques USB dans les systèmes Linux.
Lorsque nous connectons un périphérique USB à l'ordinateur, le noyau Linux détectera et chargera automatiquement le pilote correspondant afin que le périphérique puisse fonctionner correctement. Nous aborderons ensuite le processus d'identification et de chargement des périphériques USB dans les systèmes Linux.
Un contrôleur USB est un périphérique matériel utilisé pour contrôler les appareils sur le bus USB. Lorsque vous branchez un périphérique USB, le contrôleur USB détecte le changement de tension et envoie un signal d'interruption. Ce signal d'interruption est envoyé à la ligne d'interruption du contrôleur USB sur le processeur, indiquant au noyau Linux qu'un nouveau périphérique USB est branché.
Lorsque le noyau reçoit le signal d'interruption du contrôleur USB, il appellera le module usbcore
模块,该模块负责检测新的USB设备并加载相应的驱动程序。usbcore
du sous-système USB pour détecter d'abord le descripteur de périphérique. Ce descripteur comprend l'ID du fabricant de l'appareil, l'ID du produit, le code de catégorie et d'autres informations.
Si un pilote correspondant existe déjà, alors le module usbcore
chargera ce pilote. S'il n'y a pas de pilote correspondant, il tentera de charger un pilote universel prenant en charge la plupart des périphériques USB.
Une fois le bon pilote chargé, il s'enregistre auprès du sous-système USB et lui indique les périphériques qu'il peut gérer. Cette étape implique généralement l'enregistrement de la classe du périphérique USB (tel qu'un périphérique de stockage, un périphérique d'entrée, etc.) auprès du noyau.
Ce processus comprend l'enregistrement d'un nouveau pilote de périphérique USB auprès du noyau et la spécification de l'ID du fabricant du périphérique, de l'ID du produit et d'autres informations dans le pilote. Une fois le pilote enregistré avec succès, le sous-système USB peut faire correspondre le périphérique avec le bon pilote.
Le sous-système USB créera ensuite un nœud de périphérique pour le périphérique. Un nœud de périphérique est un fichier spécial dans le répertoire /dev
qui permet aux programmes de l'espace utilisateur de communiquer avec le périphérique. Le nom du nœud de périphérique est généralement généré dynamiquement par le noyau en fonction de l'ID du fabricant, de l'ID du produit, du numéro de série et d'autres informations du périphérique.
La création de nœuds de périphérique est réalisée via le processus udev
daemon. Ce démon surveille les événements de plug-in et de débranchement de périphérique dans le système et crée ou supprime automatiquement les nœuds de périphérique correspondants. Une fois le nœud de périphérique créé, le noyau peut attribuer des droits d'accès au périphérique aux programmes de l'espace utilisateur.
Une fois que le conducteur est informé qu'un nouveau périphérique est branché, il initialisera le périphérique. L'initialisation peut inclure la définition du taux de transfert de l'appareil, l'allocation de tampons mémoire, etc. Une fois l'initialisation du périphérique terminée, le pilote signale au sous-système USB que le périphérique est prêt.
Enfin, les programmes de l'espace utilisateur peuvent ouvrir des nœuds d'appareil et communiquer avec l'appareil. Les autorisations des nœuds de périphérique sont généralement définies pour autoriser uniquement l'accès à l'utilisateur root ou aux utilisateurs des groupes concernés. Les programmes de l'espace utilisateur peuvent utiliser des appels système (tels que read
和write
) pour envoyer des commandes à l'appareil et recevoir des données.
Grâce à ce processus, le système Linux peut identifier automatiquement le périphérique et charger le pilote correspondant afin que le périphérique puisse fonctionner normalement. C'est également pour cela que lorsque nous connectons un périphérique USB, nous n'avons pas besoin d'installer manuellement de pilotes ni d'effectuer d'autres opérations pour commencer à utiliser directement le périphérique.
Lorsque vous branchez un périphérique USB, le système Linux effectuera automatiquement les étapes ci-dessus pour identifier automatiquement le périphérique et charger le pilote correspondant afin que le périphérique puisse fonctionner normalement. Analysons le processus au niveau du code.
Ci-dessous, j'expliquerai le processus d'identification et de chargement des périphériques USB dans le système Linux à travers quelques exemples de codes. Ces exemples de codes servent simplement à expliquer les principes, le code réel sera plus complexe.
Lorsqu'un périphérique USB est branché sur le système, un signal d'interruption est généré, qui est capturé par la ligne d'interruption du contrôleur USB sur le processeur et traité par le sous-système USB du noyau. Voici un exemple de code qui montre comment détecter les événements de branchement et de débranchement d'un périphérique USB :
#include #include int main() { libusb_device **devs; libusb_context *ctx = NULL; int r = libusb_init(&ctx); if (r printf("Failed to initialize libusb\n"); return 1; } // 扫描USB总线并列出所有连接的设备 ssize_t cnt = libusb_get_device_list(ctx, &devs); if (cnt printf("Failed to get device list\n"); return 1; } // 遍历设备列表,检测插入和拔出事件 for (int i = 0; i if (r printf("Failed to get device descriptor\n"); continue; } printf("Vendor ID: 0x%04x, Product ID: 0x%04x\n", desc.idVendor, desc.idProduct); } // 释放设备列表 libusb_free_device_list(devs, 1); // 退出libusb libusb_exit(ctx); return 0; }
Ce code utilise la bibliothèque libusb, qui est une bibliothèque en langage C utilisée pour accéder aux périphériques USB. Il fournit une API pour initialiser le sous-système USB et analyser le bus USB, ainsi qu'une API pour accéder aux périphériques USB.
Une fois qu'un périphérique est détecté comme branché, le sous-système USB tente de charger un pilote approprié. Vous trouverez ci-dessous un exemple de code de pilote responsable de la prise en charge des périphériques de stockage USB (tels que les clés USB) :
#include #include static struct usb_device_id storage_devices[] = { { USB_DEVICE(0xabcd, 0x1234) }, { USB_DEVICE(0xffff, 0xffff) }, { } }; MODULE_DEVICE_TABLE(usb, storage_devices); static int storage_probe(struct usb_interface *interface, const struct usb_device_id *id) { // 初始化设备并注册 return 0; } static void storage_disconnect(struct usb_interface *interface) { // 释放设备 } static struct usb_driver storage_driver = { .name = "usb-storage", .probe = storage_probe, .disconnect = storage_disconnect, .id_table = storage_devices, }; module_usb_driver(storage_driver);
Ce code présente un pilote simple capable de gérer les événements de plug-in et de débranchement des périphériques de stockage USB. Lors du chargement d'un pilote, le noyau recherche dans la liste des pilotes chargés un pilote correspondant au périphérique.
Si un pilote correspondant est trouvé, le noyau utilisera ce pilote pour gérer le périphérique. Si aucun pilote correspondant n'est trouvé, le noyau ne chargera aucun pilote.
Une fois qu'un pilote correspondant au périphérique est trouvé, le pilote sera chargé et démarré, il tentera d'initialiser le périphérique et de l'enregistrer auprès du noyau. Voici un exemple de code montrant comment initialiser un périphérique de stockage USB et l'enregistrer auprès du noyau :
static int storage_probe(struct usb_interface *interface, const struct usb_device_id *id) { struct usb_device *dev = interface_to_usbdev(interface); // 获取设备描述符 struct usb_device_descriptor desc; int r = usb_get_descriptor(dev, USB_DT_DEVICE, 0, &desc, sizeof(desc)); if (r "Failed to get device descriptor\n"); return r; } // 打印设备信息 printk(KERN_INFO "USB storage device detected: Vendor ID=0x%04x, Product ID=0x%04x\n", desc.idVendor, desc.idProduct); // 初始化设备并注册到内核 // ... return 0; }
L'exemple de code ci-dessus utilise la fonction usb_get_descriptor()
函数来获取设备描述符,并使用printk()
du noyau pour enregistrer les informations sur le périphérique dans le journal du noyau.
Le pilote appellera la fonction d'initialisation du périphérique et l'enregistrera auprès du noyau, mais comme le processus d'initialisation et d'enregistrement du périphérique varie d'un périphérique à l'autre, cette partie du code est omise ici.
一旦设备已经被注册到内核,用户空间程序就可以通过设备节点来访问设备。在Linux系统中,设备节点是一种特殊的文件,可以通过标准文件I/O函数来访问。下面是一个示例代码,演示如何打开并读取USB存储设备:
#include #include #include int main() { // 打开设备节点 int fd = open("/dev/sdb", O_RDONLY); if (fd printf("Failed to open device\n"); return 1; } // 读取设备数据 char buf[1024]; ssize_t n = read(fd, buf, sizeof(buf)); if (n printf("Failed to read device\n"); close(fd); return 1; } // 关闭设备节点 close(fd); return 0; }
这段代码使用了标准的文件I/O函数来访问设备节点。在这个例子中,设备节点的路径是/dev/sdb
,这是一个典型的USB存储设备节点。接下来,程序将设备节点作为文件打开,并使用read()
函数从设备中读取数据。一旦完成数据的读取,程序将关闭设备节点并退出。
Linux系统识别USB设备的过程可以分为四个步骤:设备连接、驱动匹配、设备注册和设备访问。当用户将USB设备插入计算机时,内核将通过USB总线来检测设备的插入事件,并尝试查找与设备匹配的驱动程序。一旦找到了匹配的驱动程序,驱动程序将被加载并启动,它将尝试对设备进行初始化,并将其注册到内核。一旦设备已经被注册到内核,用户空间程序就可以通过设备节点来访问设备。
在Linux系统中,驱动程序是非常重要的组成部分,它们负责管理和控制系统中的各种设备。对于USB设备而言,内核提供了一个通用的USB驱动框架,它可以自动检测和加载驱动程序,并为用户提供了一个简单而强大的USB设备访问接口。通过深入理解USB驱动程序的工作原理,我们可以更好地理解Linux系统中设备管理的内部机制,这对于开发和调试设备驱动程序非常有帮助。
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!