Outils pour utilisateurs

Outils du site


issue92:labo_linux_1

Table des matières

1

In the last installment of our series, we went through a case study in which we configured, compiled and installed a bespoke kernel for the eeePC. This small, lightweight but quite outdated notebook still can be actually useful from time to time. However, its Celeron M Pentium III-based processor can benefit from a kernel that has been slimmed up by removing unneeded features, and compiled using the complete Pentium-III instruction set to make best use of available hardware.

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

THE PROC FILESYSTEM

We have already used the /proc virtual file-system in episode 3, “Configuring, compiling and installing the kernel” of this series, when we dumped file /proc/version_signature to see which exact kernel version we are running:

$ cat /proc/version_signature

Ubuntu 3.13.0-24.47-generic 3.13.9

To elaborate on this, it should be made clear that the /proc file-system - i.e. all files and directories hanging from this directory - do not have any real existence on disk.

This is contrary to “normal” files and directories such as /etc/passwd or /home that correspond to a specific location and data on your hard drive. When the user accesses one of these “real” files and directories, the kernel builds up internal data structures to represent an image in memory of the directory structure on the disk, using the routines defined in source directories fs/ext4, fs/btrfs, or similar.

LE SYSTÈME DE FICHIERS PROC

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

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.

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

In the case of /proc, the same data structures are built up in kernel memory, but without actually replicating an existing structure on disk. This is why they can be considered a virtual file system, since they retain the very same type of structure based on inodes as in a regular file system, but without accessing any data. Instead of which, the kernel will populate the (virtual) files with data from the kernel itself when files are accessed.

This is a quite flexible mechanism, since a file structure may be used to transfer data from the kernel to the user as in the above example, or may also be used to pass instructions from the user to the kernel itself, thus parameterizing the way it works while running. For example, network IP forwarding is off by default. On a computer that works as a router, forwarding between network interfaces may be turned on for IPv4 by issuing the command

# echo 1 > /proc/sys/net/ipv4/ip_forward

and for IPv6 with

# echo 1 > /proc/sys/net/ipv6/conf/all/forwarding

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 lui-même quand les fichiers seront consultés.

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

et pour l'IPv6 avec la commande :

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

4

ANATOMY OF A SIMPLE /proc ENTRY

The /proc file-system is controlled by source code in the fs/proc/ subdirectory. To take a simple example, consider the code that generates the /proc/version file on the fly:

$ cat /proc/version

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

This code is contained in source code file fs/proc/version.c. The complete source code (as of kernel version 3.13) is shown right.

Let's go over this part by part. The first few #include lines include header files from source code directory include/linux/: • <kernel.h> contains very basic macro definitions used across the source code. • <init.h> contains initialization code, and specifically the code that we will use to initialize a module. • <fs.h> contains basic definitions for any part of the file-system, such as codes to indicate file open for read and for write, etc. • <proc_fs.h> contains similar code, but this time specific to the /proc file-system. • <seq_file.h> defines common code for sequential file operations. • <utsname.h> contains code to access specific kernel data from within user space.

ANATOMIE D'UNE ENTRÉE SIMPLE DANS /PROC

Le système de fichiers /proc est contrôlé par le code source situé dans le sous-répertoire fs/proc/. Pour prendre un exemple simple, étudiez le code qui génère le fichier /proc/version à la volée :

$ cat /proc/version

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.

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 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. • <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-ci spécifiquement au système de fichiers /proc. • <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.

5

This may seem over-complicated to programmers used to handling C code, since most of the file-oriented routines may also be found in standard C header files such as <stdio.h>. However, it should be noted that these standard I/O routines such as “printf” are actually compiled into the glibc library file – which the kernel cannot access from disk until the file system is ready. In fact, if mounting the boot drive does not work at all, the kernel may very well not have access to the standard C libraries, while at the same time needing very much to emit an error message to inform the user about what is going on! This explains why file access and outputting text on screen needed to be built into the kernel itself. Primitives are similar but not always identical to the more familiar glib versions; for example, writing on screen is done with the “printk” command, though its syntax is in fact identical to “printf”.

Going down the code in file fs/proc/version.c, we see that the last line is

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 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 :

module_init(proc_version_init);

6

