C Programiranje

Vodič za Linux sistemske pozive s C

Vodič za Linux sistemske pozive s C
U našem posljednjem članku o Linux sistemskim pozivima definirao sam sistemski poziv, razgovarao o razlozima zbog kojih bi ih mogli koristiti u programu i udubio se u njihove prednosti i nedostatke. Čak sam dao kratki primjer u montaži unutar C-a. Ilustrirao je poantu i opisao način upućivanja poziva, ali nije učinio ništa produktivno. Nije baš uzbudljiva razvojna vježba, ali ilustrirala je poantu.

U ovom ćemo članku koristiti stvarne sistemske pozive za stvarni posao u našem C programu. Prvo ćemo pregledati trebate li koristiti sistemski poziv, a zatim ćemo pružiti primjer pomoću poziva sendfile () koji može dramatično poboljšati izvedbu kopiranja datoteka. Na kraju ćemo razmotriti neke točke koje trebate upamtiti tijekom korištenja Linux sistemskih poziva.

Trebate li sistemski poziv?

Iako je neizbježno da ćete koristiti sistemski poziv u nekom trenutku vaše razvojne karijere, osim ako ne ciljate visoke performanse ili određene funkcije, knjižnica glibc i druge osnovne knjižnice uključene u glavne distribucije Linuxa pobrinut će se za većinu vaše potrebe.

Glibc standardna knjižnica pruža višeplatformirani, dobro testirani okvir za izvršavanje funkcija koje bi inače zahtijevale sistemske pozive specifične za sustav. Na primjer, možete pročitati datoteku pomoću fscanf (), fread (), getc () itd., ili možete koristiti read () Linux sistemski poziv. Funkcije glibc pružaju više značajki (tj.e. bolje rukovanje pogreškama, formatirani IO itd.) i radit će na bilo kojem sustavu glibc podrška.

S druge strane, postoje trenuci u kojima su beskompromisne performanse i točno izvršenje presudni. Omotač koji pruža fread () dodat će dodatak, iako je sporedan, ali nije u potpunosti proziran. Uz to, možda nećete trebati ili trebati dodatne značajke koje omot nudi. U tom slučaju, najbolje vam je pružiti sistemski poziv.

Također možete koristiti sistemske pozive za izvršavanje funkcija koje glibc još ne podržava. Ako je vaša kopija glibc ažurna, to teško da će predstavljati problem, ali razvoj ove tehnike na starijim distribucijama s novijim jezgrama možda će trebati ovu tehniku.

Sad kad ste pročitali izjave o odricanju odgovornosti, upozorenja i potencijalne zaobilaznice, sada ćemo istražiti neke praktične primjere.

Na kojem smo CPU-u?

Pitanje koje većina programa vjerojatno ne misli postaviti, ali ipak valjano. Ovo je primjer sistemskog poziva koji se ne može duplicirati s glibc i nije pokriven glibc omotom. U ovom kodu pozvat ćemo poziv getcpu () izravno putem funkcije syscall (). Funkcija syscall radi na sljedeći način:

syscall (SYS_call, arg1, arg2,…);

Prvi argument, SYS_call, definicija je koja predstavlja broj sistemskog poziva. Kada uključite sys / syscall.h, uključeni su. Prvi dio je SYS_, a drugi dio je naziv sistemskog poziva.

Argumenti za poziv idu u arg1, arg2 gore. Neki pozivi zahtijevaju više argumenata i nastavit će redom sa svoje man stranice. Imajte na umu da će za većinu argumenata, posebno za povrat, biti potrebni pokazivači na ucrtavanje nizova ili memorije dodijeljene putem funkcije malloc.

primjer1.c

#include
#include
#include
#include
 
int main ()
 
nepotpisani CPU, čvor;
 
// Dohvaćanje trenutne CPU jezgre i NUMA čvora putem sistemskog poziva
// Imajte na umu da nema glibc omotač pa ga moramo izravno nazvati
syscall (SYS_getcpu, & cpu, & node, NULL);
 
// Prikaz podataka
printf ("Ovaj program je pokrenut na CPU jezgri% u i NUMA čvoru% u.\ n \ n ", procesor, čvor);
 
return 0;
 

 
Da biste sastavili i pokrenuli:
 
