Les caches mémoire

From Deimos.fr / Bloc Notes Informatique
Jump to: navigation, search
Linux

Software version Kernel 2.6.32+
Operating System Red Hat 6.3
Debian 7
Website Kernel Website
Last Update 12/09/2012
Others

1 L'allocation de pages

Reporter l'allocation de la mémoire lorsqu'un process le demande est bon pour les performances. A cause de localité de référence, la plupart des programmes qui demandent des allocations de mémoire de grande taille n'allouent pas celle ci d'un coup. Dans le cas d'une allocation de mémoire pour un programme, celle ci sera fera au fur et a mesure pour ne pas utiliser plus que nécessaire.

Il faut comprendre qu'il y a également une gestion des priorités en fonction de qui fait la demande. Pour l'allocation de mémoire virtuelle par exemple, lorsque le kernel fait une demande, la mémoire est allouée immédiatement, alors qu'une demande d'un utilisateur se fera petit à petit au fur et à mesure du besoin. Il y a de bonnes raisons à ces choix d'allocation. En fait beaucoup de programmes consommateurs en RAM ont des sections qui sont rarement utilisées. Il est donc inutile de tout chargé en mémoire si tout n'est pas utilisé. Cela permet de pouvoir éviter le gâchis de mémoire. Un process dont l'allocation mémoire est reportée durant la dernière minute est référencé comme étant en demande de pagination.

Il est possible de tuner un peu cette allocation pour les applications qui ont l'habitude d'allouer de gros blocks et qui libèrent cette même mémoire. Ca fonctionne également bien pour les applications qui allouent beaucoup d'un coup et quittent ensuite. Il faut jouer au niveau de sysctl :

Configuration File /etc/sysctl.conf
vm.min_free_kbytes=<value>

Cela permet de réduite les temps de demande de pagination, la mémoire est utilisée réellement que pour ce dont elle a besoin et ça peut mettre de la pression sur la ZONE_NORMAL[1].

2 Gestion de l'overcommit

Il est avantageux pour certaines applications de laisser au kernel le droit d'allouer plus de mémoire que ce que le système peut offrir. Ceci peut être fait avec la mémoire virtuelle. En utilisant le paramètre vm.overcommit_memory dans sysctl, il est donc possible de demander au kernel d’autoriser une application à faire pleins de petites allocations :

Configuration File /etc/sysctl.conf
vm.overcommit_memory=1

Pour désactiver cette fonctionnalité :

Configuration File /etc/sysctl.conf
vm.overcommit_memory=0

Il est possible également d'utiliser la valeur 2. Celle ci permet d'overcommiter d'une taille égale à la swap + 50% de la mémoire physique. Les 50% peuvent être changé via le paramètre ratio :

Configuration File /etc/sysctl.conf
vm.overcommit_memory=2
vm.overcommit_ratio=50

Pour estimer la taille de la RAM qui est nécessaire pour éviter un OOM (Out Of Memory) condition pour la charge de travaille actuelle du système :

Command grep
> grep -i Committed_AS /proc/meminfo  
Committed_AS:    3458788 kB

Généralement l'overcommit est utile pour les application scientifiques ou créées en Fortran.

3 Slab Cache

Le Slab cache contient les pool de mémoire pré alloués par le kernel dont il va aller puiser lorsqu'il aura besoin de donner de l'espace à différents types de structures de données. A partir du moment ou ces structures de données ne mappent que des pages toutes petites ou que celles ci sont si petites que plusieurs d'entre elles entrent dans une seule page, alors il est plus efficace pour le kernel d'allouer de la mémoire pré allouée depuis l'espace mémoire Slab. Pour obtenir ces informations :

