Linux System Call Tutorial med C

Linux System Call Tutorial med C
I vår siste artikkel om Linux -systemanrop, definerte jeg en systemanrop, diskuterte grunnene til at man kan bruke dem i et program og gikk inn i fordelene og ulempene deres. Jeg ga til og med et kort eksempel i forsamlingen innen C. Det illustrerte poenget og beskrev hvordan man skulle ringe, men gjorde ingenting produktivt. Ikke akkurat en spennende utviklingsøvelse, men den illustrerte poenget.

I denne artikkelen skal vi bruke faktiske systemanrop for å gjøre ekte arbeid i C -programmet vårt. Først vil vi gjennomgå hvis du trenger å bruke en systemanrop, og deretter gi et eksempel ved å bruke SendFile () -samtalen som dramatisk kan forbedre File Copy -ytelsen. Til slutt vil vi gå over noen punkter å huske mens vi bruker Linux -systemanrop.

Trenger du en systemanrop?

Selv om det er uunngåelig, vil du bruke en systemanrop på et tidspunkt i C -utviklingskarrieren din, med mindre du er rettet mot høy ytelse eller en bestemt type funksjonalitet, vil Glibc -biblioteket og andre grunnleggende biblioteker inkludert i store Linux -distribusjoner ta seg av flertallet av dine behov.

Glibc Standard Library gir en tverrplattform, godt testet rammeverk for å utføre funksjoner som ellers vil kreve systemspesifikke systemanrop. For eksempel kan du lese en fil med fscanf (), Fread (), getc (), etc., eller du kan bruke lest () Linux -systemanropet. Glibc -funksjonene gir flere funksjoner (i.e. Bedre feilhåndtering, formatert io osv.) og vil jobbe med ethvert system glibc støtter.

På den annen side er det tider der kompromissløst ytelse og nøyaktig utførelse er kritiske. Innpakningen som Fread () gir, kommer til å legge til overhead, og selv om mindre, er ikke helt gjennomsiktig. I tillegg kan det hende du ikke vil ha eller trenger de ekstra funksjonene innpakningen gir. I så fall serveres du best med en systemanrop.

Du kan også bruke systemanrop for å utføre funksjoner som ennå ikke er støttet av GLIBC. Hvis kopien av Glibc er oppdatert, vil dette neppe være et problem, men å utvikle seg på eldre distribusjoner med nyere kjerner kan kreve denne teknikken.

Nå som du har lest ansvarsfraskrivelser, advarsler og potensielle omveier, la oss nå grave i noen praktiske eksempler.

Hvilken CPU er vi på?

Et spørsmål som de fleste programmer sannsynligvis ikke tenker å stille, men likevel en gyldig. Dette er et eksempel på en systemanrop som ikke kan dupliseres med glibc og ikke er dekket med en glibc -innpakning. I denne koden vil vi ringe GetCPU () -kallet direkte via SysCall () -funksjonen. Syscall -funksjonen fungerer som følger:

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

Det første argumentet, SYS_CALL, er en definisjon som representerer antall systemanrop. Når du inkluderer sys/syscall.H, disse er inkludert. Den første delen er sys_ og den andre delen er navnet på systemanropet.

Argumenter for samtalen går inn i Arg1, Arg2 ovenfor. Noen samtaler krever flere argumenter, og de vil fortsette i orden fra mannens side. Husk at de fleste argumenter, spesielt for avkastning, vil kreve pekere til char -matriser eller minne som er tildelt via malloc -funksjonen.

Eksempel1.c

#inkludere
#inkludere
#inkludere
#inkludere
int main ()
usignert CPU, node;
// Få gjeldende CPU -kjerne og NUMA -node via systemanrop
// Merk at dette ikke har noen glibc -innpakning, så vi må kalle det direkte
syscall (sys_getcpu, & cpu, & node, null);
// Vis informasjon
Printf ("Dette programmet kjører på CPU Core %U og NUMA Node %U.\ n \ n ", cpu, node);
retur 0;

Å kompilere og løpe:
GCC Eksempel1.C -o Eksempel1
./Eksempel1

For mer interessante resultater kan du snurre tråder via Pthreads -biblioteket og deretter ringe denne funksjonen for å se på hvilken prosessor tråden din kjører.

SendFile: Overlegen ytelse

SendFile gir et utmerket eksempel på forbedring av ytelsen gjennom systemanrop. SendFile () -funksjonen kopierer data fra en filbeskrivelse til en annen. I stedet for å bruke flere Fread () og Fwrite () -funksjoner, utfører SendFile overføringen i kjerneområdet, reduserer overhead og øker dermed ytelsen.

I dette eksemplet skal vi kopiere 64 MB data fra en fil til en annen. I en test kommer vi til å bruke standard lese-/skrivemetoder i standardbiblioteket. I den andre bruker vi systemanrop og sendfile () -kall for å sprenge disse dataene fra det ene stedet til et annet.

test1.C (glibc)