gcc primjer1.c -o primjer1
./ primjer1

Za zanimljivije rezultate možete vrtjeti niti putem biblioteke pthreads, a zatim pozvati ovu funkciju da biste vidjeli na kojem se procesoru vaša nit izvodi.

Sendfile: Vrhunska izvedba

Sendfile pruža izvrstan primjer poboljšanja performansi sistemskim pozivima. Funkcija sendfile () kopira podatke iz jednog deskriptora datoteke u drugi. Umjesto da koristi više funkcija fread () i fwrite (), sendfile vrši prijenos u prostoru jezgre, smanjujući režijske troškove i time povećavajući performanse.

U ovom ćemo primjeru kopirati 64 MB podataka iz jedne datoteke u drugu. U jednom ćemo testu koristiti standardne metode čitanja / pisanja u standardnoj knjižnici. U drugom ćemo koristiti sistemske pozive i poziv sendfile () za razmještanje ovih podataka s jednog mjesta na drugo.

test1.c (glibc)

#include
#include
#include
#include
 
#define BUFFER_SIZE 67108864
#define BUFFER_1 "buffer1"
#define BUFFER_2 "buffer2"
 
int main ()
 
DATOTEKA * fOut, * fIn;
 
printf ("\ nI / O test s tradicionalnim glibc funkcijama.\ n \ n ");
 
// Dohvatite BUFFER_SIZE međuspremnik.
// U međuspremniku će biti slučajni podaci, ali nas to ne zanima.
printf ("Dodjela međuspremnika od 64 MB:");
char * međuspremnik = (char *) malloc (BUFFER_SIZE);
printf ("GOTOVO \ n");
 
// Napišite međuspremnik u fOut
printf ("Zapisivanje podataka u prvi međuspremnik:");
fOut = fopen (BUFFER_1, "wb");
fwrite (međuspremnik, veličina (char), BUFFER_SIZE, fOut);
fclose (fOut);
printf ("GOTOVO \ n");
 
printf ("Kopiranje podataka iz prve datoteke u drugu:");
fIn = fopen (BUFFER_1, "rb");
fOut = fopen (BUFFER_2, "wb");
fread (međuspremnik, veličina (char), BUFFER_SIZE, fIn);
fwrite (međuspremnik, veličina (char), BUFFER_SIZE, fOut);
fclose (fIn);
fclose (fOut);
printf ("GOTOVO \ n");
 
printf ("Oslobađanje međuspremnika:");
besplatno (tampon);
printf ("GOTOVO \ n");
 
printf ("Brisanje datoteka:");
ukloniti (BUFFER_1);
ukloniti (BUFFER_2);
printf ("GOTOVO \ n");
 
return 0;
 

test2.c (sistemski pozivi)

#include
#include
#include
#include
#include
#include
#include
#include
#include
 
#define BUFFER_SIZE 67108864
 
int main ()
 
int fOut, fIn;
 
printf ("\ nI / O test sa sendfile () i srodnim sistemskim pozivima.\ n \ n ");
 
// Dohvatite BUFFER_SIZE međuspremnik.
// U međuspremniku će biti slučajni podaci, ali nas to ne zanima.
printf ("Dodjela međuspremnika od 64 MB:");
char * buffer = (char *) malloc (BUFFER_SIZE);
printf ("GOTOVO \ n");
 
// Napišite međuspremnik u fOut
printf ("Zapisivanje podataka u prvi međuspremnik:");
fOut = otvoren ("buffer1", O_RDONLY);
pisanje (fOut, & buffer, BUFFER_SIZE);
zatvoriti (fOut);
printf ("GOTOVO \ n");
 
printf ("Kopiranje podataka iz prve datoteke u drugu:");
fIn = otvoren ("buffer1", O_RDONLY);
fOut = otvoren ("buffer2", O_RDONLY);
datoteka za slanje (fOut, fIn, 0, BUFFER_SIZE);
zatvoriti (fIn);
zatvoriti (fOut);
printf ("GOTOVO \ n");
 
printf ("Oslobađanje međuspremnika:");
besplatno (tampon);
printf ("GOTOVO \ n");
 
printf ("Brisanje datoteka:");
poništi vezu ("buffer1");
poništi vezu ("buffer2");
printf ("GOTOVO \ n");
 