Command
> cat /proc/slabinfo
slabinfo - version: 2.1
# name            <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> : tunables <limit> <batchcount> <sharedfactor> : slabdata <active_slabs> <num_slabs> <sharedavail>
ext4_groupinfo_1k     31     60    128   30    1 : tunables  120   60    8 : slabdata      2      2      0
jbd2_1k                0      0   1024    4    1 : tunables   54   27    8 : slabdata      0      0      0
ext4_groupinfo_4k   7419   7420    136   28    1 : tunables  120   60    8 : slabdata    265    265      0
ext4_inode_cache   98966  98980    872    4    1 : tunables   54   27    8 : slabdata  24745  24745      0
ext4_xattr             0      0     88   44    1 : tunables  120   60    8 : slabdata      0      0      0
ext4_free_data         1     67     56   67    1 : tunables  120   60    8 : slabdata      1      1      0
ext4_allocation_context      8     28    136   28    1 : tunables  120   60    8 : slabdata      1      1      0
ext4_prealloc_space     31     37    104   37    1 : tunables  120   60    8 : slabdata      1      1      0
ext4_system_zone       0      0     40   92    1 : tunables  120   60    8 : slabdata      0      0      0
ext4_io_end            1      3   1128    3    1 : tunables   24   12    8 : slabdata      1      1      0
ext4_io_page          53    202     16  202    1 : tunables  120   60    8 : slabdata      1      1      0
jbd2_inode          1775   2002     48   77    1 : tunables  120   60    8 : slabdata     26     26      0
jbd2_journal_handle     64    144     24  144    1 : tunables  120   60    8 : slabdata      1      1      0
jbd2_journal_head    590    680    112   34    1 : tunables  120   60    8 : slabdata     20     20      0
jbd2_revoke_table     10    202     16  202    1 : tunables  120   60    8 : slabdata      1      1      0
jbd2_revoke_record      0      0     32  112    1 : tunables  120   60    8 : slabdata      0      0      0
kcopyd_job             0      0   3240    2    2 : tunables   24   12    8 : slabdata      0      0      0
io                     0      0     64   59    1 : tunables  120   60    8 : slabdata      0      0      0
dm_uevent              0      0   2608    3    2 : tunables   24   12    8 : slabdata      0      0      0
dm_rq_clone_bio_info      0      0     16  202    1 : tunables  120   60    8 : slabdata      0      0      0
dm_rq_target_io        0      0    408    9    1 : tunables   54   27    8 : slabdata      0      0      0
dm_target_io         856    864     24  144    1 : tunables  120   60    8 : slabdata      6      6      0
dm_io                798    920     40   92    1 : tunables  120   60    8 : slabdata     10     10      0
bio-1                  7     20    192   20    1 : tunables  120   60    8 : slabdata      1      1      0
sd_ext_cdb             2    112     32  112    1 : tunables  120   60    8 : slabdata      1      1      0
scsi_sense_cache      60     60    128   30    1 : tunables  120   60    8 : slabdata      2      2      0
scsi_cmd_cache        45     45    256   15    1 : tunables  120   60    8 : slabdata      3      3      0
uhci_urb_priv          3     67     56   67    1 : tunables  120   60    8 : slabdata      1      1      0
sgpool-128             2      2   4096    1    1 : tunables   24   12    8 : slabdata      2      2      0
sgpool-64              2      2   2048    2    1 : tunables   24   12    8 : slabdata      1      1      0
[...]

Pour avoir une vue un peu moins détaillée :

Command vmstat
> vmstat -m
Cache                       Num  Total   Size  Pages
ext4_groupinfo_1k            31     60    128     30
jbd2_1k                       0      0   1024      4
ext4_groupinfo_4k          7419   7420    136     28
ext4_inode_cache          98971  98984    872      4
ext4_xattr                    0      0     88     44
ext4_free_data               18     67     56     67
ext4_allocation_context      16     28    136     28
ext4_prealloc_space          37     37    104     37
ext4_system_zone              0      0     40     92
ext4_io_end                   2      3   1128      3
ext4_io_page                 73    202     16    202
jbd2_inode                 1814   2002     48     77
jbd2_journal_handle           9    144     24    144
jbd2_journal_head           609    680    112     34
jbd2_revoke_table            10    202     16    202
jbd2_revoke_record            0      0     32    112
kcopyd_job                    0      0   3240      2
io                            0      0     64     59
dm_uevent                     0      0   2608      3
dm_rq_clone_bio_info          0      0     16    202
dm_rq_target_io               0      0    408      9
dm_target_io                803    864     24    144
dm_io                       801    920     40     92
[...]

Un utilitaire existe également qui permet de monitorer en temps réel ce cache Slab, vous pouvez utiliser la commande slabtop :

