Απομυθοποιώντας τη διαχείριση μνήμης στο Linux


Δεν είναι λίγες η φορές που νέοι χρήστες βλέπουν το πόσο «γεμίζει» η RAM του υπολογιστή τους και τρομάζουν από την κατανάλωση μνήμης που γίνεται. Τι συμβαίνει όμως και η μνήμη RAM γεμίζει;

Εισαγωγή

Ένα από τα σημαντικότερα υποσυστήματα όλων των σύγχρονων λειτουργικών συστημάτων είναι η διαχείριση μνήμης.

Αυτό περιλαμβάνει την υλοποίηση της «Εικονικής Μνήμης», της σελιδοποίησης, της δέσμευσης μνήμης τόσο για δομές του πυρήνα όσο και των προγραμμάτων χρήστη, την αντιστοίχηση αρχείων στο χώρο διευθύνσεων των διεργασιών που το απαιτούν και άλλα.

Ειδικά όσον αφορά το Linux, η διαχείριση μνήμης είναι ένα αρκετά περίπλοκο υποσύστημα, παραμετροποιήσιμο μέσω του procfs (/proc) και του sysctl

Στο άρθρο που ακολουθεί θα προσπαθήσουμε να αποσαφηνίσουμε μερικές βασικές έννοιες και διαδικασίες αυτού του υποσυστήματος. Θα αναλύσουμε τα επιμέρους τμήματα του για να κατανοήσουμε το πως και γιατί «γεμίζει» η μνήμη RAM και πως το διαχειρίζεται το Linux.

Χώρος Διευθύνσεων

Στο άρθρο αυτό θα αναφερόμαστε συχνά στον όρο «Χώρος Διευθύνσεων» (address space).
Ως χώρο διευθύνσεων θα εννοούμε ένα σύνολο διακριτών διευθύνσεων που «δείχνουν» σε κάποιο είδος μνήμης.

fysiki-mnimi-eikoniki-mnimi

Εικονική Μνήμη

Η εικονική μνήμη είναι η πρώτη έννοια που πρέπει να αποσαφηνίσουμε πριν προχωρήσουμε.

Εικονική μνήμη είναι ένα σύστημα αντιστοίχησης του χώρου διευθύνσεων της φυσικής μνήμης (συσκευή RAM) αλλά και συσκευών (πχ PCI devices, GPU VRam, κλπ) σε έναν άλλο, «εικονικό» χώρο διευθύνσεων.
Αυτή η τεχνική μας δίνει διάφορα πλεονεκτήματα. Μερικά από αυτά:

  • Απομόνωση μνήμης ανάμεσα σε διεργασίες μεταξύ τους και με τον πυρήνα
  • Η μνήμη συσκευών μπορεί να ενσωματωθεί κατευθείαν στο χώρο διευθύνσεων μιας διεργασίας
  • Δίνει τη δυνατότητα διαμοιρασμού μνήμης (shared memory)
  • Μπορούμε να ορίσουμε δικαιώματα ανά περιοχή μνήμης
  • Η μνήμη μπορεί να μετακινηθεί ή να γίνει «swap out» στο δίσκο (να γραφτεί στην swap)

Είδη χώρων διευθύνσεων

  • Φυσικές Διευθύνσεις
  • Εικονικές Διευθύνσεις

Οι φυσικές διευθύνσεις είναι ουσιαστικά αυτές που χρησιμοποιούνται απευθείας από το Hardware (DMA, Περιφερειακά κλπ), ενώ οι εικονικές είναι οι διευθύνσεις που χρησιμοποιεί το software.

Συγκεκριμένα για την αρχιτεκτονική που αναλύουμε (δηλαδή CISC – x86/AMD64), οι εικονικές διευθύνσεις είναι αυτές που χρησιμοποιούνται από τις εντολές assembly «Load» και «Store».

Άρα λοιπόν, έχουμε κατά νου ότι οποιαδήποτε διεργασία τρέχει σε Userspace «βλέπει» εικονικές διευθύνσεις και όχι πραγματικές. Ομοίως και ένα μεγάλο μέρος του πυρήνα.

