Linux -järjestelmän puheluopetusohjelma C: llä

Linux System Call Tutorial With C



Viimeisessä artikkelissamme aiheesta Linux -järjestelmän puhelut , Määritin järjestelmäkutsun, keskustelin syistä, miksi niitä voitaisiin käyttää ohjelmassa, ja pohdin niiden etuja ja haittoja. Annoin jopa lyhyen esimerkin kokoonpanossa C: ssä. Se havainnollisti asiaa ja kuvasi, kuinka soittaa, mutta ei tuottanut mitään tuottavaa. Ei aivan jännittävä kehitysharjoitus, mutta se valaisi asian.

Tässä artikkelissa aiomme käyttää todellisia järjestelmäpuheluita todelliseen työhön C -ohjelmassamme. Ensin tarkastelemme, onko sinun käytettävä järjestelmäpuhelua, ja annamme sitten esimerkin sendfile () -kutsun avulla, joka voi parantaa dramaattisesti tiedostojen kopiointia. Lopuksi käymme läpi joitakin kohtia, jotka on muistettava käytettäessä Linux -järjestelmäpuheluita.







Vaikka se on väistämätöntä, käytät järjestelmäpuhelua jossain vaiheessa C -kehitysuraasi, ellet ole kohdistanut suurta suorituskykyä tai tietyn tyyppisiä toimintoja, glibc -kirjasto ja muut tärkeät Linux -jakelut sisältävät peruskirjastot huolehtivat suurimmasta osasta sinun tarpeesi.



Glibc-standardikirjasto tarjoaa alustanvälisen, hyvin testatun kehyksen toimintojen suorittamiseen, jotka muuten vaatisivat järjestelmäkohtaisia ​​järjestelmäkutsuja. Voit esimerkiksi lukea tiedoston, jossa on fscanf (), fread (), getc () jne., Tai voit käyttää read () Linux -järjestelmäkutsua. Glibc -toiminnot tarjoavat enemmän ominaisuuksia (eli parempaa virheiden käsittelyä, muotoiltua IO: ta jne.) Ja toimivat kaikissa järjestelmän glibc -tuissa.



Toisaalta on tilanteita, joissa tinkimätön suorituskyky ja tarkka toteutus ovat kriittisiä. Freadin () tarjoama kääre lisää yläpuolelle, ja vaikka se on vähäistä, se ei ole täysin läpinäkyvä. Lisäksi et ehkä halua tai tarvitse kääreen tarjoamia lisäominaisuuksia. Siinä tapauksessa sinua palvelee parhaiten järjestelmäpuhelu.





Voit myös käyttää järjestelmäpuheluita suorittaaksesi toimintoja, joita glibc ei vielä tue. Jos glibc -kopiosi on ajan tasalla, tästä tuskin tulee ongelma, mutta vanhempien jakelujen kehittäminen uudemmilla ytimillä saattaa vaatia tätä tekniikkaa.

Nyt kun olet lukenut vastuuvapauslausekkeet, varoitukset ja mahdolliset kiertoteet, pohditaan nyt joitain käytännön esimerkkejä.



Mikä CPU meillä on käytössä?

Kysymys, jota useimmat ohjelmat eivät luultavasti halua kysyä, mutta pätevä kuitenkin. Tämä on esimerkki järjestelmäkutsusta, jota ei voi kopioida glibc -ohjelmalla eikä peitetä glibc -kääreellä. Tässä koodissa soitamme getcpu () -puheluun suoraan syscall () -toiminnon kautta. Syscall -toiminto toimii seuraavasti:

syscall(SYS_call,arg1,arg2,...);

Ensimmäinen argumentti, SYS_call, on määritelmä, joka edustaa järjestelmäkutsun numeroa. Kun sisällytät sys/syscall.h, nämä sisältyvät. Ensimmäinen osa on SYS_ ja toinen osa on järjestelmäkutsun nimi.

Puhelun argumentit menevät yllä arg1, arg2. Jotkut puhelut vaativat enemmän argumentteja, ja ne jatkuvat järjestyksessä man -sivulta. Muista, että useimmat argumentit, erityisesti palautukset, vaativat osoittimia matriisin tai muistin malloc -funktion kautta varaamiseen.

esimerkki1.c

#sisältää
#sisältää
#sisältää
#sisältää