Command slabtop
> slabtop
 Active / Total Objects (% used)    : 468837 / 561926 (83,4%)
 Active / Total Slabs (% used)      : 46669 / 46681 (100,0%)
 Active / Total Caches (% used)     : 108 / 186 (58,1%)
 Active / Total Size (% used)       : 158581,33K / 169955,34K (93,3%)
 Minimum / Average / Maximum Object : 0,02K / 0,30K / 4096,00K
 
  OBJS ACTIVE  USE OBJ SIZE  SLABS OBJ/SLAB CACHE SIZE NAME                   
154993  80496  51%    0,10K   4189       37     16756K buffer_head
119300 119300 100%    0,19K   5965       20     23860K dentry
 99016  99012  99%    0,85K  24754        4     99016K ext4_inode_cache
 28615  24772  86%    0,06K    485       59      1940K size-64
 18810  18601  98%    0,17K    855       22      3420K vm_area_struct
 15561  12820  82%    0,55K   2223        7      8892K radix_tree_node
 15045  14103  93%    0,25K   1003       15      4012K filp
 14715  14674  99%    0,14K    545       27      2180K sysfs_dir_cache
 14560  11186  76%    0,03K    130      112       520K size-32
 12474  11745  94%    0,05K    162       77       648K anon_vma_chain
  9420   9218  97%    0,62K   1570        6      6280K shmem_inode_cache
  9120   8814  96%    0,50K   1140        8      4560K size-512
[...]

Lorsqu'un process fait référence à un fichier, le kernel créer et associe un 'dentry object' pour chaque élément au niveau se son pathname. Par exemple pour /home/pmavro/.zshrc, le kernel va créer 4 'dentry objects' :

  1. /
  2. home
  3. pmavro
  4. zshrc

Chaque dentry object pointe vers l'inode associé à son fichier. Afin d'éviter à chaque utilisation de ces même paths la lecture sur disque, le kernel utilise le dentry cache ou sont stockés les dentry objects. Pour les mêmes raisons, le kernel cache également les informations sur les inodes qui sont donc contenu dans le slab.

4 Le cache ARP

Beaucoup de problèmes de performances réseaux peuvent être due au cache ARP étant trop faible. Par défaut, celui ci est limité à 512 entrées soft et 1024 hard au niveau des Ulimits. La limite soft devient une limite hard après 5 secondes. Lorsque cette limite est dépassée, le kernel fait un garbage collector et scan le cache pour purger les entrées pour rester en dessous de cette limite. Ce garbage collector peut également entrainer une suppression complète du cache. Admettons que votre cache est limité à 1 entrée dans le cache mais vous vous connectez depuis 2 machines distantes. Chaque packets entrant et sortant vont causer un garbage collector et une réinsertion dans le cache ARP. Il y aura donc un changement permanent dans le cache. Pour vous donner une idée de ce qui peut se passer sur un système :

Routing-cache-stats.png[2]

Pour voir les entrées ARP qui mappent les adresses matériels vers les adresses des protocoles :

Command grep
> grep -i arp /proc/slabinfo 
arp_cache              4      8    448    8    1 : tunables   54   27    8 : slabdata      1      1      0

Trop d'entrées ARP dans le cache mettent la pression sur la ZONE_NORMAL. Pour lister les entrées ARP, il existe 2 solution :

Command ip
> ip neighbor list
10.101.0.254 dev eth0 lladdr 00:25:45:db:71:57 REACHABLE

ou

Command cat
> cat /proc/net/arp 
IP address       HW type     Flags       HW address            Mask     Device
10.101.0.254     0x1         0x2         00:25:45:db:71:57     *        eth0

Pour vider le cache ARP :

Command ip
ip neighbor flush dev eth0

Il est possible de faire quelques réglages du cache ARP en spécifiant la limite soft, hard et la fréquence à laquelle le garbage collector doit être exécuté (en secondes) :

Configuration File /etc/sysctl.conf
net.ipv4.neigh.default.gc_thresh2=<soft_value>
net.ipv4.neigh.default.gc_thresh3=<hard_value>
net.ipv4.neigh.default.gc_interval=<gc_intrval_value>

Il existe également une autre option qui permet de régler le temps minimum des jiffies en espace utilisateur, vers les entrées en cache. Il y a 100 jiffies en espace utilisateur en 1 seconde :

Configuration File /etc/sysctl.conf
net.ipv4.neigh.default.locktime=<value>

5 Les pages cache