H αντιστοίχηση φυσικών σε εικονικές διευθύνσεις γίνεται σε επίπεδο hardware. Αυτό έχει σαν συνέπεια τα παρακάτω

  • Δεν επηρεάζεται η επίδοση του συστήματος (performance overhead) όταν γίνεται προσπέλαση ήδη χαρτογραφημένης μνήμης
  • Χρησιμοποιούνται οι ίδιες εντολές (assembly) τόσο για την προσπέλαση μνήμης όσο και συσκευών
  • Οι διεργασίες τόσο του userspace όσο και του πυρήνα χρησιμοποιούν εικονικές διευθύνσεις

Page Table

O πίνακας σελίδων – θα αναφερόμαστε σε αυτόν ως page table – είναι μία δομή δεδομένων που χρησιμοποιεί το λειτουργικό σύστημα για να αποθηκεύσει τις αντιστοιχήσεις μεταξύ εικονικών και φυσικών διευθύνσεων.

O πυρήνας αποθηκεύει τις πληροφορίες των αντιστοιχίσεων αυτών στις δομές

  • struct_mm
  • vm_area_struct
  • task_struct
domes-mnimis
Οι παραπάνω δομές και η σχέση μεταξύ τους

Σελίδα Μνήμης (page)

Ως σελίδα μνήμης (page) εννοούμε, γενικά, ένα συνεχές μπλοκ μνήμης σταθερού μήκους διευθυνσιοδοτούμενο πάνω σε ένα χώρο διευθύνσεων

Εδώ πρέπει να γίνει διάκριση μεταξύ «virtual page» και «page frame». Virtual page είναι ένα συνεχές σταθερού μήκους μπλοκ πάνω στο χώρο διευθύνσεων της εικονικής μνήμη, το οποίο περιέχεται στον πίνακα σελίδων (page table) του συστήματος. Page frame είναι πάλι ένα συνεχές σταθερού μήκους μπλοκ, αλλά πάνω στον φυσικό χώρο διευθύνσεων, δηλαδή πάνω στη φυσική μνήμη του συστήματος.

Στα σύγχρονα λειτουργικά συστήματα, τα οποία λειτουργούν με εικονική μνήμη, τα ανωτέρω μπλοκ είναι οι μικρότερες δυνατές μονάδες δεδομένων που χρησιμοποιούν για να διαχειριστούν τη μνήμη του συστήματος.

level-page-table
Μετάφραση από Εικονικές σε Φυσικές Διευθύνσεις

Αρχιτεκτονική του hardware που διαχειρίζεται τη μνήμη

Το υλικό που είναι υπεύθυνο για την μνήμη συνοψίζεται χοντρικά σε τρία διακριτά μέρη:

  • MMU – Memory Management Unit
    Το εξάρτημα που είναι υπεύθυνο για την υλοποίηση της εικονικής μνήμης.
  • Συνήθως είναι ενσωματωμένο στον επεξεργαστή και κάθεται ανάμεσα στον πυρήνα του επεξεργαστή και την μνήμη
    Χειρίζεται την προσπέλαση μνήμης των εντολών load/store με διαφανή τρόπο
    Αντιστοιχίζει τις προσπελάσεις που χρησιμοποιούν εικονικές διευθύνσεις σε διευθύνσεις στη φυσική μνήμη RAM
    Ομοίως για τις συσκευές που έχουν αντιστοιχηθεί σε μνήμη (memory-mapped devices)
    Χειρίζεται τα δικαιώματα των χώρων μνήμης
    Δημιουργεί το Page Fault Exception στην περίπτωση λανθασμένης προσπέλασης (λόγω permissions ή αχαρτογράφητης διεύθυνσης)
  • Memory Controller
    To εξάρτημα που είναι υπεύθυνο για την άμεση επικοινωνία με τη μνήμη και κατ᾽ επέκταση την περαιτέρω μετάφραση των διευθύνσεων (φυσικών) σε διευθύνσεις που έχουν νόημα για τις DRAM (bank, page, row, column bits) – ανάλογα φυσικά με το είδος της μνήμης.
    Πρέπει να είναι φυσικά compatible με τα DRAM protocols και timings αναλόγως (πχ DDR3/4/5 κλπ )
  • TLB – Translation Lookaside Buffer
    Το TLB είναι στην ουσία μία λίστα από αντιστοιχίσεις μεταξύ εικονικών και φυσικών διευθύνσεων
    Έχει περιορισμένο αριθμό στοιχείων, ανάλογα φυσικά με το μοντέλο επεξεργαστή

