Linux Swappiness: Myte vs. Virkelighet – Avslør Sannheten!

Linux sin bytteverdi har ingenting å gjøre med hvor mye RAM som er i bruk før bytteprosessen aktiveres. Dette er en vanlig misforståelse. Vi skal forklare hva den egentlig gjør.

Oppklaring av myter rundt swap-funksjonen

Bytte er en teknikk der data fra Random Access Memory (RAM) skrives til et spesifikt område på harddisken – enten en dedikert partisjon eller en fil. Dette frigjør plass i RAM.

Linux har en innstilling kalt «swappiness»-verdien, som ofte misforstås. Den mest vanlige feiloppfatningen er at den setter en grense for RAM-bruk, slik at bytting startes når denne grensen nås.

Denne misforståelsen har blitt gjentatt så ofte at den nå oppfattes som sannheten. Hvorfor skulle du tro oss når vi sier noe annet, når nesten alle andre forteller deg at det er slik bytte fungerer?

Fordi vi skal bevise det.

RAM-minnet er delt inn i soner

Linux ser ikke på RAM-en som en enkelt stor lagringsplass, men som delt inn i ulike regioner kalt soner. Hvilke soner som finnes avhenger av om datamaskinen er 32-bit eller 64-bit. Her er en forenklet oversikt over mulige soner på en x86-arkitektur datamaskin:

Direkte minnetilgang (DMA): De laveste 16 MB av minnet. Navnet kommer fra eldre maskiner der direkte minnetilgang kun var mulig i dette området.
Direct Memory Access 32 (DMA32): En sone som kun finnes i 64-bit Linux. Det er de laveste 4 GB med minne. Linux på 32-bit maskiner kan kun bruke DMA opp til denne mengden RAM (med mindre de bruker Physical Address Extension (PAE)), og på 32-bits maskiner kalles dette ofte HighMem.
Normal: På 64-bit maskiner er dette all RAM over 4 GB (omtrent), mens på 32-bit maskiner er det RAM mellom 16 MB og 896 MB.
HighMem: Kun til stede på 32-bits Linux-maskiner. Dette er all RAM over 896 MB, inkludert RAM over 4 GB på tilstrekkelig store maskiner.

PAGESIZE-verdien

RAM er tildelt i sider med fast størrelse. Størrelsen bestemmes av kjernen ved oppstart, basert på datamaskinens arkitektur. En vanlig sidestørrelse på en Linux-maskin er 4 Kbyte.

Du kan sjekke din sidestørrelse ved hjelp av getconf-kommandoen:

getconf PAGESIZE

Soner er knyttet til noder

Soner er knyttet til noder, som igjen er knyttet til en CPU. Kjernen forsøker å allokere minne for en prosess som kjører på en CPU fra noden som er koblet til den CPU-en.

Konseptet med at noder er knyttet til CPUer muliggjør bruk av ulike typer minne i spesialiserte datamaskiner med flere CPUer, ved hjelp av Non-Uniform Memory Access-arkitektur.

Dette er et avansert konsept. En gjennomsnittlig Linux-datamaskin vil ha en enkelt node, kalt node null, og alle soner tilhører denne noden. Du kan sjekke noder og soner i filen /proc/buddyinfo. Her brukes «less» kommandoen for å vise innholdet:

less /proc/buddyinfo

Dette er utdata fra en 64-bits datamaskin:

Node 0, zone DMA   1  1  1  0  2  1  1  0  1  1  3
Node 0, zone DMA32 2 67 58 19  8  3  3  1  1  1 17

Det er en enkelt node (node null). Maskinen har kun 2 GB RAM, så det er ingen «normal» sone. Kun DMA og DMA32.

Hver kolonne viser antall tilgjengelige sider av en viss størrelse. For DMA32-sonen leses verdiene fra venstre:

2: 2 av 2(0*PAGESIZE) minnebiter.
67: 67 av 2(1*PAGE_SIZE) minnebiter.
58: 58 av 2(2*PAGESIZE) minnebiter.
Osv. opp til…
17: 17 av 2(512*PAGESIZE) biter.

Det viktigste her er å se relasjonen mellom noder og soner.

Filsider og anonyme sider

Minnekartlegging bruker sidetabellposter for å holde oversikt over hvilke minnesider som brukes, og til hva.

Minnetildelinger kan være:

Filstøttet: Inneholder data lest fra en fil, uansett filtype. Disse dataene kan leses fra filen igjen hvis de frigjøres. Endringer i filstøttede data i minnet må skrives til fil før minnet frigjøres, ellers går endringene tapt.
Anonym: Minne uten fil eller enhet som støtte. Disse sidene kan inneholde data for programmer, stack og heap. Siden det ikke er noen fil som støtter disse dataene, må det settes av en spesiell plass for lagring av anonyme data: swap-partisjonen eller swap-filen. Anonyme data skrives til swap før de frigjøres.
Enhetsstøttet: Enheter som blokkenhetsfiler som behandles som filer. Data leses fra og skrives til dem. Enhetsstøttet minnekartlegging lagrer data fra en enhet.
Delt: Flere sidetabellposter kan peke til samme RAM-side. Tilgang gjennom en av tildelingene viser samme data. Dette er en effektiv måte for prosesser å kommunisere. Delte, skrivbare kartlegginger er vanlig for høyhastighets kommunikasjon.
Kopier-ved-skriving (Copy-on-Write): En tildelingsteknikk. Hvis en kopi av en ressurs i minnet etterspørres, returneres en tildeling til den opprinnelige ressursen. Hvis en prosess prøver å skrive til den «delte» ressursen, replikeres den for å tillate endringene i den nye kopien. Minnetildeling skjer dermed først ved første skrivekommando.

For bytte er det kun fil- og anonyme sider som er relevante.

Byttefunksjonen

Her er beskrivelsen av bytte fra Linux-dokumentasjonenGitHub:

«Denne kontrollen brukes til å definere hvor aggressiv kjernen vil bytte minnesider. Høyere verdier gir mer aggressivitet, lavere verdier reduserer byttingen. En verdi på 0 instruerer kjernen om å ikke starte bytting før mengden ledige og filstøttede sider er mindre enn høyvannsmerket i en sone.

Standardverdien er 60.»

Det høres ut som om bytteintensiteten varierer. Å sette bytte til null slår ikke av bytte, men instruerer kjernen om å ikke bytte før visse betingelser er oppfylt. Bytte kan fortsatt forekomme.

La oss se nærmere. Her er definisjonen av vm_swappiness i kjernens kildekodefil vmscan.c:

/*
* Fra 0 .. 100. Høyere betyr mer bytting.
*/
int vm_swappiness = 60;

Swappiness kan variere fra 0 til 100. Kommentaren tyder på at verdien påvirker hvor mye bytting som skjer, med høyere tall som resulterer i mer bytting.

Videre i kildekoden ser vi at en ny variabel, kalt swappiness, tildeles en verdi returnert av funksjonen mem_cgroup_swappiness(). Dette viser seg å være verdien av vm_swappiness.

int swappiness = mem_cgroup_swappiness(memcg);

Og litt lenger ned i samme fil ser vi dette:

/*
* Med bytte på 100 har anonym og fil samme prioritet.
* Denne skanneprioriteten er i hovedsak det omvendte av IO-kostnaden.
*/
anon_prio = swappiness;
file_prio = 200 – anon_prio;

Dette er interessant. To verdier er avledet fra swappiness: anon_prio og file_prio. Når den ene øker, synker den andre, og omvendt.

Linux sin bytteverdi definerer forholdet mellom disse to verdiene.

Det gylne snitt

Filsider inneholder data som lett kan hentes hvis minnet frigjøres. Linux kan lese filen på nytt. Hvis dataene er endret i RAM, må endringene skrives til filen før siden frigjøres. Derfor lagres ikke filsider i swap, men i den opprinnelige filen.

Anonyme sider har ingen underliggende fil. Verdiene i disse sidene er dynamisk generert, og kan ikke leses fra fil. Den eneste måten å gjenopprette dem på er å lagre dataene før minnet frigjøres. Dette er swap’s funksjon: å lagre anonyme sider.

Både for fil- og anonyme sider kan frigjøring av minne kreve harddiskoperasjoner. Hvis data er endret siden sist de ble skrevet, kreves en filsystemskriving. For å hente dataene kreves en filsystemlesing. Begge typer sidegjenvinning er kostbare. Å redusere bytting av anonyme sider kan øke harddiskinndata og -utdata for filsystemet.

Som vi så i kodebiten, er det to variabler: file_prio (filprioritet) og anon_prio (anonym prioritet).

anon_prio er satt til Linux sin bytteverdi.
file_prio er 200 minus anon_prio.

Disse variablene henger sammen. Hvis begge er satt til 100, er de like. For andre verdier, vil anon_prio reduseres fra 100 mot 0, og file_prio vil øke fra 100 mot 200. Disse verdiene inngår i en algoritme som bestemmer om kjernen foretrekker å gjenvinne (frigjøre) fil- eller anonyme sider.