Un très grand pourcentage de l'activité de pagination est due aux IO. Pour la lecture depuis le disque vers la mémoire par exemple, ça forme des pages cache. Voici les cas de vérification du page cache pour les requêtes IO :

  • Lecture et écriture de fichiers
  • Lecture et écriture via des fichiers de type block device
  • Les accès à des fichiers mappés en mémoire
  • Les accès qui swappent des pages
  • La lecture de dossiers

Pour voir les allocations du page cache, il suffit de regarder les buffers caches :

Command grep
> grep -i buffer /proc/meminfo
Buffers:          225624 kB

Il est possible de tuner la taille mémoire des pages cache :

Configuration File /etc/sysctl.conf
vm.lowmem_reserve_ratio=<value>
vm.vfs_cache_presure=<value>

Et il est également possible de tuner le taux d'arrivée :

Configuration File /etc/sysctl.conf
vm.page-cluster=<value>
vm.zone_reclaim_mode=<value>

6 Les pages Anonymes

Sous Linux, seulement certains types de pages sont swappées. Il n'y a pas besoin de swapper des programmes de type texte du fait qu'ils existent déjà sur le disque. Également, pour la mémoire qui a été utilisée pour stocker le fichier dont le contenu a été modifié, le kernel va prendre les devants et écrire les données sur le fichier auquel il appartient vers le swap. Seules les pages qui n'ont pas d'association avec un fichier sont écrites en swap.

Le cache swap est utilisé pour garder une trace des pages qui ont été précédemment sorties du swap et qui n'ont pas été reswappées depuis. Si le kernel swap les threads qui ont besoin de swapper une page plus tard, si il trouve une entrée pour cette page en cache swap, il est possible de swapper sans avoir à l'écrire sur le disque.

Le fichier de chaque PID statm permet de voir les pages anonymes (ici PID 1) :

Command
> cat /proc/1/statm
2659 209 174 9 0 81 0

  • 2659 : total program size
  • 209 : resident set size (RSS)
  • 174 : shared pages (from shared mappings)
  • 9 : text (code)
  • 81 : data + stack

Ceci contient donc la RSS et la mémoire partagée utilisé par un process. Mais en fait le RSS que fournit le kernel est constitué les pages anonymes et partagées, d'où :

Pages anonymes = RSS - Partagée

7 SysV IPC

Une autre chose qui consomme de la mémoire est la mémoire pour les communications IPC.
Les Semaphores autorisent 2 ou plusieurs process pour coordonner les accès aux ressources partagées.
Les Message Queues autorisent les process à se coordonner pour les échanges de message. Les régions de Shared memory autorisent les process à communiquer en lisant et écrivant aux mêmes régions mémoire.

Un process peut souhaiter utiliser un de ces mécanismes mais doit créer des appels systèmes appropriés pour aller les ressources souhaitées.
Il est possible de mettre des limites à ces IPC sur systèmes SYSV. Pour voir la liste actuelle :

Command ipcs
> ipcs -l
 
------ Shared Memory Limits --------
max number of segments = 4096
max seg size (kbytes) = 32768
max total shared memory (kbytes) = 8388608
min seg size (bytes) = 1
 
------ Semaphore Limits --------
max number of arrays = 128
max semaphores per array = 250
max semaphores system wide = 32000
max ops per semop call = 32
semaphore max value = 32767
 
------ Messages Limits --------
max queues system wide = 7599
max size of message (bytes) = 8192
default max size of queue (bytes) = 16384

L'utilisation du /dev/shm peut être une solution pour réduire considérablement le temps de service de certaines applications. Attention toute fois à utiliser ce système comme espace de stockage temporaire du fait qu'il soit en mémoire. Il existe également une commande 'ipcrm' pour forcer la suppression des segments partagés en mémoire. Mais dans l'absolu, vous n'aurez jamais à utiliser cette commande.

Il est possible de tuner ces valeurs (présentes dans /proc/sys/kernel) via les sysctl :

Command cat
> cat sem 
250	32000	32	128

  • 250 : nombre maximum de sémaphores par tableaux de sémaphores
  • 32000 : nombre maximum de sémaphores allouées côté système
  • 32 : nombre maximum d'opérations allouées par appels systèmes de sémaphore
  • 128 : nombre de tableaux de sémaphore

Si vous souhaitez les modifier :

