Ero sivun ”Järjestelmäkutsu” versioiden välillä
Jem (keskustelu | muokkaukset) (Ak: Uusi sivu: Järjestelmäkutsut muodostavat rajapinnan prosessissa suoritettavan User Mode -ohjelman ja ytimen välillä. Rajapinnan avulla ohjelma voi käyttää ytimen tarjoamia palveluita,...) |
Jem (keskustelu | muokkaukset) pEi muokkausyhteenvetoa |
||
Rivi 9: | Rivi 9: | ||
toiset vähän harvemmin käytettyjä. On tärkeää huomata, että [[POSIX]]-standardi, jota | toiset vähän harvemmin käytettyjä. On tärkeää huomata, että [[POSIX]]-standardi, jota | ||
Linux noudattaa, ei erottele järjestelmäkutsuja tavallisista funktiokutsuista. | Linux noudattaa, ei erottele järjestelmäkutsuja tavallisista funktiokutsuista. | ||
POSIX määrittelee vain rajapinnan, ei sitä, mikä rajapinnan funktio on | POSIX määrittelee vain [[API|rajapinnan]], ei sitä, mikä rajapinnan funktio on | ||
tavallinen kirjastofunktio ja mikä oikeasti järjestelmäkutsu. | tavallinen kirjastofunktio ja mikä oikeasti järjestelmäkutsu. | ||
Järjestelmäkutsut ovat olemassa vain siksi, että ydin pystyisi takaamaan | Järjestelmäkutsut ovat olemassa vain siksi, että ydin pystyisi takaamaan | ||
Rivi 31: | Rivi 31: | ||
* käyttäjien hallinta: <tt>getuid, setuid, chown, chgrp</tt> | * käyttäjien hallinta: <tt>getuid, setuid, chown, chgrp</tt> | ||
Järjestelmäkutsut on dokumentoitu Linuxin manuaalin kappaleessa 2. | Järjestelmäkutsut on dokumentoitu Linuxin manuaalin kappaleessa 2. Lisätietoja: | ||
man 2 intro | |||
==Järjestelmäkutsumekanismi== | ==Järjestelmäkutsumekanismi== | ||
Rivi 45: | Rivi 45: | ||
oikeutta lukea tiedostoa tai tappaa prosessia. | oikeutta lukea tiedostoa tai tappaa prosessia. | ||
Järjestelmäkutsussa tapahtuu siirtymä | Järjestelmäkutsussa tapahtuu siirtymä suorittimen (CPU) tilassa User Modesta | ||
[[Kernel Mode]] -tilaan. Miten tämä tapahtuu riippuu käytetyn | [[Kernel Mode]] -tilaan. Miten tämä tapahtuu riippuu käytetyn suorittimen | ||
arkkitehtuurista, mutta tyypillinen tapa on niin sanottu "ohjelmallinen | arkkitehtuurista, mutta tyypillinen tapa on niin sanottu "ohjelmallinen | ||
keskeytys" (software interrupt). Tämä mekanismi toimii siten, että | keskeytys" (software interrupt). Tämä mekanismi toimii siten, että suoritin | ||
suorittaa 'int'- tai 'trap'-käskyn, jonka seurauksena | suorittaa 'int'- tai 'trap'-käskyn, jonka seurauksena suoritin vaihtaa | ||
tilansa Supervisor Mode -tilaan ja jatkaa suoritusta määritellystä | tilansa Supervisor Mode -tilaan ja jatkaa suoritusta määritellystä | ||
keskeytyksen käsittelijärutiinista. | keskeytyksen käsittelijärutiinista. | ||
Intel x86-perheen | Intel x86-perheen suorittimilla Linuxissa on järjestelmäkutsut perinteisesti toteutettu | ||
<tt>int $0x80</tt> - | <tt>int $0x80</tt> -käskyllä. Järjestelmäkutsun | ||
numero välitetään <tt>eax</tt>-rekisterissä, muut parametrit rekistereissä | numero välitetään <tt>eax</tt>-rekisterissä, muut parametrit rekistereissä | ||
<tt>ebx</tt>, <tt>ecx</tt>, jne., riippuen kutsun parametrien lukumäärästä. | <tt>ebx</tt>, <tt>ecx</tt>, jne., riippuen kutsun parametrien lukumäärästä. | ||
Rivi 69: | Rivi 69: | ||
Tällä tavalla saadaan eristettyä varsinaisen järjestelmäkutsun monimutkaisuus | Tällä tavalla saadaan eristettyä varsinaisen järjestelmäkutsun monimutkaisuus | ||
Libc-kirjaston hoidettavaksi Sovellusohjelmat voivat kutsua Libc:n vastaavaa | Libc-kirjaston hoidettavaksi Sovellusohjelmat voivat kutsua Libc:n vastaavaa | ||
funktiota normaalilla korkean tason kielen (C) kutsulla, assembly-koodin sijasta. | funktiota normaalilla korkean tason kielen ([[C]]) kutsulla, assembly-koodin sijasta. | ||
Lisäksi ytimen kutsutapa on riippumaton kääntäjän käyttämästä C-kielen kutsutavasta. | Lisäksi ytimen kutsutapa on riippumaton kääntäjän käyttämästä C-kielen kutsutavasta. | ||
Sovellusohjelmissa voidaan jopa vaihtaa kutsutapaa (esimerkiksi käyttämään parametrinvälitystä | Sovellusohjelmissa voidaan jopa vaihtaa kutsutapaa (esimerkiksi käyttämään parametrinvälitystä | ||
Rivi 76: | Rivi 76: | ||
kääntämistä uudella kääntäjällä tai kääntäjän optioita muuttaen.) | kääntämistä uudella kääntäjällä tai kääntäjän optioita muuttaen.) | ||
Suorittimen rekistereitä käytetään parametrien välittämiseen siksi, | |||
että | että suorittimen tilan vaihtuessa User Mode:sta Supervisor Mode -tilaan, | ||
vaihtuu myös käytettävä pino. | vaihtuu myös käytettävä pino. | ||
==Sysenter ja Sysreturn== | ==Sysenter ja Sysreturn== | ||
Intelin x86- | Intelin x86-suorittimissa on Pentium II:sta alkaen ollut vaihtoehtoinen | ||
käsky, jolla siirtymän User Modesta Kernel Mode -tilaan voi toteuttaa: <tt>sysenter</tt>. | käsky, jolla siirtymän User Modesta Kernel Mode -tilaan voi toteuttaa: <tt>sysenter</tt>. | ||
Tämä käsky keksittiin tuomaan ratkaisu int-käskyyn liittyvään ongelmaan. | Tämä käsky keksittiin tuomaan ratkaisu int-käskyyn liittyvään ongelmaan. | ||
<tt>int $0x80</tt> -mekanismi on hidas, eli suoritus vaatii monta | <tt>int $0x80</tt> -mekanismi on hidas, eli suoritus vaatii monta | ||
kellojaksoa, ja se on muuttunut nykyaikaisissa | kellojaksoa, ja se on muuttunut nykyaikaisissa suorittimissa | ||
suhteellisesti mitattuna yhä hitaammaksi. Uusi <tt>sysenter</tt>-käsky on | suhteellisesti mitattuna yhä hitaammaksi. Uusi <tt>sysenter</tt>-käsky on | ||
nopea, mutta vanhemmat | nopea, mutta vanhemmat suorittimet eivät tue sitä, jolloin on | ||
käytettävä | käytettävä perinteistä <tt>int $0x80</tt>-käskyä. (<tt>sysenter</tt>-käskyn kanssa | ||
samanaikaisesti esitellyllä <tt>sysreturn</tt>-käskyllä palataan järjestelmäkutsusta.) | samanaikaisesti esitellyllä <tt>sysreturn</tt>-käskyllä palataan järjestelmäkutsusta.) | ||
Tavoitteena on toisaalta | Tavoitteena on toisaalta | ||
pitää Libc riippumattomana | pitää Libc riippumattomana suorittimen versiosta ja myös puhtaana | ||
koodista, joka ajon aikana tutkii mitä käskyjä | koodista, joka ajon aikana tutkii mitä käskyjä suoritin tukee ja | ||
mitä se ei tue. | mitä se ei tue. | ||
Ratkaisuna tähän ongelmaan ''ydin'' tarjoaa koodin, jolla | Ratkaisuna tähän ongelmaan ''ydin'' tarjoaa koodin, jolla | ||
järjestelmäkutsu suoritetaan suoritinkohtaisesti optimaalisella | |||
tavalla: joko <tt>sysenter</tt>-käskyä käyttäen, <tt>int $0x80</tt>-käskyllä, | tavalla: joko <tt>sysenter</tt>-käskyä käyttäen, <tt>int $0x80</tt>-käskyllä, | ||
tai tulevaisuudessa mahdollisesti jollain muulla mekanismilla. Tämä | tai tulevaisuudessa mahdollisesti jollain muulla mekanismilla. Tämä |
Versio 24. lokakuuta 2011 kello 11.31
Järjestelmäkutsut muodostavat rajapinnan prosessissa suoritettavan User Mode -ohjelman ja ytimen välillä. Rajapinnan avulla ohjelma voi käyttää ytimen tarjoamia palveluita, kuten esimerkiksi lukea tiedostosta tai käynnistää uuden ohjelman.
Yleistä
Linuxissa on yli kolmesataa erilaista järjestelmäkutsua, toiset keskeisempiä, toiset vähän harvemmin käytettyjä. On tärkeää huomata, että POSIX-standardi, jota Linux noudattaa, ei erottele järjestelmäkutsuja tavallisista funktiokutsuista. POSIX määrittelee vain rajapinnan, ei sitä, mikä rajapinnan funktio on tavallinen kirjastofunktio ja mikä oikeasti järjestelmäkutsu. Järjestelmäkutsut ovat olemassa vain siksi, että ydin pystyisi takaamaan lupaamansa säännöt, kuten muistialueiden ja tiedostojen suojaukset.
Joskus onkin käynyt niin, että järjestelmäkutsu on "alennettu" kirjastofunktioksi. Esimerkiksi fork-funktio oli alunperin Linuxin järjestelmäkutsu, mutta se korvattiin yleiskäyttöisemmällä clone-kutsulla, ja fork:sta tehtiin tavallinen funktio, joka kutsuu clone:a.
Esimerkkejä järjestelmäkutsuista
Seuraavassa luettelo muutamasta usein käytetyistä kutsuista.
- tiedostojen käsittely: creat, open, read, write, lseek, close, chmod, link, unlink
- prosessien käsittely: clone, exec, wait, execve, nice, exit
- verkkorajapintaan liittyvät kutsut: socketcall (sisältäen socket, connect, bind, listen, accept)
- käyttäjien hallinta: getuid, setuid, chown, chgrp
Järjestelmäkutsut on dokumentoitu Linuxin manuaalin kappaleessa 2. Lisätietoja:
man 2 intro
Järjestelmäkutsumekanismi
Sovellusohjelman ei voida sallia kutsua ytimen koodia ihan vapaasti, vaan sen on tapahduttava määritellyn kutsurajapinnan kautta. Rajoittamalla kutsut tapahtuviksi tiettyjen osoitteiden kautta varmistutaan siitä, että ytimen koodia kutsutaan oikein. Järjestelmäkutsun käsittelijärutiinissa tehdään tyypillisesti tarkistuksia, kuten syötettyjen parametrien järkevyyden testausta. Useimmissa tapauksissa tarkistetaan myös onko kutsujalla oikeus suorittaa kutsu; esimerkiksi onko käyttäjällä oikeutta lukea tiedostoa tai tappaa prosessia.
Järjestelmäkutsussa tapahtuu siirtymä suorittimen (CPU) tilassa User Modesta Kernel Mode -tilaan. Miten tämä tapahtuu riippuu käytetyn suorittimen arkkitehtuurista, mutta tyypillinen tapa on niin sanottu "ohjelmallinen keskeytys" (software interrupt). Tämä mekanismi toimii siten, että suoritin suorittaa 'int'- tai 'trap'-käskyn, jonka seurauksena suoritin vaihtaa tilansa Supervisor Mode -tilaan ja jatkaa suoritusta määritellystä keskeytyksen käsittelijärutiinista.
Intel x86-perheen suorittimilla Linuxissa on järjestelmäkutsut perinteisesti toteutettu int $0x80 -käskyllä. Järjestelmäkutsun numero välitetään eax-rekisterissä, muut parametrit rekistereissä ebx, ecx, jne., riippuen kutsun parametrien lukumäärästä.
Sovellusohjelmat eivät kutsu järjestelmäkutsuja suoraan, vaan samannimisia funktioita standardikirjasto Libc:ssä, jotka muodostavat ohuen "kuoren" järjestelmäkutsun ympärille. Nämä funktiot suorittavat sitten kukin vastaavan järjestelmäkutsun. Syynä tähän järjestelyyn on se, että järjestelmäkutsuilla on oma kutsutapansa; parametrit siirretään rekistereissä ja itse kutsua ei suoriteta call-käskyllä, vaan muulla mekanismlla, esim. yllä mainitulla int $0x80 -käskyllä.
Tällä tavalla saadaan eristettyä varsinaisen järjestelmäkutsun monimutkaisuus Libc-kirjaston hoidettavaksi Sovellusohjelmat voivat kutsua Libc:n vastaavaa funktiota normaalilla korkean tason kielen (C) kutsulla, assembly-koodin sijasta. Lisäksi ytimen kutsutapa on riippumaton kääntäjän käyttämästä C-kielen kutsutavasta. Sovellusohjelmissa voidaan jopa vaihtaa kutsutapaa (esimerkiksi käyttämään parametrinvälitystä rekistereissä pinon sijasta) muuttamatta ytimen järjestelmäkutsurajapintaa. (Tämä tosin vaatii sekä sovellusten että kirjastojen, mukaan lukien libc, uudelleen kääntämistä uudella kääntäjällä tai kääntäjän optioita muuttaen.)
Suorittimen rekistereitä käytetään parametrien välittämiseen siksi, että suorittimen tilan vaihtuessa User Mode:sta Supervisor Mode -tilaan, vaihtuu myös käytettävä pino.
Sysenter ja Sysreturn
Intelin x86-suorittimissa on Pentium II:sta alkaen ollut vaihtoehtoinen käsky, jolla siirtymän User Modesta Kernel Mode -tilaan voi toteuttaa: sysenter. Tämä käsky keksittiin tuomaan ratkaisu int-käskyyn liittyvään ongelmaan.
int $0x80 -mekanismi on hidas, eli suoritus vaatii monta kellojaksoa, ja se on muuttunut nykyaikaisissa suorittimissa suhteellisesti mitattuna yhä hitaammaksi. Uusi sysenter-käsky on nopea, mutta vanhemmat suorittimet eivät tue sitä, jolloin on käytettävä perinteistä int $0x80-käskyä. (sysenter-käskyn kanssa samanaikaisesti esitellyllä sysreturn-käskyllä palataan järjestelmäkutsusta.)
Tavoitteena on toisaalta pitää Libc riippumattomana suorittimen versiosta ja myös puhtaana koodista, joka ajon aikana tutkii mitä käskyjä suoritin tukee ja mitä se ei tue.
Ratkaisuna tähän ongelmaan ydin tarjoaa koodin, jolla järjestelmäkutsu suoritetaan suoritinkohtaisesti optimaalisella tavalla: joko sysenter-käskyä käyttäen, int $0x80-käskyllä, tai tulevaisuudessa mahdollisesti jollain muulla mekanismilla. Tämä koodi sijaitsee prosessin osoiteavaruudessa pienellä, ennalta määritetyllä muistialueella. Libc suorittaa järjestelmäkutsun kutsumalla funktiota tällä alueella, ja funktio tekee varsinaisen järjestelmäkutsun. Libc joutuu toki edelleen tutkimaan, onko tämä muistialue olemassa jotta kirjasto toimisi myös ytimellä, joka ei tue tätä uutta mekanismia.
User Mode -järjestelmäkutsut
Nopeammallakin sysenter-käskyllä suoritettuna järjestelmäkutsu on suhteellisen paljon aikaa vievä operaatio. Kaikkia kutsuja varten ei kuitenkaan tarvitse tehdä siirtymää Kernel Mode -tilaan. Esimerkiksi gettimeofday-kutsu, joka palauttaa ytimen käsityksen kellonajasta, voidaan toteuttaa siten, että ydin sijoittaa toistuvasti tuoreen arvon tunnettuun muistipaikkaan prosessin muistiavaruudessa. gettimeofday-funktio voi käydä lukemassa arvon tästä muistipaikasta, tarvitsematta suorittaa sysenter-käskyä.