return 0;
 

Sastavljanje i izvođenje testova 1 i 2

Za izgradnju ovih primjera trebat će vam razvojni alati instalirani na vašoj distribuciji. Na Debianu i Ubuntuu ovo možete instalirati sa:

apt instalirati osnovno gradivo

Zatim kompajlirajte sa:

gcc test1.c -o test1 && gcc test2.c -o test2

Da biste pokrenuli oba i testirali izvedbu, pokrenite:

vrijeme ./ test1 && vrijeme ./ test2

Trebali biste dobiti ovakve rezultate:

Ulazno-izlazni test s tradicionalnim glibc funkcijama.

Dodjela međuspremnika od 64 MB: GOTOVO
Zapisivanje podataka u prvi međuspremnik: GOTOVO
Kopiranje podataka iz prve datoteke u drugu: GOTOVO
Oslobađajući međuspremnik: GOTOVO
Brisanje datoteka: GOTOVO
stvarna 0m0.397-e
korisnik 0m0.000
sys 0m0.203-e
I / O test s sendfile () i povezanim sistemskim pozivima.
Dodjela međuspremnika od 64 MB: GOTOVO
Zapisivanje podataka u prvi međuspremnik: GOTOVO
Kopiranje podataka iz prve datoteke u drugu: GOTOVO
Oslobađajući međuspremnik: GOTOVO
Brisanje datoteka: GOTOVO
stvarna 0m0.019
korisnik 0m0.000
sys 0m0.016s

Kao što vidite, kod koji koristi sistemske pozive radi puno brže od ekvivalenta glibc.

Stvari koje treba zapamtiti

Sistemski pozivi mogu povećati performanse i pružiti dodatnu funkcionalnost, ali nisu bez nedostataka. Morat ćete odvagnuti prednosti sistemskih poziva prema nedostatku prenosivosti platforme i ponekad smanjenoj funkcionalnosti u usporedbi s funkcijama knjižnice.

Kada koristite neke sistemske pozive, morate paziti da koristite resurse vraćene iz sistemskih poziva, a ne funkcije knjižnice. Na primjer, struktura FILE koja se koristi za glibc-ove funkcije fopen (), fread (), fwrite () i fclose () nisu iste kao broj deskriptora datoteke iz sistemskog poziva open () (vraćen kao cijeli broj). Njihovo miješanje može dovesti do problema.

Općenito, Linux sistemski pozivi imaju manje zaštitnih traka nego glibc funkcije. Iako je istina da sistemski pozivi imaju određenu obradu pogrešaka i izvješćivanje, detaljniju funkciju dobit ćete od funkcije glibc.

I na kraju, riječ o sigurnosti. Pozivi sustava izravno se sučeljavaju s jezgrom. Jezgra Linuxa ima opsežnu zaštitu protiv prevara s korisničkog zemljišta, ali postoje neotkriveni bugovi. Ne vjerujte da će sistemski poziv provjeriti vaše podatke ili vas izolirati od sigurnosnih problema. Pametno je osigurati sanaciju podataka koje predajete na sistemski poziv. Naravno, ovo je dobar savjet za bilo koji API poziv, ali ne možete biti oprezni pri radu s jezgrom.

Nadam se da ste uživali u ovom dubljem zaronu u zemlju sistemskih poziva Linuxa. Potpuni popis sistemskih poziva za Linux pogledajte na našem glavnom popisu.

Instalirajte najnoviju strategiju igre OpenRA na Ubuntu Linux
OpenRA je Libre / Free Real Time strateški pokretač igre koji stvara rane Westwoodove igre poput klasične Command & Conquer: Red Alert. Distribuirani ...
Instalirajte najnoviji Dolphin Emulator za Gamecube i Wii na Linux
Dolphin Emulator omogućuje vam igranje odabranih Gamecube i Wii igara na Linux osobnim računalima (PC). Dolphin Emulator je slobodno dostupan i emula...
Kako koristiti GameConqueror Cheat Engine u Linuxu
Članak pokriva vodič o korištenju GameConqueror varalice u Linuxu. Mnogi korisnici koji igraju igre na sustavu Windows često koriste aplikaciju "Cheat...