This is where the code controlling virtual file /proc/version is loaded into memory as a module. Now, so what about this “proc_version_init”? This (shown top right) is the procedure that is declared just above module initialization, that defines what should be done when the module is installed. All it needs to do is create the virtual file “version” within the /proc framework, and assign to it a table of callback functions that will be invoked when usual operations are performed on the virtual file.

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 (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 » (à 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

Immediately before, function “version_proc_open” has been defined as shown right (third down).

This simply does a sequential file write “seq_printf” of the linux version banner, the system name on which it is running, and release and version numbers.

MODIFYING A /proc ENTRY

The /proc/version file is perhaps not a very interesting proposition to modify. File /proc/cpuinfo spits out quite a lot of information that may allow more tweaking:

$ cat /proc/cpuinfo

[…] processor : 3 vendor_id : GenuineIntel cpu family : 6 model : 37 model name : Intel(R) Core(TM) i5 CPU M 460 @ 2.53GHz stepping : 5 microcode : 0x2 cpu MHz : 2527.207 cache size : 3072 KB […] (Some parts have been edited for brevity).

Juste avant, la fonction « version_proc_open » a été définie comme présentée à droite (troisième en bas).

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

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

[…] processor : 3 vendor_id : GenuineIntel cpu family : 6 model : 37 model name : Intel(R) Core(TM) i5 CPU M 460 @ 2.53GHz stepping : 5 microcode : 0x2 cpu MHz : 2527.207 cache size : 3072 KB […] (Certaines parties ont été édité pour faire bref).

8

All this information is given for each processor core present in the system. This can be nice to have to identify a CPU or its number of cores – without needing to peruse the documentation on the chip itself. However, I have always been a little dubious about the presentation. I would prefer to have less information, but more pertinent to my specific needs.

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

9

This means we now need to find out where symbol “cpuinfo_op” has been pre-defined in the kernel source code. This can be searched out either using some imaginative configurations of the grep command, or perhaps more easily with the Linux Cross Reference tool at Free Electrons: http://lxr.free-electrons.com/ . This has a very useful search engine that will help us find words or symbols wherever they are within the linux kernel source code (try “Identifier search”).

In any case, the appropriate code can be found in file arch/x86/kernel/cpu/proc.c . If we study procedure:

static int show_cpuinfo(struct seq_file *m, void *v)

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

All this can be commented out, and replaced by our own code. It may be a good idea to mark the modifications with appropriate comments. I altered procedure show_cpuinfo in file arch/x86/kernel/cpu/proc.c and put my code shown right (second down).

This is a quick modification of the original code written by the kernel developers.

The kernel now needs to be recompiled, and then reinstalled in the /boot directory and the GRUB entries updated. Since we have modified no modules, it shall not be necessary to recompile and reinstall them, so if we have already compiled the kernel at least once previously, the process needs to do less work this time and should complete rather more rapidly. So:

$ make

$ sudo bash

# 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).

C'est une modification rapide du code original écrit par les développeurs du noyau.

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

$ sudo bash

# make install

11

When tampering with file arch/x86/kernel/cpu/proc.c, we commented out the use of several of the existing functions. The compiler will complain with the following message:

warning: ‘show_cpuinfo_core’ defined but not used [-Wunused-function]

static void show_cpuinfo_core(struct seq_file *m, struct cpuinfo_x86 *c,

This is not a problem for our simple experiment, though if submitting code to the actual linux kernel project, it would be best to clean things up a little. Otherwise, you may get shouted at!

When compilation is over and the new kernel is installed, boot from it. No visible changes should be observed on the desktop - the two mushrooms were already there on the old kernel. Now re-examine the contents of /proc/cpuinfo. It should now correspond to whatever you put in proc.c:

$ cat /proc/cpuinfo

CPU[0]: GenuineIntel Intel(R) Core(TM) i5 CPU M 460 @ 2.53GHz 2.527462 GHz cpu cores : 2 […]

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

This is more compact, looks better and is more readable to my eyes.

In this part of our series on compiling the linux kernel, we saw how to introduce small modifications into the kernel source code. The /proc virtual file-system is a good place to start trying out our own code, at first just to obtain information from the kernel. Later on, the more adventurous could also try the inverse: tweaking the kernel's internal processes while running.

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

issue92/labo_linux_1.txt · Dernière modification : 2015/03/08 10:41 de auntiee