Operativsystemer: Tre enkle deler. Del 1: Introduksjon (oversettelse)

Innledning til operativsystemer

Hei, Habr! Jeg vil gjerne presentere en serie artikler som oversettelser av en litteratur som jeg synes er interessant - OSTEP. I dette materialet blir det grundig behandlet hvordan unix-lignende operativsystemer fungerer, nemlig hvordan prosesser, forskjellige planleggere, minne og andre lignende komponenter som utgjør et moderne OS, jobber. Du kan se originalen av alt materialet her: here. Vennligst merk at oversettelsen er gjort uprofesjonelt (temmelig fritt), men jeg håper jeg har bevart den generelle meningen.

Laboratoriearbeid knyttet til emnet kan finnes her:
— original: pages.cs.wisc.edu/~remzi/OSTEP/Homework/homework.html
— original: github.com/remzi-arpacidusseau/ostep-code
— min personlige tilpasning: github.com/bykvaadm/OS/tree/master/ostep

Og du kan også sjekke kanalen min på telegram =)

Programmets arbeid

Hva skjer når et program kjører? Det kjørte programmet utfører én enkel oppgave - det kjører instruksjoner. Hver sekund hentes millioner, om ikke milliarder, av instruksjoner av prosessoren fra RAM, som deretter dekoder dem (for eksempel gjenkjenner hvilken type disse instruksjonene tilhører) og utfører dem. Dette kan være å legge sammen to tall, få tilgang til minne, sjekke en betingelse, gå til en funksjon, og så videre. Etter at en instruksjon er ferdig, går prosessoren videre til neste. Og slik, instruksjon etter instruksjon, blir de kjørt til programmet er over.
Dette eksempelet er selvfølgelig forenklet - faktisk tillater moderne maskinvare at instruksjoner kjøres uten rekkefølge, beregner mulige resultater, utfører instruksjoner samtidig, og slikt.

Von Neumann-modellen for beregning

Den forenklede formen for arbeid vi har beskrevet ligner på Von Neumann-modellen for beregning. Von Neumann er en av pionerene innen datamaskinsystemer, og også en av forfatterne til spillteorien.. Under programkjøringen skjer det ytterligere mange hendelser, mange andre prosesser og tredjeparts logikk er i drift, hvis hovedmål er å forenkle oppstart, drift og vedlikehold av systemet.
Det finnes en programvarepakke som ansvarer for hvor enkelt det er å starte programmer (eller til og med la flere programmer kjøre samtidig), og som lar programmene dele samme minne og interagere med ulike enheter. Denne programvarepakken kalles i bunn og grunn operativsystemet, og oppgavene det har inkluderer å overvåke at systemet fungerer korrekt og effektivt, samt å sikre enkel styring av systemet.

Operativsystem

Operativsystem, forkortet OS, er et sett med sammenkoblede programmer som er ment for å håndtere datamaskinens ressurser og organisere brukerens interaksjon med datamaskinen..
OS oppnår sin effektivitet først og fremst gjennom den viktigste teknikken — teknikken for virtualisering.OS interagerer med fysiske ressurser (prosessor, minne, disk og lignende) og transformerer dem til en mer generell, mer funksjonsrik og enklere form å bruke. Derfor kan man, for overordnet forståelse, ganske grovt sammenligne operativsystemet med en virtuell maskin.
For at brukerne skal kunne gi kommandoer til operativsystemet og dermed benytte seg av mulighetene til den virtuelle maskinen (som: kjøre program, tildele minne, få tilgang til filer osv.), gir operativsystemet et grensesnitt, kalt API (application programming interface), som man kan gjøre kallelser (calls) til. Et typisk operativsystem gir mulighet for å gjøre hundrevis av systemkall.
Og til slutt, ettersom virtualisering tillater mange programmer å kjøre (dermed dele CPU), og samtidig få tilgang til deres instruksjoner og data (dermed dele minne), samt få tilgang til diskene (dermed dele inndata-utdata-enheter), kalles operativsystemet også ressursforvalter. Hver prosessor, disk og minne er en ressurs i systemet, og på den måten blir en av operativsystemets roller å håndtere disse ressursene, gjøre det effektivt, rettferdig, eller motsatt, avhengig av oppgaven operativsystemet er designet for.

Virtualisering av CPU