H MMU κοιτάει στο TLB κάθε φορά που γίνεται προσπέλαση κάποιας εικονικής μνήμης.

  • Αν η εικονική διεύθυνση περιέχεται εντός του TLB, τότε η MMU μπορεί να προχωρήσει στην προσπέλαση του φυσικού πόρου (μνήμη, συσκευή κλπ)
  • Αν η εικονική διεύθυνση δεν περιέχεται εντός του TLB τότε δημιουργείται ένα «σφάλμα σελίδας» (θα το λέμε Page Fault) και θα διαδοθεί ένα interrupt στη CPU, η οποία θα παραδώσει τον έλεγχο στην αντίστοιχη interrupt routine που θα χειριστεί το εν λόγω σφάλμα

Page Faults

To page fault είναι ένα «exception» σε επίπεδο επεξεργαστή, το οποίο δημιουργείται όταν έχουμε προσπέλαση «μη ορθής» εικονικής διεύθυνσης μνήμης. Έχοντας μιλήσει για τις διαφορές μεταξύ εικονικής μνήμης και φυσικής, καταλαβαίνουμε ότι στην περίπτωση της εικονικής, μία διεύθυνση είναι «invalid» όχι μόνο όταν δεν υπάρχει στο χώρο διευθύνσεων, αλλά και στις παρακάτω περιπτώσεις:

  • Η εικονική διεύθυνση δεν έχει αντιστοιχηθεί ακόμα στο χώρο διευθύνσεων της διεργασίας που προσπαθεί να την προσπελάσει
  • Η διεργασία δεν έχει δικαίωμα προσπέλασης της διεύθυνσης που ζητάει
  • Η διεύθυνση είναι έγκυρη αλλά έχει γίνει swap out.Το συγκεκριμένο fault είναι software fault που θα διαχειριστεί ο kernel, σε αντίθεση με τα παραπάνω. Αυτό είναι μία σημαντική λεπτομέρεια που αφορά το σύστημα swap και συνήθως μας διαφεύγει όταν συγκρίνουμε το swapped memory με τη RAM

Kernel Virtual Memory

Στο Linux ο πυρήνας χρησιμοποιεί εικονικές διευθύνσεις, ακριβώς όπως και οι διεργασίες στο userspace.

O εικονικός χώρος διευθύνσεων «σπάει» σε δύο μέρη.

  • Το ανώτερο μέρος του είναι δεσμευμένο για τον πυρήνα
  • To κατώτερο μέρος του δεσμεύεται για χρήση από το userspace

Στην περίπτωση 32-bit αρχιτεκτονικής ο διαχωρισμός γίνεται στη διεύθυνση
0xC0000000 Στην περίπτωση 64-bit αρχιτεκτονικής (X86/AMD64) ο διαχωρισμός γίνεται στη διεύθυνση
0xffff880000000000

Παρατηρούμε ότι η διεύθυνση αυτή είναι ΠΟΛΥ «ψηλά» στο χώρο διευθύνσεων, μια ευχέρεια που μας δίνεται από τον τεράστιο χώρο διευθύνσεων σε 64bit συστήματα (2^64 συνολικές διευθύνσεις)