file_prio kan ses på som systemets vilje til å frigjøre filsider, mens anon_prio er viljen til å frigjøre anonyme sider. Disse verdiene setter ikke en trigger for når swap skal brukes. Dette bestemmes et annet sted.

Når minne må frigjøres, brukes disse to variablene – og forholdet mellom dem – av gjenvinnings- og byttealgoritmer for å bestemme hvilke sider som fortrinnsvis vurderes for frigjøring, og dermed om harddiskaktiviteten behandler filer eller bytteplassen.

Når slår swap inn?

Vi har fastslått at bytteverdien angir en preferanse for type minnesider som skal skannes for potensiell gjenvinning. Men hva bestemmer når swap slår inn?

Hver minnesone har et høyvannsmerke og et lavvannsmerke, som er systemgenererte prosenter av RAM i hver sone. Disse verdiene er triggere for bytte.

Du kan se dine høye og lave vannmerker i /proc/zoneinfo-filen med kommandoen:

less /proc/zoneinfo

Hver sone har et sett med minneverdier målt i sider. Her er verdiene for DMA32-sonen: Lavvannsmerket er 13966 sider, og høyvannsmerket er 16759 sider:

Under normal drift, når ledig minne faller under en sones lavvannsmerke, begynner swap-algoritmen å skanne etter minne som kan gjenvinnes, og tar hensyn til anon_prio og file_prio.
Hvis bytteverdien er null, skjer bytting når verdien av filsider pluss ledige sider er mindre enn høyvannsmerket.

Linux sin bytteverdi kan altså ikke brukes til å påvirke bytte basert på RAM-bruk.

Hva bør swappiness settes til?

Dette avhenger av maskinvare, arbeidsbelastning, harddisktype og om maskinen er en stasjonær eller server. Det er ikke en «one size fits all»-innstilling.

Husk at swap ikke kun er en mekanisme for å frigjøre RAM. Swap er en viktig del av et velfungerende system, og uten den blir minneadministrasjon vanskeligere for Linux.

Endring av bytteverdien har umiddelbar effekt; du trenger ikke å starte på nytt. Du kan gjøre små justeringer og overvåke effektene. Ideelt sett bør dette gjøres over flere dager med forskjellige aktiviteter.

Her er noen punkter å vurdere:

Hvis du prøver å «deaktivere swap» ved å sette bytteverdien til null, bytter du bare swap-relatert aktivitet til filrelatert aktivitet.
Hvis du har eldre, mekaniske harddisker, kan du prøve å redusere bytteverdien for å redusere churn i byttepartisjonen. Dette kan øke churn i filsystemet, men det kan fungere bedre for din maskin. Den eneste måten å vite det sikkert er å prøve.
For enkeltbruksservere, som databaseservere, kan du få veiledning fra leverandørene av databasen. Ofte har disse applikasjonene sine egne minnehåndteringsrutiner. Leverandørene kan foreslå en bytteverdi basert på spesifikasjoner og arbeidsbelastning.
For den gjennomsnittlige stasjonære brukeren med rimelig fersk maskinvare: La det være som det er.

Hvordan endre Linux Swappiness-verdien

Før du endrer bytteverdien, må du finne ut hva den er. Du kan finne det med denne kommandoen:

cat /proc/sys/vm/swappiness

For å konfigurere bytteverdien, bruk sysctl-kommandoen:

sudo sysctl vm.swappiness=45

Den nye verdien brukes umiddelbart. En omstart vil tilbakestille bytteverdien til 60. Når du har funnet en passende verdi, kan du gjøre endringen permanent ved å legge den til /etc/sysctl.conf-filen. Du kan bruke den tekstredigereren du foretrekker, for eksempel nano:

sudo nano /etc/sysctl.conf

Legg til denne linjen nederst i filen, for eksempel med en permanent bytteverdi på 35. Erstatt 35 med din ønskede verdi:

vm.swappiness=35

For å lagre endringene i nano, trykk «Ctrl+O», «Enter», og «Ctrl+Z».

Minnehåndtering er komplisert

Minnehåndtering er komplisert. Det er vanligvis best å la kjernen håndtere det for deg.

Det er lett å tro at du bruker mer RAM enn du gjør. Verktøy som «top» og «free» kan gi feil inntrykk. Linux vil bruke ledig RAM til egne formål, som diskbufring. Dette øker det «brukte» minnet og reduserer det «ledige» minnetallet. RAM brukt som diskbuffer er markert som både «brukt» og «tilgjengelig» fordi det kan frigjøres raskt ved behov.

For uinnvidde kan det se ut som at swap ikke fungerer, eller at bytteverdien må endres.

Som alltid, ligger detaljene i detaljene, eller i dette tilfellet: kjernens byttedemon.