Vadnica za sistemske klice v sistemu Linux s C.

Linux System Call Tutorial With C



V našem zadnjem članku o Sistemski klici v sistemu Linux , Določil sem sistemski klic, razpravljal o razlogih, ki bi jih lahko uporabili v programu, ter se poglobil v njihove prednosti in slabosti. Dal sem celo kratek primer pri sestavljanju znotraj C. Ponazoril je bistvo in opisal, kako klicati, vendar ni storil nič produktivnega. Ni ravno navdušujoča razvojna vaja, vendar je ponazorila bistvo.

V tem članku bomo uporabili dejanske sistemske klice za resnično delo v našem programu C. Najprej bomo pregledali, ali morate uporabiti sistemski klic, nato podali primer s klicem sendfile (), ki lahko dramatično izboljša zmogljivost kopiranja datotek. Nazadnje bomo preučili nekaj točk, ki si jih moramo zapomniti pri uporabi sistemskih klicev v sistemu Linux.







Čeprav je na neki točki vaše razvojne kariere C neizogibno uporabiti sistemski klic, razen če ciljate na visoko zmogljivost ali funkcijo določene vrste, bodo za večino vaše potrebe.



Standardna knjižnica glibc ponuja navzkrižno platformo, dobro preizkušen okvir za izvajanje funkcij, ki bi sicer zahtevale sistemske klice. Na primer, lahko preberete datoteko s fscanf (), fread (), getc () itd., Lahko pa uporabite tudi sistemski klic read () Linux. Funkcije glibc ponujajo več funkcij (npr. Boljše ravnanje z napakami, formatiran IO itd.) In bodo delovale na vseh sistemskih podporah glibc.



Po drugi strani pa obstajajo trenutki, ko sta brezkompromisna zmogljivost in natančna izvedba ključnega pomena. Ovoj, ki ga ponuja fread (), bo dodal stroške in čeprav manjši, ni povsem pregleden. Poleg tega morda ne želite ali potrebujete dodatnih funkcij, ki jih ponuja ovoj. V tem primeru vam bo najbolje ponudil sistemski klic.





Sistemske klice lahko uporabite tudi za izvajanje funkcij, ki jih glibc še ne podpira. Če je vaša kopija programa glibc posodobljena, to verjetno ne bo težava, vendar bo razvoj te tehnike na starejših distribucijah z novejšimi jedri morda zahteval.

Zdaj, ko ste prebrali zavrnitve odgovornosti, opozorila in morebitne obvoze, poglejmo zdaj nekaj praktičnih primerov.



Na katerem procesorju smo?

Vprašanje, ki si ga večina programov verjetno ne misli zastaviti, a vseeno veljavno. To je primer sistemskega klica, ki ga ni mogoče podvojiti z glibc in ni pokrit z ovojem glibc. V tej kodi bomo klic getcpu () poklicali neposredno prek funkcije syscall (). Funkcija syscall deluje na naslednji način:

syscall(SYS_pokliči,arg1,arg2,...);

Prvi argument, SYS_call, je definicija, ki predstavlja številko sistemskega klica. Ko vključite sys/syscall.h, so te vključene. Prvi del je SYS_, drugi del pa je ime sistemskega klica.

Argumenti klica gredo v arg1, arg2 zgoraj. Nekateri klici zahtevajo več argumentov in se bodo nadaljevali po vrstnem redu s svoje strani za moške. Ne pozabite, da bo večina argumentov, zlasti za vračila, zahtevala kazalce za niz nizov ali pomnilnik, dodeljen prek funkcije malloc.

example1.c

#vključi
#vključi
#vključi
#vključi

intglavni() {

brez podpisaprocesor,vozlišče;

// S sistemskim klicem pridobite trenutno jedro CPU -ja in vozlišče NUMA
// Upoštevajte, da ta nima glibc ovoja, zato ga moramo poklicati neposredno
syscall(SYS_getcpu, &procesor, &vozlišče,NIČ);

// Prikaz informacij
printf ('Ta program deluje na jedru CPU %u in vozlišču NUMA %u. n n',procesor,vozlišče);

vrnitev 0;

}

Za sestavljanje in zagon:

Primer gcc1.c -o primer 1
./primer1

Za bolj zanimive rezultate lahko vrtete niti prek knjižnice pthreads in nato pokličete to funkcijo, da vidite, na katerem procesorju se izvaja vaša nit.

Sendfile: Vrhunska zmogljivost

Sendfile je odličen primer izboljšanja zmogljivosti s sistemskimi klici. Funkcija sendfile () kopira podatke iz enega deskriptorja datoteke v drugega. Namesto da uporablja več funkcij fread () in fwrite (), sendfile izvede prenos v prostoru jedra, kar zmanjša stroške in s tem poveča zmogljivost.

V tem primeru bomo kopirali 64 MB podatkov iz ene datoteke v drugo. V enem testu bomo uporabili standardne metode branja/pisanja v standardni knjižnici. V drugem bomo za prestrezanje teh podatkov z ene lokacije na drugo uporabili sistemske klice in klic sendfile ().

test1.c (glibc)

#vključi
#vključi
#vključi
#vključi

#define BUFFER_SIZE 67108864
#define BUFFER_1 'buffer1'
#define BUFFER_2 'buffer2'