Είδη Εικονικών Διευθύνσεων

  • Λογικές Διευθύνσεις Πυρήνα
    • Οι λογικές διευθύνσεις αφορούν τη μνήμη που δεσμεύεται εντός του πυρήνα με διάφορους τρόπους. Μερικοί από αυτούς είναι: η συνάρτηση kmalloc(), η οποία και μας επιστρέφει λογικές διευθύνσεις, memory allocators (SLAB κλπ), με caches και ως «Stack» κάποιας διεργασίας του πυρήνα – όμοια με τις διεργασίες του userspace
    • Οι λογικές διευθύνσεις είναι πάντα σε ένα σταθερό και καθορισμένο offset και με ένα-προς-ένα αντιστοίχηση ως προς τις φυσικές διευθύνσεις. Για παράδειγμα, στην περίπτωση της προαναφερθείσας 32-bit αρχιτεκτονικής, η βάση των λογικών διευθύνσεων
      0xC0000000
      αντιστοιχεί στην φυσική διεύθυνση
      0x00000000
    • Έτσι είναι πολύ εύκολη η μετατροπή από λογική σε φυσική διεύθυνση και το αντίστροφο
    • Για ακριβώς αυτή τη μετατροπή ορίζονται τα ακόλουθα C Macros στον πυρήνα:
      arch/x86/include/asm/page.h:#define __pa(x) __phys_addr((unsigned long)(x)) arch/x86/include/asm/page.h:#define __va(x) ((void *)((unsigned long)(x)+PAGE_OFFSET))
      Όπου το __pa(x) μας δίνει τη φυσική διεύθυνση της λογικής διεύθυνσης x, και το __va(x) το ανάποδο.
      – Εδώ παρατηρούμε ότι το implementation είναι architecture-specific
    • Οι λογικές διευθύνσεις δεν γίνονται ΠΟΤΕ swap.
    • Συνεχείς λογικές διευθύνσεις σημαίνει αυτόματα και συνεχείς φυσικές
    • Ο συνδυασμός των δύο παραπάνω κάνει τη μνήμη που αντιστοιχεί σε λογικές διευθύνσεις κατάλληλη για χρήση από συστήματα που χρειάζονται Άμεση Προσπέλαση στη φυσική Μνήμη (DMA)
  • Εικονικές Διευθύνσεις Πυρήνα
    • Οι εικονικές διευθύνσεις «ζουν», όπως και οι λογικές μέσα στο χώρο διευθύνσεων του πυρήνα και συγκεκριμένα, στην περιοχή «επάνω» από τις λογικές διευθύνσεις.
    • Η περιοχή των εικονικών διευθύνσεων του πυρήνα λέγεται συνήθως περιοχή vmalloc()
    • Οι εικονικές διευθύνσεις χρησιμοποιούνται για
      – Δέσμευση ΜΗ συνεχών περιοχών μνήμης, πχ για μεγάλους buffers, όπου συχνά δεν είναι εφικτή η δέσμευση συνεχών διευθύνσεων
      – Input/Output που έχει αντιστοιχηθεί στη μνήμη, όπως πχ περιφερειακές συσκευές PCI
    • Η δέσμευση γίνεται με κλήση στις συναρτήσεις
      vmalloc() στην περίπτωση των buffers
      – request_mem_region()
      και
      – kmap()
      στην περίπτωση που δεσμεύουμε σελίδες στο χώρο μνήμης για χρήση I/O
    • Ο χώρος διευθύνσεων των εικονικών διευθύνσεων μνήμης αντιστοιχίζονται σε ΜΗ συνεχής περιοχές στη φυσική μνήμη και λόγω αυτού:
      – Είναι ευκολότερο να δεσμευτεί, ειδικά για χρήση μεγάλων buffer
      – Είναι ακατάλληλος για χρήση από συστήματα που απαιτούν άμεση προσπέλαση (DMA)
  • Εικονικές Διευθύνσεις Χρήστη
    • Οι εικονικές διευθύνσεις χρήστη αντιπροσωπεύουν μνήμη που προορίζεται για χρήση από διεργασίες του userspace και αποτελούν το μεγαλύτερο μέρος της συνολικής μνήμης του συστήματος
    • Στο συνολικό χώρο διευθύνσεων του συστήματος, οι εικονικές διευθύνσεις χρήστη είναι «κάτω» από αυτές του πυρήνα, στο σημείο που ορίζεται ως PAGE_OFFSET
    • Κάθε διεργασία έχει τη δική της περιοχή στο χώρο διευθύνσεων χρήστη. Τα Thread μιας διεργασίας μοιράζονται μία περιοχή.
    • Διεργασίες που ξεκίνησαν με την κλήση συστήματος clone() μοιράζονται ακριβώς την ίδια περιοχή
    • Αντίθετα από τις λογικές διευθύνσεις πυρήνα, οι οποίες χρησιμοποιούν μια σταθερή αντιστοιχία μεταξύ εικονικών και πραγματικών διευθύνσεων, οι εικονικές διευθύνσεις χρήστη εκμεταλλεύονται πλήρως την MMU και κατά συνέπεια:
      – Αντιστοιχίζονται μόνο οι περιοχές της RAM που είναι σε χρήση
      – Η μνήμη προφανώς δεν είναι συνεχής
      – H μνήμη μπορεί ανά πάσα στιγμή να γίνει swap-out
      – H μνήμη μπορεί να μετακινηθεί
    • Η μνήμη που αντιστοιχεί σε εικονικές διευθύνσεις χρήστη είναι ακατάλληλη για DMA
    • Κάθε διεργασία έχει τις δικές τις αντιστοιχήσεις μνήμης, όπως είπαμε παραπάνω, και αυτό φαίνεται, αν δούμε τον πηγαίο κώδικα του πυρήνα, στη δομή που αντικατοπτρίζει τις διεργασίες και τα νήματα, την task_struct, όπου και θα βρούμε δείκτες (pointers) σε μία άλλη δομή, την struct mm, η οποία περιέχει τις εν λόγω αντιστοιχήσεις μνήμης.
    • Κάθε φορά που γίνεται context switching, δηλαδή αλλαγή από τρέξιμο μίας διεργασίας σε έναν επεξεργαστή σε τρέξιμο μίας άλλης διεργασίας στον ίδιο επεξεργαστή, οι ανωτέρω αντιστοιχίσεις μνήμης αλλάζουν και αυτές