inttärkein() {

allekirjoittamatonprosessori,solmu;

// Hanki nykyinen suoritinydin ja NUMA -solmu järjestelmäkutsun kautta
// Huomaa, että tässä ei ole glibc -kääriä, joten meidän on kutsuttava sitä suoraan
syscall(SYS_getcpu, &prosessori, &solmu,TYHJÄ);

// Näytä tiedot
printf (Tämä ohjelma toimii suorittimen ytimessä %u ja NUMA -solmussa %u. n n'',prosessori,solmu);

palata 0;

}

Käännä ja ajaa:

gcc esimerkki 1.c -o esimerkki 1
./esimerkki 1

Saadaksesi mielenkiintoisempia tuloksia, voit pyörittää säikeitä pthreads -kirjaston kautta ja kutsua tämän toiminnon nähdäksesi, millä prosessorilla lanka on käynnissä.

Lähetystiedosto: Erinomainen suorituskyky

Sendfile on erinomainen esimerkki suorituskyvyn parantamisesta järjestelmäpuheluiden avulla. Sendfile () -toiminto kopioi tiedot tiedostojen kuvauksesta toiseen. Sen sijaan, että käytettäisiin useita fread () - ja fwrite () -toimintoja, sendfile suorittaa siirron ytintilassa vähentäen yleiskustannuksia ja parantamalla siten suorituskykyä.

Tässä esimerkissä kopioimme 64 megatavua dataa tiedostosta toiseen. Yhdessä testissä aiomme käyttää vakiokirjasto -luku-/kirjoitusmenetelmiä. Toisessa tapauksessa käytämme järjestelmäpuheluita ja sendfile () -puhelua tietojen välittämiseksi paikasta toiseen.

test1.c (glibc)

#sisältää
#sisältää
#sisältää
#sisältää

#define BUFFER_SIZE 67108864
#define BUFFER_1 'puskuri1'
#define BUFFER_2 'puskuri2'

