C Programmering

Linux System Call Tutorial med C

Linux System Call Tutorial med C
I vår senaste artikel om Linux-systemsamtal definierade jag ett systemanrop, diskuterade orsakerna till att man kunde använda dem i ett program och grävde in i deras fördelar och nackdelar. Jag gav till och med ett kort exempel vid montering inom C. Det illustrerade poängen och beskrev hur man ringer, men gjorde inget produktivt. Inte precis en spännande utvecklingsövning, men det illustrerade poängen.

I den här artikeln ska vi använda faktiska systemanrop för att göra riktigt arbete i vårt C-program. Först granskar vi om du behöver använda ett systemanrop och ger sedan ett exempel med hjälp av sendfile () -anropet som dramatiskt kan förbättra filkopieringens prestanda. Slutligen går vi igenom några punkter att komma ihåg när vi använder Linux-systemanrop.

Behöver du ett systemanrop?

Även om det är oundvikligt kommer du att använda ett systemanrop någon gång i din C-utvecklingskarriär, såvida du inte riktar dig till hög prestanda eller en viss typ av funktionalitet, kommer glibc-biblioteket och andra grundläggande bibliotek som ingår i större Linux-distributioner att ta hand om majoriteten av dina behov.

Glibc-standardbiblioteket tillhandahåller en plattformig, väl testad ram för att utföra funktioner som annars skulle kräva systemspecifika systemanrop. Du kan till exempel läsa en fil med fscanf (), fread (), getc (), etc., eller så kan du använda läs () Linux-systemanropet. Glibc-funktionerna ger fler funktioner (i.e. bättre felhantering, formaterad IO, etc.) och fungerar på alla system som stöds av glibc.

Å andra sidan finns det tillfällen där kompromisslös prestanda och exakt utförande är kritiska. Omslaget som fread () tillhandahåller kommer att lägga till overhead, och även om det är mindre, är det inte helt transparent. Dessutom kanske du inte vill ha eller behöver de extra funktioner som omslaget tillhandahåller. I så fall tjänar du bäst med ett systemanrop.

Du kan också använda systemanrop för att utföra funktioner som ännu inte stöds av glibc. Om din kopia av glibc är uppdaterad kommer detta knappast att vara ett problem, men att utveckla på äldre distributioner med nyare kärnor kan kräva denna teknik.

Nu när du har läst ansvarsfriskrivningarna, varningarna och potentiella omvägar, låt oss nu gräva i några praktiska exempel.

Vilken CPU är vi på?

En fråga som de flesta program nog inte tänker ställa, men ändå en giltig fråga. Detta är ett exempel på ett systemanrop som inte kan dupliceras med glibc och inte täcks av ett glibc-omslag. I den här koden kommer vi att ringa getcpu () -samtalet direkt via syscall () -funktionen. Syscall-funktionen fungerar enligt följande:

syscall (SYS_call, arg1, arg2,…);

Det första argumentet, SYS_call, är en definition som representerar antalet systemanrop. När du inkluderar sys / syscall.h, dessa ingår. Den första delen är SYS_ och den andra delen är namnet på systemanropet.

Argument för samtalet går in på arg1, arg2 ovan. Vissa samtal kräver fler argument, och de fortsätter i ordning från sin mansida. Kom ihåg att de flesta argument, särskilt för returer, kommer att kräva pekare för att char-arrays eller minne som tilldelats via malloc-funktionen.

exempel1.c

#omfatta
#omfatta
#omfatta
#omfatta
 
int main ()
 
osignerad cpu, nod;
 
// Få aktuell CPU-kärna och NUMA-nod via systemanrop
// Observera att detta inte har något glibc-omslag så vi måste ringa det direkt
syscall (SYS_getcpu, & cpu, & node, NULL);
 
// Visa information
printf ("Detta program körs på CPU-kärnan% u och NUMA-noden% u.\ n \ n ", cpu, node);
 
returnera 0;
 

 
Att kompilera och köra:
 
gcc exempel1.c-o exempel 1
./ exempel1

För mer intressanta resultat kan du snurra trådar via pthreads-biblioteket och sedan ringa den här funktionen för att se på vilken processor din tråd kör.

Sendfile: överlägsen prestanda

Sendfile är ett utmärkt exempel på att förbättra prestanda genom systemanrop. Funktionen sendfile () kopierar data från en filbeskrivare till en annan. I stället för att använda flera fread () och fwrite () -funktioner, utför sendfile överföringen i kärnutrymme, vilket minskar overhead och därmed ökar prestanda.

I det här exemplet ska vi kopiera 64 MB data från en fil till en annan. I ett test ska vi använda standardläs / skrivmetoderna i standardbiblioteket. I den andra använder vi systemanrop och sendfile () -anropet för att spränga dessa data från en plats till en annan.

test1.c (glibc)

#omfatta
#omfatta
#omfatta
#omfatta
 
#define BUFFER_SIZE 67108864
#define BUFFER_1 "buffer1"
#define BUFFER_2 "buffer2"
 
int main ()
 
FIL * fOut, * fIn;
 
printf ("\ nI / O-test med traditionella glibc-funktioner.\ n \ n ");
 
// Ta en BUFFER_SIZE-buffert.
// Bufferten kommer att ha slumpmässiga data i den, men vi bryr oss inte om det.
printf ("Allokering av 64 MB buffert:");
char * buffert = (char *) malloc (BUFFER_SIZE);
printf ("KLAR \ n");
 