Memory Management Unit – MMU

Η μονάδα διαχείρισης μνήμης, MMU, είναι όπως είδαμε παραπάνω το component που είναι υπεύθυνο για το χειρισμό των εικονικών διευθύνσεων και των αντιστοιχιών τους σε φυσικές

Η MMU λειτουργεί με βασική μονάδα της τη Σελίδα Μνήμης. Τονίζεται ότι το μέγεθος του page διαφέρει ανά αρχιτεκτονική και σε μερικές μάλιστα είναι configurable στο kernel compile time. Σε x86 είναι συνήθως 4Kbytes

Κάθε φορά που μια διεργασία κάνει προσπέλαση μνήμης που δεν έχει αντιστοιχηθεί ακόμα, όπως είδαμε παραπάνω, δημιουργείται ένα page fault. Τα Page faults και ο χειρισμός τους είναι μέρος της κανονικής λειτουργίας του συστήματος διαχείρισης μνήμης του πυρήνα. Επειδή το page table είναι συνήθως μικρότερο από το συνολικό αριθμό αντιστοιχίσεων μίας διεργασίας, κάθε φορά που έχουμε context switching, έχουμε και page fault
Ομοίως, δημιουργείται page fault όταν γίνεται ετεροχρονισμένη δέσμευση μνήμης (lazy allocation)

Τονίζεται ότι ενώ οι εικονικές διευθύνσεις που έχουν δοθεί σε μια διεργασία μπορεί να είναι συνεχείς, οι φυσικές ΔΕΝ είναι. Αυτό έχει σαν αποτέλεσμα μερικές φορές κόστος σε επεξεργαστικό χρόνο, καθώς δεν «χωράνε» όλες οι αντιστοιχήσεις στον TLB

Είναι συχνό να έχουν δοθεί από το σύστημα ίδιες εικονικές διευθύνσεις σε δύο διαφορετικές διεργασίες, οι οποίες όμως σίγουρα αντιστοιχούν σε διαφορετικές φυσικές διευθύνσεις.

Shared Memory

Ένα από τα σημαντικότερα μέρη του συστήματος διαχείρισης μνήμης είναι η διαμοιραζόμενη μνήμη – Shared Memory -, κάτι που όσοι από εμάς έχουν ασχοληθεί με βάσεις δεδομένων και βελτιστοποίηση αυτών σίγουρα έχουν συναντήσει.

Με απλά λόγια, shared memory είναι ένα ή περισσότερα page frames, τα οποία έχουν αντιστοιχηθεί σε δύο ή περισσότερες διεργασίες. Κοινώς, πολλές διεργασίες μοιράζονται την ίδια περιοχή στη φυσική μνήμη.

Προφανώς οι εικονικές διευθύνσεις που αντιστοιχούν στις ανωτέρω διεργασίες δεν είναι ανάγκη να είναι ίδιες, αρκεί να δείχνουν στο ίδιο page frame.

Παρόλα αυτά, κάποιες φορές χρειαζόμαστε να είναι ίδιες ΚΑΙ οι εικονικές διευθύνσεις. Για παράδειγμα σε δομές δεδομένων όπως συνδεδεμένες λίστες, δέντρα κλπ, όπου έχουμε pointers από στοιχείο σε στοιχείο, τις οποίες δομές θέλουμε να έχουμε εντός shared memory.

