Les deux révisions précédentesRévision précédenteProchaine révision | Révision précédente |
issue92:labo_linux_1 [2015/03/05 16:50] – [8] vincent | issue92:labo_linux_1 [2015/03/08 10:41] (Version actuelle) – [12] auntiee |
---|
**In this episode, we will start hacking the kernel code itself, by making small changes in the source code and seeing what comes out of it. To start with something simple, but that can help give us some insight into the kernel source code internals, we will make some simple changes in the /proc file-system. This also has the advantage of introducing the reader into one of the most used mechanisms that tell the system user about what is happening within the kernel.** | **In this episode, we will start hacking the kernel code itself, by making small changes in the source code and seeing what comes out of it. To start with something simple, but that can help give us some insight into the kernel source code internals, we will make some simple changes in the /proc file-system. This also has the advantage of introducing the reader into one of the most used mechanisms that tell the system user about what is happening within the kernel.** |
| |
Dans le dernier épisode de notre série, nous sommes passés par une étude de cas dans laquelle nous avons configuré, compilé et installé un noyau sur-mesure pour l'eeePC. Ce petit notebook, léger mais quand même plutôt désuet peut en fait se montrer utile de temps en temps. Cependant, son processeur Celeron M basé sur le Pentium-III peut bénéficier d'un noyau aminci par la suppression de fonctionnalités inutiles, et compilé en utilisant le jeu d'instruction Pentium-III pour faire un meilleur usage du matériel à disposition. | Dans le dernier épisode de notre série, nous avons fait, étape par étape, une étude de cas dans laquelle nous avons configuré, compilé et installé un noyau sur mesure pour l'eeePC. Ce petit notebook, léger, mais plutôt désuet, peut en fait se montrer utile de temps en temps. Cependant, son processeur Celeron M basé sur le Pentium-III peut bénéficier d'un noyau aminci par la suppression de fonctionnalités inutiles, et compilé en utilisant le jeu d'instructions Pentium-III pour faire un meilleur usage du matériel à disposition. |
| |
Dans cet épisode, nous commencerons par bricoler le code du noyau en lui-même, en effectuant de petites modifications dans le code source et en observant ce qui en résulte. Pour commencer par le plus simple, mais qui peut aider à nous donner quelques aperçus du du fonctionnement interne du code source du noyau, nous effectuerons quelques modifications simples dans le système de fichiers /proc. Cela aura aussi l'avantage d'initier le lecteur à l'un des mécanismes les plus utilisés qui dit à l'utilisateur du système ce qui se passe à l'intérieur du noyau. | Dans cet épisode, nous commencerons par bricoler le code du noyau lui-même, en effectuant de petites modifications dans le code source et en observant ce qui en résulte. Pour commencer par le plus simple, mais qui peut aider à nous donner quelques aperçus du fonctionnement interne du code source du noyau, nous effectuerons quelques modifications simples dans le système de fichiers /proc. Cela aura aussi l'avantage d'initier le lecteur à l'un des mécanismes les plus utilisés qui dit à l'utilisateur du système ce qui se passe à l'intérieur du noyau. |
====== 2 ====== | ====== 2 ====== |
| |
LE SYSTÈME DE FICHIERS PROC | LE SYSTÈME DE FICHIERS PROC |
| |
Nous avons déjà utilisé le pseudo système de fichiers /proc dans l'épisode 3 de cette série, « Configuration, compilation et installation du noyau », quand nous avons consulté le fichier /proc/version_signature pour voir quelle version du noyau nous utilisions exactement. | Nous avons déjà utilisé le système de fichiers virtuel /proc dans l'épisode 3 de cette série, « Configuration, compilation et installation du noyau », quand nous avons consulté le fichier /proc/version_signature pour voir quelle version du noyau nous utilisions exactement. |
| |
$ cat /proc/version_signature | $ cat /proc/version_signature |
Ubuntu 3.13.0-24.47-generic 3.13.9 | Ubuntu 3.13.0-24.47-generic 3.13.9 |
| |
Pour être précis à ce sujet, il faut noter que le système de fichiers /proc - c'est à dire tous les fichiers et dossiers contenus dans ce répertoire - ne sont pas physiquement présents sur le disque. | Pour être précis à ce sujet, il faut noter que le système de fichiers /proc - c'est-à-dire tous les fichiers et dossiers contenus dans ce répertoire - ne sont pas physiquement présents sur le disque. |
| |
Cela s'oppose aux fichiers et dossier « normaux » tels /etc/passwd ou /home qui correspondent à un emplacement et des données spécifiques sur votre disque dur. Quand l'utilisateur accède à l'un de ces fichiers et dossiers « réels », le noyau développe des structures de données internes pour représenter une image dans la mémoire de la structure du répertoire présent sur le disque, en utilisant les fonctions définies dans les répertoires de sources fs/ext4, fs/btrfs, ou autres. | Cela s'oppose aux fichiers et dossiers « normaux » tels /etc/passwd ou /home qui correspondent à un emplacement et des données spécifiques sur votre disque dur. Quand l'utilisateur accède à l'un de ces fichiers et dossiers « réels », le noyau développe des structures de données internes pour représenter une image dans la mémoire de la structure du répertoire présent sur le disque, en utilisant les fonctions définies dans les répertoires de sources fs/ext4, fs/btrfs, ou autres. |
====== 3 ====== | ====== 3 ====== |
| |
**Default value '0' corresponds to the 'off' state, while any positive value turns the option on. Naturally, we must act as root since we are altering the system configuration. This change will be applied only while the kernel is active; it must be re-applied after a system reboot.** | **Default value '0' corresponds to the 'off' state, while any positive value turns the option on. Naturally, we must act as root since we are altering the system configuration. This change will be applied only while the kernel is active; it must be re-applied after a system reboot.** |
| |
Dans le cas de /proc, les mêmes structures de données sont accumulées dans la mémoire du noyau, mais sans répliquer une structure existante sur le disque. C'est pourquoi on peut le considérer comme un pseudo système de fichiers, étant donné qu'il conserve le même type de structure en inodes comme dans un système de fichiers standard, mais sans accéder à aucune donnée. Au lieu de quoi, le noyau remplira les (pseudo) fichiers avec des données du noyau en lui-même quand les fichiers seront consultés. | Dans le cas de /proc, les mêmes structures de données sont accumulées dans la mémoire du noyau, mais sans répliquer une structure existante sur le disque. C'est pourquoi on peut le considérer comme un pseudo système de fichiers, étant donné qu'il conserve le même type de structure en inodes comme dans un système de fichiers standard, mais sans accéder à aucune donnée. Au lieu de quoi, le noyau remplira les (pseudo) fichiers avec des données du noyau lui-même quand les fichiers seront consultés. |
| |
C'est un mécanisme plutôt souple, vu qu'une structure de fichier peut être utilisée pour transférer des données depuis le noyau vers l'utilisateur comme dans l'exemple ci-dessus, ou peut aussi être utilisé pour passer des instructions depuis l'utilisateur vers le noyau en lui-même, paramétrant ainsi son fonctionnement. Par exemple, le réacheminement IP sur le réseau est désactivé par défaut. Sur un ordinateur fonctionnant comme router, le réacheminement entre les interfaces réseau peut être activé pour l'IPv4 en rentrant la commande : | C'est un mécanisme très souple, vu qu'une structure de fichier peut être utilisée pour transférer des données depuis le noyau vers l'utilisateur, comme dans l'exemple ci-dessus, ou peut aussi être utilisée pour passer des instructions depuis l'utilisateur vers le noyau lui-même, paramétrant ainsi son fonctionnement. Par exemple, le réacheminement IP sur le réseau est désactivé par défaut. Sur un ordinateur fonctionnant comme routeur, le réacheminement entre les interfaces réseau peut être activé pour l'IPv4 en rentrant la commande : |
| |
# echo 1 > /proc/sys/net/ipv4/ip_forward | # echo 1 > /proc/sys/net/ipv4/ip_forward |
# echo 1 > /proc/sys/net/ipv6/conf/all/forwarding | # echo 1 > /proc/sys/net/ipv6/conf/all/forwarding |
| |
La valeur par défaut 'O' correspond à l'état de désactivation, tandis que n'importe quelle valeur positive active l'option. Naturellement, nous devons effectuer cette opération avec l'utilisateur root étant donné que nous altérons la configuration du système. Cette modification sera seulement appliquée tant que le noyau est actif; cette opération doit être répétée à chaque redémarrage. | La valeur par défaut 'O' correspond à l'état de désactivation, tandis que n'importe quelle valeur positive active l'option. Naturellement, nous devons effectuer cette opération avec l'utilisateur root étant donné que nous altérons la configuration du système. Cette modification sera seulement appliquée tant que le noyau est actif ; cette opération doit être répétée à chaque redémarrage. |
====== 4 ====== | ====== 4 ====== |
| |
Linux version 3.13.0-24-generic (buildd@batsu) (gcc version 4.8.2 (Ubuntu 4.8.2-19ubuntu1) ) #47-Ubuntu SMP Fri May 2 23:30:00 UTC 2014 | Linux version 3.13.0-24-generic (buildd@batsu) (gcc version 4.8.2 (Ubuntu 4.8.2-19ubuntu1) ) #47-Ubuntu SMP Fri May 2 23:30:00 UTC 2014 |
| |
Ce code est contenu dans le fichier source fs/proc/version.c. Le code source complet (comme celui du noyau 3.13) est présent ci-contre. | Ce code est contenu dans le fichier source fs/proc/version.c. Le code source complet (comme celui du noyau 3.13) est présenté ci-contre. |
| |
Parcourons-le point par point. Les quelques premières lignes #include incluent les fichiers d'en-tête depuis le répertoire source include/linux/ : | Parcourons-le point par point. Les quelques premières lignes #include incluent les fichiers d'en-tête depuis le répertoire source include/linux/ : |
| |
• <kernel.h> contient des définitions de macros très basiques utilisées à travers le code source. | • <kernel.h> contient des définitions de macros très basiques utilisées partout dans le code source. |
• <init.h> contient le code d'initialisation, et plus spécifiquement le code que nous utiliserons pour initialiser un module. | • <init.h> contient le code d'initialisation et, plus spécifiquement, le code que nous utiliserons pour initialiser un module. |
• <fs.h> contient des définitions basiques pour chaque partie du système de fichiers, comme des codes pour indiquer les fichiers ouverts pour lire et écrire, etc. | • <fs.h> contient des définitions basiques pour chaque partie du système de fichiers, comme des codes pour indiquer les fichiers ouverts pour lire et écrire, etc. |
• <proc_fs.h> comme le précédent, mais cette fois spécifiquement au système de fichiers /proc. | • <proc_fs.h> comme le précédent, mais cette fois-ci spécifiquement au système de fichiers /proc. |
• <seq_file.h> définit le code pour les opérations sur les fichiers séquentiel. | • <seq_file.h> définit le code pour les opérations sur les fichiers séquentiels. |
• <utsname.h> contient le code pour accéder à des données spécifiques au noyau depuis un espace utilisateur. | • <utsname.h> contient le code pour accéder à des données spécifiques au noyau depuis un espace utilisateur. |
====== 5 ====== | ====== 5 ====== |
**module_init(proc_version_init);** | **module_init(proc_version_init);** |
| |
Cela peut sembler trop compliqué pour des programmeurs habitués à maîtriser le langage C, puisque la plupart des processus orientés fichier peuvent aussi être trouvés dans des fichiers d'en-tête C standards tel <stdio.h>. Cependant, il devrait être précisé que ces fonctions I/O standards comme « printf » sont en fait compilées dans le fichier de bibliothèque glibc - auquel le noyau ne peut pas accéder depuis le disque avant que le système de fichiers ne soit prêt. En effet, si le montage du périphérique de démarrage ne fonctionne pas du tout, le noyau peut très bien ne pas avoir accès aux bibliothèques C standards, et dans le même temps avoir besoin d'émettre un message d'erreur pour informer l'utilisateur sur ce qui se trame ! Ceci explique pourquoi l'accès à des fichiers et l'affichage de texte sur l'écran nécessitent d'être compilés dans le noyau. Les premières version de glib sont plutôt similaires mais pas toujours identiques aux versions plus courantes; par exemple, l'affichage de texte sur l'écran se fait avec la commande « printk », bien que sa syntaxe soit identique à celle de « printf. » | Cela peut sembler trop compliqué pour des programmeurs habitués à maîtriser le langage C, puisque la plupart des processus orientés fichiers peuvent être trouvés aussi dans des fichiers d'en-tête C standards tel <stdio.h>. Cependant, il devrait être précisé que ces fonctions I/O standards comme « printf » sont en fait compilées dans le fichier de bibliothèque glibc, auquel le noyau ne peut pas accéder depuis le disque avant que le système de fichiers ne soit prêt. En effet, si le montage du périphérique de démarrage ne fonctionne pas du tout, le noyau peut très bien ne pas avoir accès aux bibliothèques C standards et, dans le même temps, avoir besoin d'émettre un message d'erreur pour informer l'utilisateur sur ce qui se trame ! Ceci explique pourquoi l'accès à des fichiers et l'affichage de texte sur l'écran nécessitent d'être compilés dans le noyau même. Les premières versions de glib sont plutôt similaires mais pas toujours identiques aux versions plus courantes ; par exemple, l'affichage de texte sur l'écran se fait avec la commande « printk », bien que sa syntaxe soit identique à celle de « printf ». |
| |
En descendant dans le code du fichier fs/proc/version.c, on peut voir à la dernière ligne : | En descendant dans le code du fichier fs/proc/version.c, on peut voir à la dernière ligne : |
**The table of callback functions is named “file_operations version_proc_fops” (right). Of these, only the open operation has been defined, in function “version_proc_open”. The other three functions are left at their default values.** | **The table of callback functions is named “file_operations version_proc_fops” (right). Of these, only the open operation has been defined, in function “version_proc_open”. The other three functions are left at their default values.** |
| |
C'est ici que le code contrôlant le fichier virtuel /proc/version est chargé dans la mémoire en tant que module. Qu'en est-il désormais de ce « proc_version_init » ? Ceci (présenté ci-contre) est le processus déclaré juste au dessus du module d'initialisation, qui définit ce qui devrait être fait quand ce dernier sera installé. Tout ce que nous avons à faire est de créer le fichier virtuel « version » dans le framework /proc, et lui assigner un tableau de fonctions de rappel qui seront invoquées quand des opérations seront effectuées sur le fichier virtuel. | C'est ici que le code contrôlant le fichier virtuel /proc/version est chargé dans la mémoire en tant que module. Qu'en est-il désormais de ce « proc_version_init » ? Ceci (en haut à droite) est le processus déclaré juste au dessus du module d'initialisation, qui définit ce qui devrait être fait quand ce dernier sera installé. Tout ce qu'il doit faire est de créer le fichier virtuel « version » dans le framework /proc, et lui assigner un tableau de fonctions de rappel qui seront invoquées quand des opérations seront effectuées sur le fichier virtuel. |
| |
Le tableau de fonctions de rappel se nomme « file_operations version_proc_fops » (ci-contre à droite). Parmi ces fonctions, seule l'opération d'ouverture a été définie, dans la fonction « version_proc_open. » Les trois autres fonctions sont laissées avec leur valeur par défaut. | Le tableau de fonctions de rappel se nomme « file_operations version_proc_fops » (à droite). Parmi ces fonctions, seule l'opération d'ouverture a été définie, dans la fonction « version_proc_open. » Les trois autres fonctions sont laissées avec leur valeur par défaut. |
====== 7 ====== | ====== 7 ====== |
| |
**(Some parts have been edited for brevity).** | **(Some parts have been edited for brevity).** |
| |
Juste avant, la fonction « version_proc_open » a été définie comme présenté à droite (troisième en bas). | Juste avant, la fonction « version_proc_open » a été définie comme présentée à droite (troisième en bas). |
| |
FIXME(This simply does a sequential file write “seq_printf” of the linux version banner), le nom du système sur lequel il est en fonctionnement, ainsi que son numéro de version. | Cela ne fait qu'un « seq_printf » de la bannière de la version du Linux, le nom du système sur lequel il est en fonctionnement, ainsi que son numéro de version. |
| |
MODIFIER UNE ENTRÉE /PROC | MODIFIER UNE ENTRÉE /PROC |
| |
Le fichier /proc/version n'est peut-être pas un exemple très intéressant à modifier. Le fichier /proc/cpuinfo retourne pas mal d'information qui pourraient permettre plus de personnalisation : | Le fichier /proc/version n'est peut-être pas un exemple très intéressant à modifier. Le fichier /proc/cpuinfo retourne pas mal d'informations qui pourraient permettre plus de personnalisation : |
| |
$ cat /proc/cpuinfo | $ cat /proc/cpuinfo |
**To find out how to change this, our first stop will be file cpuinfo.c in source code directory fs/proc. This file has a similar structure to version.c, except for the callback routine invoked when an open operation is performed on virtual file /proc/cpuinfo. In the case of cpuinfo.c, the procedure is called “cpuinfo_open”, and is simply defined as shown below.** | **To find out how to change this, our first stop will be file cpuinfo.c in source code directory fs/proc. This file has a similar structure to version.c, except for the callback routine invoked when an open operation is performed on virtual file /proc/cpuinfo. In the case of cpuinfo.c, the procedure is called “cpuinfo_open”, and is simply defined as shown below.** |
| |
Toutes ces informations sont données pour chaque cœur de processeur présent dans le système. Cela peut être bien d'avoir à identifier un CPU ou son nombre de cœurs - sans avoir besoin de consulter la documentation de la puce. Cependant, j'ai toujours été dubitatif sur la présentation. Je préférerais avoir moins d'informations, et qu'elles soient plus en adéquation avec mes besoins spécifiques. | Toutes ces informations sont données pour chaque cœur de processeur présent dans le système. Avoir la possibilité d'identifier un CPU ou son nombre de cœurs, sans avoir besoin de consulter la documentation de la puce, est sans doute utile. Cependant, j'ai toujours été dubitatif sur la présentation. Je préférerais avoir moins d'informations et qu'elles soient plus en adéquation avec mes besoins spécifiques. |
| |
Pour trouver comment modifier cela, nous nous arrêterons en premier sur le fichier cpuinfo.c dans le répertoire de sources fs/proc. Ce fichier a une structure similaire au fichier version.c, sauf en ce qui concerne la fonction de rappel invoquée quand une opération d'ouverture est effectuée sur le fichier virtuel /proc/cpuinfo. Dans le cas de cpuinfo.c, la procédure est appelée « cpuinfo_open », et est définie simplement comme présenté ci-dessous. | Pour trouver comment modifier cela, nous nous arrêterons en premier sur le fichier cpuinfo.c dans le répertoire de sources fs/proc. Ce fichier a une structure similaire au fichier version.c, sauf en ce qui concerne la fonction de rappel invoquée quand une opération d'ouverture est effectuée sur le fichier virtuel /proc/cpuinfo. Dans le cas de cpuinfo.c, la procédure est appelée « cpuinfo_open », et est définie simplement comme présenté ci-dessous. |
**we will see how the information given in /proc/cpuinfo is progressively appended sequentially to the output file. Starting at line number 60, we find that which is shown top right.** | **we will see how the information given in /proc/cpuinfo is progressively appended sequentially to the output file. Starting at line number 60, we find that which is shown top right.** |
| |
| Cela signifie que nous avons désormais besoin de trouver où a été prédéfini le symbole « cpuinfo_op » dans le code source du noyau. Cela peut être effectué soit en utilisant quelques commandes grep plutôt imaginatives, soit peut-être plus simplement avec l'outil Linux Cross Reference sur le site Free Electrons : http://lxr.free-electrons.com/. Cet outil dispose d'un moteur de recherche très pratique qui nous aidera à trouver les mots et les symboles où qu'ils soient dans le code source du noyau (essayez « Identifier search »). |
| |
| De toute façon, le code adéquat peut être trouvé dans le fichier arch/x86/kernel/cpu/proc.c. Si nous étudions la procédure : |
| |
| static int show_cpuinfo(struct seq_file *m, void *v) |
| |
| nous verrons comment l'information donnée dans le fichier /proc/cpuinfo est progressivement annexée de façon séquentielle au fichier de sortie. En commençant par la ligne 60, nous trouvons ce qui est présenté en haut à droite. |
====== 10 ====== | ====== 10 ====== |
| |
**# make install** | **# make install** |
| |
Tout ceci peut être commenté, et remplacé par notre propre code. Il peut être intéressant de marquer les modifications avec des commentaires appropriés. J'ai modifié le processus show_cpuinfo dans le fichier arch/x86/kernel/cpu/proc.c et ajouté mon code présenté ci-contre (deuxième en bas). | Tout ceci peut être commenté et remplacé par notre propre code. Il peut être intéressant de marquer les modifications avec des commentaires appropriés. J'ai modifié le processus show_cpuinfo dans le fichier arch/x86/kernel/cpu/proc.c et ajouté mon code présenté ci-contre (deuxième en bas). |
| |
C'est une modification rapide du code original écrit par les développeurs du noyau. | C'est une modification rapide du code original écrit par les développeurs du noyau. |
| |
Désormais le noyau requiert d'être recompilé, puis réinstallé dans le répertoire /boot et les entrées GRUB doivent être mises à jour. Puisque nous n'avons modifié aucun module, il ne doit pas être nécessaire de les recompiler ni de les réinstaller, donc si nous avons déjà compilé le noyau au moins une fois précédemment, le processus aura moins de travail cette fois-ci et devrait se terminer plus rapidement. Ainsi : | Désormais le noyau doit être recompilé, puis réinstallé dans le répertoire /boot et les entrées GRUB doivent être mises à jour. Puisque nous n'avons modifié aucun module, il n'est point nécessaire de les recompiler ni de les réinstaller ; ainsi, si nous avons déjà compilé le noyau au moins une fois précédemment, le processus aura moins de travail cette fois-ci et devrait se terminer plus rapidement : |
| |
$ make | $ make |
**[...]** | **[...]** |
| |
| En manipulant le fichier arch/x86/kernel/cpu/proc.c, nous avons commenté l'utilisation de plusieurs des fonctions existantes. Le compilateur s'en plaindra par le message suivant : |
| |
| warning: ‘show_cpuinfo_core’ defined but not used [-Wunused-function] |
| |
| static void show_cpuinfo_core(struct seq_file *m, struct cpuinfo_x86 *c, |
| |
| Ceci n'est pas un problème pour notre expérience basique, toutefois si nous soumettions ce code au projet du noyau Linux actuel, il serait mieux de nettoyer un peu les choses. Autrement vous pourriez vous faire crier dessus ! |
| |
| Quand la compilation est terminée et que le nouveau noyau est installé, démarrez dessus. Vous ne devriez pas apercevoir des changements visibles sur le bureau - les deux champignons étaient déjà là avec l'ancien noyau. Maintenant retournez voir le contenu du fichier /proc/cpuinfo. Il devrait désormais correspondre à tout ce que vous avez mis dans le fichier proc.c : |
| |
| $ cat /proc/cpuinfo |
| |
| CPU[0]: |
| GenuineIntel Intel(R) Core(TM) i5 CPU M 460 @ 2.53GHz |
| 2.527462 GHz |
| cpu cores : 2 |
| [...] |
====== 12 ====== | ====== 12 ====== |
| |
**In the next - and last - installment, we will try out a new module, written from scratch and added to the default kernel source tree.** | **In the next - and last - installment, we will try out a new module, written from scratch and added to the default kernel source tree.** |
| |
Cela est plus compact, plus joli et plus lisible pour mes yeux. | Cela est plus compact, plus joli et plus facile à lire. |
| |
Dans cet partie de notre série sur la compilation du noyau Linux, nous avons vu comment introduire des petites modifications dans le code source du noyau. Le pseudo système de fichiers /proc est un bon emplacement pour commencer à essayer notre propre code, en premier lieu pour obtenir des informations sur le noyau. Ultérieurement, les plus aventureux pourront également essayer l'inverse : la personnalisation des processus internes du noyau pendant son fonctionnement. | Dans cet partie de notre série sur la compilation du noyau Linux, nous avons vu comment introduire des petites modifications dans le code source du noyau. Le pseudo système de fichiers /proc est un bon emplacement pour commencer à essayer notre propre code, en premier lieu pour obtenir tout simplement des informations sur le noyau. Ultérieurement, les plus aventureux pourront également essayer l'inverse : la personnalisation des processus internes du noyau pendant son fonctionnement. |
| |
Dans le prochain - et dernier - épisode, nous essayerons un nouveau module, écrit de zéro et ajouté à l'arborescence des sources du noyau. | Dans le prochain - et dernier - épisode, nous essayerons un nouveau module, écrit de zéro et ajouté à l'arborescence des sources du noyau. |