// Skriv bufferten till fOut
printf ("Skriva data till första buffert:");
fOut = fopen (BUFFER_1, "wb");
fwrite (buffert, storlek på (char), BUFFER_SIZE, fOut);
fclose (fOut);
printf ("KLAR \ n");
 
printf ("Kopiera data från första filen till andra:");
fIn = fopen (BUFFER_1, "rb");
fOut = fopen (BUFFER_2, "wb");
fread (buffer, sizeof (char), BUFFER_SIZE, fIn);
fwrite (buffert, storlek på (char), BUFFER_SIZE, fOut);
fclose (fIn);
fclose (fOut);
printf ("KLAR \ n");
 
printf ("Freeing buffer:");
fri (buffert);
printf ("KLAR \ n");
 
printf ("Radera filer:");
ta bort (BUFFER_1);
ta bort (BUFFER_2);
printf ("KLAR \ n");
 
returnera 0;
 

test2.c (systemanrop)

#omfatta
#omfatta
#omfatta
#omfatta
#omfatta
#omfatta
#omfatta
#omfatta
#omfatta
 
#define BUFFER_SIZE 67108864
 
int main ()
 
int fOut, fIn;
 
printf ("\ nI / O-test med sendfile () och relaterade systemanrop.\ n \ n ");
 
// Ta en BUFFER_SIZE-buffert.
// Bufferten kommer att ha slumpmässiga data i den men vi bryr oss inte om det.
printf ("Allokering av 64 MB buffert:");
char * buffert = (char *) malloc (BUFFER_SIZE);
printf ("KLAR \ n");
 
// Skriv bufferten till fOut
printf ("Skriva data till första buffert:");
fOut = öppen ("buffert1", O_RDONLY);
skriv (fOut, & buffert, BUFFER_SIZE);
stäng (fOut);
printf ("KLAR \ n");
 
printf ("Kopiera data från första filen till andra:");
fIn = öppen ("buffert1", O_RDONLY);
fOut = öppen ("buffert2", O_RDONLY);
sendfile (fOut, fIn, 0, BUFFER_SIZE);
stäng (fIn);
stäng (fOut);
printf ("KLAR \ n");
 
printf ("Freeing buffer:");
fri (buffert);
printf ("KLAR \ n");
 
printf ("Radera filer:");
ta bort länk ("buffert1");
ta bort länk ("buffert2");
printf ("KLAR \ n");
 
returnera 0;
 

Kompilera och köra tester 1 & 2

För att bygga dessa exempel behöver du utvecklingsverktygen installerade på din distribution. På Debian och Ubuntu kan du installera detta med:

apt install build-essentials

Kompilera sedan med:

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

För att köra båda och testa prestanda, kör:

tid ./ test1 && tid ./ test2

Du borde få resultat så här:

I / O-test med traditionella glibc-funktioner.

Tilldelning av 64 MB buffert: Klar
Skriva data till första bufferten: Klar
Kopiera data från första filen till den andra: Klar
Befriande buffert: Klar
Radera filer: Klar
verklig 0m0.397s
användare 0m0.000-talet
sys 0m0.203-talet
I / O-test med sendfile () och relaterade systemanrop.
Tilldelning av 64 MB buffert: Klar
Skriva data till första bufferten: Klar
Kopiera data från första filen till den andra: Klar
Befriande buffert: Klar
Radera filer: Klar
verklig 0m0.019s
användare 0m0.000-talet
sys 0m0.016s

Som du ser går koden som använder systemanropen mycket snabbare än glibc-motsvarigheten.

Saker att komma ihåg

Systemanrop kan öka prestanda och ge ytterligare funktionalitet, men de är inte utan sina nackdelar. Du måste väga fördelarna som systemanrop ger mot bristen på plattformsportabilitet och ibland minskad funktionalitet jämfört med biblioteksfunktioner.

När du använder vissa systemanrop måste du vara noga med att använda resurser som returneras från systemanrop snarare än biblioteksfunktioner. Till exempel är FILE-strukturen som används för glibc's fopen (), fread (), fwrite () och fclose () inte samma som filbeskrivningsnumret från det öppna () systemanropet (returneras som ett heltal). Att blanda dessa kan leda till problem.

I allmänhet har Linux-systemanrop färre stötfångarbanor än glibc-funktioner. Även om det är sant att systemanrop har felhantering och rapportering, får du mer detaljerad funktionalitet från en glibc-funktion.

Och slutligen ett ord om säkerhet. System anropar direkt gränssnitt med kärnan. Linux-kärnan har omfattande skydd mot shenanigans från användarland, men oupptäckta buggar finns. Lita inte på att ett systemanrop kommer att validera din inmatning eller isolera dig från säkerhetsproblem. Det är klokt att se till att data du lämnar till ett systemanrop saneras. Naturligtvis är detta ett bra råd för alla API-samtal, men du kan inte vara så försiktig när du arbetar med kärnan.

Jag hoppas att du gillade det här djupare dyket i landet med Linux-systemanrop. För en fullständig lista över Linux-systemanrop, se vår huvudlista.

Portar med öppen källkod för kommersiella spelmotorer
Gratis, öppen källkod och plattformsmekaniska rekreationer kan användas för att spela gamla såväl som några av de ganska senaste speltitlarna. I den h...
Bästa kommandoradsspel för Linux
Kommandoraden är inte bara din största allierade när du använder Linux, det kan också vara källan till underhållning eftersom du kan använda den för a...
Bästa Gamepad Mapping Apps för Linux
Om du gillar att spela spel på Linux med en gamepad istället för ett typiskt tangentbord och musinmatningssystem, finns det några användbara appar för...