inttärkein() {

TIEDOSTO*väärä, *loppuun;

printf ('' nI/O -testi perinteisillä glibc -toiminnoilla. n n'');

// Tartu BUFFER_SIZE -puskuriin.
// Puskurissa on satunnaisia ​​tietoja, mutta emme välitä siitä.
printf ('64 Mt: n puskurin jakaminen:');
hiiltyä *puskuri= (hiiltyä *) malloc (PUSKURIN KOKO);
printf ('TEHTY n'');

// Kirjoita puskuri fOutiin
printf ('Tietojen kirjoittaminen ensimmäiseen puskuriin:');
väärä= fopen (PUSKURI_1, 'wb');
fwrite (puskuri, koko(hiiltyä),PUSKURIN KOKO,väärä);
fclose (väärä);
printf ('TEHTY n'');

printf ('Tietojen kopioiminen ensimmäisestä tiedostosta toiseen:');
loppuun= fopen (PUSKURI_1, 'rb');
väärä= fopen (PUSKURI_2, 'wb');
fread (puskuri, koko(hiiltyä),PUSKURIN KOKO,loppuun);
fwrite (puskuri, koko(hiiltyä),PUSKURIN KOKO,väärä);
fclose (loppuun);
fclose (väärä);
printf ('TEHTY n'');

printf ('Vapautuspuskuri:');
vapaa (puskuri);
printf ('TEHTY n'');

printf (Tiedostojen poistaminen:);
Poista (PUSKURI_1);
Poista (PUSKURI_2);
printf ('TEHTY n'');

palata 0;

}

test2.c (järjestelmäkutsut)

#sisältää
#sisältää
#sisältää
#sisältää
#sisältää
#sisältää
#sisältää
#sisältää
#sisältää

#define BUFFER_SIZE 67108864

inttärkein() {

intväärä,loppuun;

printf ('' nI/O -testi sendfile (): n ja siihen liittyvien järjestelmäkutsujen kanssa. n n'');

// Tartu BUFFER_SIZE -puskuriin.
// Puskurissa on satunnaisia ​​tietoja, mutta emme välitä siitä.
printf ('64 Mt: n puskurin jakaminen:');
hiiltyä *puskuri= (hiiltyä *) malloc (PUSKURIN KOKO);
printf ('TEHTY n'');


// Kirjoita puskuri fOutiin
printf ('Tietojen kirjoittaminen ensimmäiseen puskuriin:');
väärä=avata('puskuri1',O_RDONLY);
kirjoittaa(väärä, &puskuri,PUSKURIN KOKO);
kiinni(väärä);
printf ('TEHTY n'');

printf ('Tietojen kopioiminen ensimmäisestä tiedostosta toiseen:');
loppuun=avata('puskuri1',O_RDONLY);
väärä=avata('puskuri2',O_RDONLY);
Lähetä tiedosto(väärä,loppuun, 0,PUSKURIN KOKO);
kiinni(loppuun);
kiinni(väärä);
printf ('TEHTY n'');

printf ('Vapautuspuskuri:');
vapaa (puskuri);
printf ('TEHTY n'');

printf (Tiedostojen poistaminen:);
poista linkitys('puskuri1');
poista linkitys('puskuri2');
printf ('TEHTY n'');

palata 0;

}

Testien 1 ja 2 kokoaminen ja suorittaminen

Näiden esimerkkien luomiseksi tarvitset jakeluun asennetut kehitystyökalut. Debianissa ja Ubuntussa voit asentaa tämän seuraavilla tavoilla:

sopivaAsentaarakennustarvikkeet

Kokoa sitten:

gcctesti1.c-taitesti 1&& gcctesti2.c-taitesti 2

Jos haluat suorittaa molemmat ja testata suorituskykyä, suorita:

aika./testi 1&& aika./testi 2

Sinun pitäisi saada tällaisia ​​tuloksia:

I/O -testi perinteisillä glibc -toiminnoilla.

64 Mt: n puskurin jakaminen: VALMIS
Tietojen kirjoittaminen ensimmäiseen puskuriin: VALMIS
Tietojen kopioiminen ensimmäisestä tiedostosta toiseen: VALMIS
Vapautuspuskuri: VALMIS
Tiedostojen poistaminen: VALMIS
todellinen 0m0.397s
käyttäjä 0m0.000s
sys 0m0,203s
I/O -testi sendfile (): n ja siihen liittyvien järjestelmäkutsujen kanssa.
64 Mt: n puskurin jakaminen: VALMIS
Tietojen kirjoittaminen ensimmäiseen puskuriin: VALMIS
Tietojen kopioiminen ensimmäisestä tiedostosta toiseen: VALMIS
Vapautuspuskuri: VALMIS
Tiedostojen poistaminen: VALMIS
todellinen 0m0.019s
käyttäjä 0m0.000s
sys 0m0.016s

Kuten näette, järjestelmäpuheluja käyttävä koodi toimii paljon nopeammin kuin glibc -vastaava.

Muistettavia asioita

Järjestelmäpuhelut voivat parantaa suorituskykyä ja tarjota lisätoimintoja, mutta ne eivät ole ilman haittoja. Sinun on punnittava järjestelmän puheluiden tarjoamat edut alustan siirrettävyyden puutetta ja toisinaan heikentyneitä toimintoja verrattuna kirjastotoimintoihin verrattuna.

Kun käytät joitakin järjestelmäpuheluita, sinun on huolehdittava siitä, että käytät järjestelmäpuheluista palautettuja resursseja kirjastotoimintojen sijasta. Esimerkiksi FILE -rakenne, jota käytetään glibcin fopen () -, fread () -, fwrite () - ja fclose () -toiminnoissa, eivät ole samat kuin avoimen () järjestelmäkutsun tiedostonkuvausnumero (palautetaan kokonaislukuna). Näiden sekoittaminen voi aiheuttaa ongelmia.

Yleensä Linux -järjestelmäpuheluissa on vähemmän puskurikaistoja kuin glibc -toimintoja. Vaikka on totta, että järjestelmäpuheluissa on jonkin verran virheiden käsittelyä ja raportointia, saat tarkempia toimintoja glibc -toiminnosta.

Ja lopuksi muutama sana turvallisuudesta. Järjestelmäpuhelut liitetään suoraan ytimeen. Linux -ytimellä on laaja suoja käyttäjien maahaittoja vastaan, mutta havaitsemattomia vikoja on olemassa. Älä luota siihen, että järjestelmäpuhelu vahvistaa syötteesi tai eristää sinut turvallisuusongelmista. On järkevää varmistaa, että järjestelmäpuheluun antamasi tiedot puhdistetaan. Tämä on tietysti hyvä neuvo kaikille API -kutsuille, mutta et voi olla varovainen työskennellessäsi ytimen kanssa.

Toivottavasti nautit tästä syvemmästä sukelluksesta Linux -järjestelmäkutsujen maahan. Täydellinen luettelo Linux -järjestelmäpuheluista on pääluettelossamme.