Για τις ανωτέρω περιπτώσεις έχουμε την κλήση συστήματος mmap()
– Η mmap() ουσιαστικά ζητά από τον πυρήνα μία συγκεκριμένη εικονική διεύθυνση, η οποία θα δείχνει σε περιοχή shared memory
– Αν ο πυρήνας δεν μπορεί να δεσμεύσει αυτή την διεύθυνση, η mmap() γυρνάει failure

Memory Allocation

Κάθε φορά που ζητείται μνήμη από μία διεργασία, ο πυρήνας δημιουργεί μία εγγραφή στα page tables

Όταν η μνήμη αυτή προσπελαστεί από τη διεργασία, ο επεξεργαστής θα δημιουργήσει ένα page fault – προφανώς λόγω του παραπάνω

Μετά, ως αποτέλεσμα του παραπάνω page fault, καλείται ο page fault handler του πυρήνα, ο οποίος και ψάχνει στα page tables για τη διεύθυνση που ζητήθηκε. Ο πυρήνας ύστερα διαλέγει μία εγγραφή από το TLB και την αφαιρεί

Στη θέση της ανωτέρω εγγραφής βάζει μία καινούρια με την αντιστοίχηση που ζητήθηκε. Ο έλεγχος γυρνάει πίσω στη διεργασία που ζήτησε τη μνήμη

Steps_In_a_Translation_Lookaside_Buffer

Lazy Memory Allocation

Η διαδικασία του lazy allocation μοιάζει πολύ με την παραπάνω κανονική διαδικασία δέσμευσης

Η βασική διαφορά είναι ότι ο kernel δεν δεσμεύει άμεσα τις σελίδες που ζητήθηκαν από μία διεργασία, παρά περιμένει μέχρι οι σελίδες αυτές να χρησιμοποιηθούν πραγματικά.

Το lazy allocation είναι μία πάρα πολύ σημαντική βελτιστοποίηση, καθώς πολλές φορές ζητείται μνήμη που δεν θα χρησιμοποιηθεί ποτέ

Συγκεκριμένα, κάθε φορά που ζητείται μνήμη από μία διεργασία, ο πυρήνας δημιουργεί μία εγγραφή στα page tables του και γυρνάει απευθείας τον έλεγχο στη διεργασία, χωρίς να πειράξει το TLB

Όταν κάποια στιγμή η μνήμη αυτή χρειαστεί, ο επεξεργαστής θα δημιουργήσει ένα page fault – προφανώς λόγω του παραπάνω

Μετά, ως αποτέλεσμα του παραπάνω page fault, καλείται ο page fault handler του πυρήνα, ο οποίος και ψάχνει στα page tables για τη διεύθυνση που ζητήθηκε.

Αν η ανωτέρω διεύθυνση πράγματι υπάρχει στα page tables, ο πυρήνας δεσμεύει ένα page frame, ενημερώνει το TLB και γυρνάει τον έλεγχο στη διεργασία

Swapping

Όταν η χϱήση της φυσικής μνήμης είναι υψηλή, ο kernel αποφασίζει να μεταφέρει κάποια page frames στο δίσκο για να ελευθερώσει χώρο στη RAM

To swapping είναι εφικτό λόγω της ύπαρξης της MMU. O kernel όταν κάνει swap, ουσιαστικά αντιγράφει το RAM page frame στο δίσκο και αφαιρεί το entry του από το TLB. Το frame αυτό ύστερα, μπορεί να επαναχρησιμοποιηθεί από άλλη διεργασία.

Όταν το frame που έγινε swap ζητηθεί πάλι από την διεργασία του, θα δημιουργηθεί ένα page fault, όπως είδαμε παραπάνω. Ύστερα, σαν μέρος του page fault process, ο πυρήνας βάζει τη διεργασία σε sleep, αντιγράφει το εν λόγω frame πίσω στη RAM, ενημερώνει το page table και τέλος, δίνει ξανά τον έλεγχο στη διεργασία

Τονίζεται πως όταν γίνεται αντιγραφή πίσω στη RAM, το frame δεν πάει πίσω στην παλιά του διεύθυνση. Η MMU παρόλο το ανωτέρω, θα χρησιμοποιήσει την ίδια εικονική διεύθυνση, με αποτέλεσμα η διαδικασία να μην καταλάβει τη διαφορά.