intglavni() {

MAPA*narobe, *konec;

printf (' nV/I preskus s tradicionalnimi funkcijami glibc. n n');

// Zgrabi vmesnik BUFFER_SIZE.
// Medpomnilnik bo vseboval naključne podatke, vendar nas to ne zanima.
printf ('Dodelitev 64 MB vmesnega pomnilnika:');
char *pufra= (char *) malloc (BUFFER_SIZE);
printf ('KONČANO n');

// Napišite vmesnik v fOut
printf ('Zapis podatkov v prvi medpomnilnik:');
narobe= fopen (BUFFER_1, 'wb');
fwrite (pufra, velikostof(char),BUFFER_SIZE,narobe);
blizu (narobe);
printf ('KONČANO n');

printf ('Kopiranje podatkov iz prve datoteke v drugo:');
konec= fopen (BUFFER_1, 'rb');
narobe= fopen (BUFFER_2, 'wb');
fread (pufra, velikostof(char),BUFFER_SIZE,konec);
fwrite (pufra, velikostof(char),BUFFER_SIZE,narobe);
blizu (konec);
blizu (narobe);
printf ('KONČANO n');

printf ('Sprostitev medpomnilnika:');
prost (pufra);
printf ('KONČANO n');

printf ('Brisanje datotek:');
Odstrani (BUFFER_1);
Odstrani (BUFFER_2);
printf ('KONČANO n');

vrnitev 0;

}

test2.c (sistemski klici)

#vključi
#vključi
#vključi
#vključi
#vključi
#vključi
#vključi
#vključi
#vključi

#define BUFFER_SIZE 67108864

intglavni() {

intnarobe,konec;

printf (' nV/I preskus s sendfile () in povezanimi sistemskimi klici. n n');

// Zgrabi vmesnik BUFFER_SIZE.
// Medpomnilnik bo vseboval naključne podatke, vendar nas to ne zanima.
printf ('Dodelitev 64 MB vmesnega pomnilnika:');
char *pufra= (char *) malloc (BUFFER_SIZE);
printf ('KONČANO n');


// Napišite vmesnik v fOut
printf ('Zapis podatkov v prvi medpomnilnik:');
narobe=odprto('buffer1',O_RDONLY);
pisati(narobe, &pufra,BUFFER_SIZE);
blizu(narobe);
printf ('KONČANO n');

printf ('Kopiranje podatkov iz prve datoteke v drugo:');
konec=odprto('buffer1',O_RDONLY);
narobe=odprto('buffer2',O_RDONLY);
sendfile(narobe,konec, 0,BUFFER_SIZE);
blizu(konec);
blizu(narobe);
printf ('KONČANO n');

printf ('Sprostitev medpomnilnika:');
prost (pufra);
printf ('KONČANO n');

printf ('Brisanje datotek:');
prekiniti povezavo('buffer1');
prekiniti povezavo('buffer2');
printf ('KONČANO n');

vrnitev 0;

}

Sestavljanje in izvajanje testov 1 in 2

Za izdelavo teh primerov boste potrebovali razvojna orodja, nameščena v vaši distribuciji. V Debianu in Ubuntuju lahko to namestite z:

aptnamestitegradbene osnove

Nato sestavite z:

gcctest1.c-alitest1&& gcctest2.c-alitest2

Če želite zagnati oboje in preizkusiti delovanje, zaženite:

čas./test1&& čas./test2

Morali bi dobiti take rezultate:

V/I preskus s tradicionalnimi funkcijami glibc.

Dodelitev 64 MB medpomnilnika: KONČANO
Zapis podatkov v prvi medpomnilnik: KONČANO
Kopiranje podatkov iz prve datoteke v drugo: KONČANO
Sprostitev medpomnilnika: KONČANO
Brisanje datotek: KONČANO
realnih 0m0.397s
uporabnik 0m0.000s
sys 0m0.203s
V/I preskus s sendfile () in povezanimi sistemskimi klici.
Dodelitev 64 MB medpomnilnika: KONČANO
Zapis podatkov v prvi medpomnilnik: KONČANO
Kopiranje podatkov iz prve datoteke v drugo: KONČANO
Sprostitev medpomnilnika: KONČANO
Brisanje datotek: KONČANO
realnih 0m0.019s
uporabnik 0m0.000s
sys 0m0.016s

Kot lahko vidite, koda, ki uporablja sistemske klice, teče veliko hitreje kot ekvivalent glibc.

Stvari, ki si jih morate zapomniti

Sistemski klici lahko povečajo zmogljivost in zagotovijo dodatne funkcije, vendar niso brez pomanjkljivosti. Upoštevati morate prednosti sistemskih klicev glede pomanjkanja prenosljivosti platforme in včasih zmanjšane funkcionalnosti v primerjavi s funkcijami knjižnice.

Ko uporabljate nekatere sistemske klice, morate paziti, da namesto knjižničnih funkcij uporabite sredstva, vrnjena iz sistemskih klicev. Na primer, struktura FILE, ki se uporablja za funkcije glibc fopen (), fread (), fwrite () in fclose (), ni enaka številki deskriptorja datoteke iz sistemskega klica open () (vrnjeno kot celo število). Njihovo mešanje lahko povzroči težave.

Na splošno imajo sistemski klici Linuxa manj odbijačev kot funkcije glibc. Čeprav je res, da imajo sistemski klici nekaj obravnave napak in poročanje o njih, boste podrobnejšo funkcijo dobili s funkcijo glibc.

In za konec še nekaj besed o varnosti. Sistemski klici so neposredno povezani z jedrom. Jedro Linuxa sicer ima obsežno zaščito pred goljufijo iz uporabniške zemlje, vendar obstajajo neodkrite hrošče. Ne zaupajte, da bo sistemski klic potrdil vaš vnos ali vas izoliral od varnostnih težav. Pametno je zagotoviti, da so podatki, ki jih posredujete sistemskemu klicu, očiščeni. Seveda je to dober nasvet za vsak klic API -ja, vendar pri delu z jedrom ne morete biti previdni.

Upam, da ste uživali v tem globljem potopu v deželo sistemskih klicev v sistemu Linux. Za celoten seznam sistemskih klicev Linuxa si oglejte naš glavni seznam.