#inkludere
#inkludere
#inkludere
#inkludere
#Define Buffer_Size 67108864
#define buffer_1 "buffer1"
#define buffer_2 "buffer2"
int main ()
Fil *fout, *fin;
printf ("\ ni/o test med tradisjonelle glibc -funksjoner.\ n \ n ");
// ta en buffer_størrelse buffer.
// bufferen vil ha tilfeldige data i seg, men vi bryr oss ikke om det.
printf ("tildeler 64 MB buffer:");
char *buffer = (char *) malloc (buffer_size);
printf ("gjort \ n");
// skriv bufferen til Fout
printf ("Skrive data til First Buffer:");
fout = fopen (buffer_1, "wb");
fwrite (buffer, størrelse av (char), buffer_størrelse, fout);
fclose (fout);
printf ("gjort \ n");
printf ("Kopiering av data fra første fil til andre:");
fin = fopen (buffer_1, "rb");
fout = fopen (buffer_2, "wb");
Fread (buffer, størrelse av (røye), buffer_størrelse, fin);
fwrite (buffer, størrelse av (char), buffer_størrelse, fout);
fclose (fin);
fclose (fout);
printf ("gjort \ n");
printf ("frigjør buffer:");
gratis (buffer);
printf ("gjort \ n");
printf ("Slette filer:");
Fjern (buffer_1);
Fjern (buffer_2);
printf ("gjort \ n");
retur 0;

Test2.C (Systemanrop)

#inkludere
#inkludere
#inkludere
#inkludere
#inkludere
#inkludere
#inkludere
#inkludere
#inkludere
#Define Buffer_Size 67108864
int main ()
int fout, fin;
printf ("\ ni/o test med sendfile () og relaterte systemanrop.\ n \ n ");
// ta en buffer_størrelse buffer.
// bufferen vil ha tilfeldige data i seg, men vi bryr oss ikke om det.
printf ("tildeler 64 MB buffer:");
char *buffer = (char *) malloc (buffer_size);
printf ("gjort \ n");
// skriv bufferen til Fout
printf ("Skrive data til First Buffer:");
fout = åpen ("buffer1", o_rdonly);
Skriv (Fout, & Buffer, Buffer_Size);
Lukk (fout);
printf ("gjort \ n");
printf ("Kopiering av data fra første fil til andre:");
fin = åpen ("buffer1", o_rdonly);
fout = åpen ("buffer2", o_rdonly);
SendFile (Fout, Fin, 0, Buffer_Size);
Lukk (fin);
Lukk (fout);
printf ("gjort \ n");
printf ("frigjør buffer:");
gratis (buffer);
printf ("gjort \ n");
printf ("Slette filer:");
unlink ("buffer1");
unlink ("buffer2");
printf ("gjort \ n");
retur 0;

Sammenstille og løpe tester 1 og 2

For å bygge disse eksemplene, trenger du utviklingsverktøyene som er installert på distribusjonen din. På Debian og Ubuntu kan du installere dette med:

APT installere build-essensials

Deretter kompilere med:

GCC TEST1.c -o test1 && gcc test2.C -o Test2

For å kjøre begge deler og teste ytelsen, kjør:

tid ./test1 && tid ./test2

Du bør få resultater slik:

I/O -test med tradisjonelle glibc -funksjoner.

Tildeling av 64 MB -buffer: Ferdig
Skrive data til First Buffer: Ferdig
Kopiering av data fra første fil til sekund: Ferdig
Frigjøre buffer: Ferdig
Slette filer: Ferdig
ekte 0m0.397s
Bruker 0m0.000s
SYS 0M0.203S
I/O -test med SendFile () og relaterte systemanrop.
Tildeling av 64 MB -buffer: Ferdig
Skrive data til First Buffer: Ferdig
Kopiering av data fra første fil til sekund: Ferdig
Frigjøre buffer: Ferdig
Slette filer: Ferdig
ekte 0m0.019S
Bruker 0m0.000s
SYS 0M0.016s

Som du kan se, kjører koden som bruker systemanropene mye raskere enn glibc -ekvivalent.

Ting å huske

Systemanrop kan øke ytelsen og gi ytterligere funksjonalitet, men de er ikke uten ulempene. Du må veie fordelene systemanrop gir mot mangelen på plattformportabilitet og noen ganger redusert funksjonalitet sammenlignet med bibliotekfunksjoner.

Når du bruker noen systemanrop, må du passe på å bruke ressurser som returneres fra systemanrop i stedet for bibliotekfunksjoner. For eksempel er filstrukturen som brukes til Glibc's Fopen (), Fread (), Fwrite () og FClose () -funksjonene ikke de samme som filbeskrivelsesnummeret fra det åpne () systemanropet (returnert som et heltall). Å blande disse kan føre til problemer.

Generelt sett har Linux -systemanrop færre støtfangerbaner enn glibc -funksjoner. Selv om det er sant at systemanrop har noen feilhåndtering og rapportering, vil du få mer detaljert funksjonalitet fra en glibc -funksjon.

Og til slutt et ord om sikkerhet. Systemanrop direkte grensesnitt med kjernen. Linux -kjernen har omfattende beskyttelse mot shenanigans fra brukerland, men uoppdagede feil eksisterer. Ikke stol på at en systemanrop vil validere innspill eller isolere deg fra sikkerhetsproblemer. Det er lurt å sikre at dataene du leverer til en systemanrop blir desinfisert. Naturligvis er dette gode råd for enhver API -samtale, men du kan ikke være å forsiktig når du jobber med kjernen.

Jeg håper du likte dette dypere dykket ned i Linux -systemanropene. For en fullstendig liste over Linux -systemanrop, se vår hovedliste.