Τα παραπάνω είναι και ο σημαντικότερος λόγος που ο χώρος διευθύνσεων χρήστη δεν είναι κατάλληλος για DMA.

swap-in-out

Swap και Swappiness

Μία πάρα πολύ σημαντική παράμετρος του πυρήνα είναι το swappiness. H συγκεκριμένη παράμετρος, αλλάζει την ισορροπία ανάμεσα στο να γίνει swap out μνήμη και στο να αφαιρεθούν σελίδες απευθείας από την page cache του συστήματος. Οι τιμές που μπορεί να πάρει το swappiness είναι από 0 έως και 100. Όσο χαμηλότερη φυσικά η τιμή, τόσο περισσότερο θα προσπαθήσει ο πυρήνας να μην κάνει swap και το αντίστροφο.

Σε απλουστευμένη μορφή, ο αλγόριθμος με τον οποίο ο πυρήνας αποφασίζει αν θα κάνει swap είναι ο κάτωθι:
– Υπολογίζεται το distress value. Αυτό είναι ένας αριθμός που αντιπροσωπεύει τη δυσκολία που συναντά ο πυρήνας στην προσπάθειά του να ελευθερώσει μνήμη. Την πρώτη φορά που προσπαθεί το distress είναι 0. Αν χρειαστούν περαιτέρω προσπάθειες ο αριθμός αυτός ανεβαίνει, ασυμπτωτικά μέχρι το 100.
– Υπολογίζεται το mapped_ratio, το οποίο δεν είναι παρά μία προσέγγιση του ποσοστού της συνολικής μνήμης που έχει δεσμευθεί στο χώρο διευθύνσεων των διεργασιών χρήστη.
– Διαβάζεται το vm_swappiness, η παράμετρος δηλαδή που αναφέρεται παραπάνω, από το procfs.
– Υπολογίζεται το swap_tendency, ως εξής:
swap_tendency = mapped_ratio/2 + distress + vm_swappiness
Αν το swap_tendency είναι κάτω από 100, ο πυρήνας θα ελευθερώσει μόνο cache pages, ειδάλλως κάποια pages διεργασιών του userspace θα γίνουν swap out στο δίσκο.

Η διαδικασία ρύθμισης του vm_swappiness είναι αρκετά περίπλοκη και θέλει συνήθως trial and error, με βάση φυσικά το εκάστοτε workload

Επίλογος

Ελπίζω μετά τα παραπάνω να έχουμε καταλήξει σε λίγο καλύτερη κατανόηση του συστήματος διαχείρισης μνήμης στα linux. Σίγουρα το θέμα είναι τεράστιο και δεν έχουμε καν μιλήσει για Huge Pages, Transparent Huge Pages και άλλα. Σε επόμενο άρθρο θα εμβαθύνουμε περισσότερο.

Παραπομπές:

Linux Memory Mangment


Advertisements

7 σκέψεις σχετικά με το “Απομυθοποιώντας τη διαχείριση μνήμης στο Linux

Add yours

  1. Μπράβο για το άρθρο, πολύ αναλυτικό και καλογραμμένο. Ανυπομονώ για το επόμενο.

Γράψτε απάντηση στο Jason Karaoglanis Ακύρωση απάντησης

Εισάγετε τα παρακάτω στοιχεία ή επιλέξτε ένα εικονίδιο για να συνδεθείτε:

Λογότυπο WordPress.com

Σχολιάζετε χρησιμοποιώντας τον λογαριασμό WordPress.com. Αποσύνδεση /  Αλλαγή )

Φωτογραφία Google

Σχολιάζετε χρησιμοποιώντας τον λογαριασμό Google. Αποσύνδεση /  Αλλαγή )

Φωτογραφία Twitter

Σχολιάζετε χρησιμοποιώντας τον λογαριασμό Twitter. Αποσύνδεση /  Αλλαγή )

Φωτογραφία Facebook

Σχολιάζετε χρησιμοποιώντας τον λογαριασμό Facebook. Αποσύνδεση /  Αλλαγή )

Σύνδεση με %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Create a website or blog at WordPress.com

ΠΑΝΩ ↑