La oss se på følgende program:
(https://www.youtube.com/watch?v=zDwT5fUcki4&feature=youtu.be)

Operativsystemer: Tre enkle deler. Del 1: Introduksjon (oversettelse)

Det utfører ingen spesielle handlinger, i hovedsak det den gjør er å kalle funksjonen spin(), hvis oppgave er å sjekke tiden i en syklus og returnere etter at ett sekund har gått. Dermed gjentar den uendelig strengen som brukeren har sendt som argument.
La oss kjøre dette programmet og sende det argumentet symbolet “A”. Resultatet blir ikke spesielt interessant — systemet utfører bare programmet som periodisk viser symbolet “A” på skjermen.
Nå skal vi prøve en variant der mange instanser av det samme programmet kjører samtidig, men viser forskjellige bokstaver, for å gjøre det klarere. I dette tilfellet vil resultatet bli noe annerledes. Til tross for at vi har én prosessor, kjører programmet samtidig. Hvordan er det mulig? Det viser seg at operativsystemet, med hjelp fra maskinvarekapasitetene, skaper en illusjon. Illusjonen av at det finnes flere virtuelle prosessorer i systemet, og forvandler én fysisk prosessor til teoretisk uendelig antall, og dermed lar programmene tilsynelatende kjøre samtidig. Denne illusjonen kalles CPU-virtualisering.
Et slikt bilde gir mange spørsmål, for eksempel, hvis flere programmer ønsker å starte samtidig, hvilket vil da bli startet? Dette spørsmålet besvares av “politikker” i operativsystemet. Politikker brukes mange steder i OS og svarer på slike spørsmål, og er grunnleggende mekanismer som OS implementerer. Herav rollen til OS som ressursforvalter.

Minnevirtualisering

La oss nå se på minnet. Den fysiske modellen av minnet i moderne systemer representeres som et array av bytes. For å lese fra minnet må man spesifisere adressen til cellen, for å få tilgang til den. For å skrive eller oppdatere data må man også spesifisere dataene og adressen til cellen der de skal skrives.
Tilgang til minnet skjer kontinuerlig i løpet av programmet. Programmet lagrer hele sin datakonstruksjon i minnet, og får tilgang til den ved å utføre ulike instruksjoner. Instruksjonene lagres også i minnet, så nå til neste instruksjon skjer derfor også med hver forespørsel.

Kall til malloc()

La oss se på følgende program som allokerer minne ved hjelp av et kall malloc() (https://youtu.be/jnlKRnoT1m0):

Operativsystemer: Tre enkle deler. Del 1: Introduksjon (oversettelse)

Programmet gjør flere ting. For det første allokerer det en viss mengde minne (linje 7), deretter skriver det ut adressen til den tildelte cellen (linje 9), og skriver null i den første plassen av det tildelte minnet. Videre går programmet inn i en løkke der det inkrementerer verdien som er skrevet inn i minnet på adressen i variabelen “p”. Det skriver også ut prosess-ID-en til seg selv. Prosess-ID-en er unik for hver kjørende prosess. Når vi derimot starter flere eksemplarer, vil vi støte på et interessant resultat: I det første tilfellet, hvis vi ikke gjør noe og bare starter flere kopier, vil adressene være forskjellige. Men dette stemmer jo ikke med teorien vår! Riktig, fordi i moderne distribusjoner er minnerandomisering aktivert som standard. Hvis vi deaktiverer den, vil vi få det forventede resultatet — minneadressene til to programmer som kjører samtidig vil være like.

Operativsystemer: Tre enkle deler. Del 1: Introduksjon (oversettelse)

Resultatet blir at to uavhengige programmer arbeider med sine egne private adresseområder, som igjen avbildes i det fysiske minnet av operativsystemet. Derfor vil bruk av minneadresser innen én enkelt program ikke påvirke andre, og hvert program opplever at det har sitt eget stykke fysiske minnet, fullstendig overgitt til dets disposisjon. Virkeligheten er imidlertid at det fysiske minnet er en delt ressurs, hvis administrasjon utføres av operativsystemet.

Konsistens

En annen viktig tema innen operativsystemer er konsistens. Dette begrepet brukes når det refereres til problemer i systemet som kan oppstå ved håndtering av mange ting samtidig innen ett program. Konsistensproblemer oppstår til og med i operativsystemet selv. I de tidligere eksemplene med minne- og prosessvirtualisering forsto vi at OS-et håndterer mange ting samtidig - det starter først én prosess, så en annen, og så videre. Det viste seg at denne oppførselen kan føre til visse problemer. Moderne flertrådede programmer opplever slik vanskeligheter.

La oss se på følgende program:

Operativsystemer: Tre enkle deler. Del 1: Introduksjon (oversettelse)

Programmet i hovedfunksjonen oppretter to tråder ved hjelp av kallet Pthread_create(). I dette eksempelet kan vi tenke på en tråd som en funksjon som kjører i et minneområde sammen med andre funksjoner, og antallet kjørende funksjoner er åpenbart mer enn én. I dette eksempelet starter hver tråd og utfører funksjonen worker(), som på sin side ganske enkelt inkrementerer en variabel.,.

Vi vil kjøre dette programmet med argumentet 1000. Som du kanskje allerede har gjettet, bør resultatet bli 2000, siden hver tråd inkrementerer variabelen 1000 ganger. Men alt er ikke så enkelt. La oss prøve å kjøre programmet med et antall repetisjoner som er en orden større.

Operativsystemer: Tre enkle deler. Del 1: Introduksjon (oversettelse)

Ved å gi tallet, for eksempel 100000 som inndata, forventer vi å se tallet 200000 som utdata. Men når vi kjører tallet 100000 flere ganger, vil vi ikke bare se et feil svar, men også forskjellige feil svar. Løsningen ligger i at for å øke tallet kreves det tre operasjoner - hente tallet fra minnet, inkrementere det, og deretter skrive tallet tilbake. Siden alle disse instruksjonene ikke utføres atomisk (alle samtidig), kan slike merkelige ting skje. Dette problemet kalles i programmering race condition - tilstand av kappløp. Når ukjente krefter på ukjente tidspunkter kan påvirke utførelsen av dine operasjoner.

Kilde: habr.com

Kjøp pålitelig hosting for nettsteder med DDoS-beskyttelse, VPS VDS-servere 🔥 Kjøp pålitelig hosting for nettsteder med DDoS-beskyttelse, VPS VDS-servere | ProHoster