Configuration File /etc/sysctl.conf
kernel.sem = 250 256000  32 1024

Il y a d'autres paramètres intéressants (avec leurs valeurs par défaut) :

Configuration File /etc/sysctl.conf
# Nombre maximum de bytes dans un message en queue
kernel.msgmnb=16384
# Nombre maximum d'identifiants message dans la queue
kernel.msgmni=16
# Taille maximale 'un message qui peut être passé à un process (cette mémoire ne peut être swappée)
kernel.msgmax=8192
# Nombre maximum de segments en mémoire partagée côté système
kernel.shmmni=4096
# Taille maximale de des segments en mémoire partagées pouvant être créer. Un système 32 bits supporte au maximum jusqu'à 4G - 1
kernel.shmmax=33554432
# Montant total de mémoire partagé en pages qui peuvent être utilisés en une fois côté système. Cette valeur doit être au minimum kernel.shmmax/PAGE_SIZE (4KiB sur 32 bits)
kernel.shmall=2097152

Pour plus d'informations consultez le man 5 de proc.

8 Obtenir les informations mémoire

Il existe plusieurs solutions pour récupérer les tailles des mémoires. La plus connue d'entre elles est la commande free :

Command free
> free -ltm
             total       used       free     shared    buffers     cached
Mem:          3801       3520        281          0        224       1822
Low:          3801       3520        281
High:            0          0          0
-/+ buffers/cache:       1473       2328
Swap:         3811          4       3807
Total:        7613       3524       4089

Vous pouvez aussi récupérer les informations dans dmesg. Nous l'avons vu également plus haut, il est possible de récupérer dans meminfo la taille totale de l’espace virtuelle :

Command grep
> grep -i vmalloc /proc/meminfo 
VmallocTotal:   34359738367 kB
VmallocUsed:      560128 kB
VmallocChunk:   34359113168 kB

Pour voir la plus grosse taille de chunk libre :

Command grep
> grep -i chunk /proc/meminfo
VmallocChunk:   34359113168 kB

Pour les pages table :

Command vmstat
> vmstat -s
      3892968 K total memory
      3585172 K used memory
      1991172 K active memory
      1348148 K inactive memory
       307796 K free memory
       230100 K buffer memory
      1822744 K swap cache
      3903484 K total swap
         4140 K used swap
      3899344 K free swap
       397323 non-nice user cpu ticks
         6518 nice user cpu ticks
       102540 system cpu ticks
      5898943 idle cpu ticks
       146534 IO-wait cpu ticks
            1 IRQ cpu ticks
         1476 softirq cpu ticks
            0 stolen cpu ticks
     24899538 pages paged in
     24575197 pages paged out
           43 pages swapped in
         1061 pages swapped out
     38389133 interrupts
     74156999 CPU context switches
   1347436271 boot time
       171650 forks

Pour les allocations IO, il existe iomem :

Command cat
> cat /proc/iomem 
00000000-0000ffff : reserved
00010000-0009fbff : System RAM
0009fc00-0009ffff : RAM buffer
000a0000-000bffff : PCI Bus 0000:00
000c0000-000effff : PCI Bus 0000:00
  000c0000-000c7fff : Video ROM
  000ce800-000cffff : Adapter ROM
000f0000-000fffff : PCI Bus 0000:00
  000f0000-000fffff : reserved
    000f0000-000fffff : System ROM
00100000-cd9ffbff : System RAM
  01000000-01354585 : Kernel code
  01354586-0169367f : Kernel data
  01727000-01805fff : Kernel bss
cd9ffc00-cda53bff : ACPI Non-volatile Storage
cda53c00-cda55bff : ACPI Tables
cda55c00-dfffffff : reserved
  cdb00000-dfffffff : PCI Bus 0000:00
    d0000000-dfffffff : 0000:00:02.0
e0000000-efffffff : PCI MMCONFIG 0000 [bus 00-ff]
  e0000000-efffffff : reserved
f0000000-fed003ff : reserved
  f0000000-fec00000 : PCI Bus 0000:00
    f0000000-f01fffff : PCI Bus 0000:02
[...]

9 References

  1. ^ L'adressage mémoire et son allocation#UMA
  2. ^ http://vincent.bernat.im/fr/blog/2011-ipv4-route-cache-linux.html