C programozás

Linux rendszerhívás oktatóanyag C-vel

Linux rendszerhívás oktatóanyag C-vel
A Linux rendszerhívásokról szóló legutóbbi cikkünkben meghatároztam egy rendszerhívást, megvitattam azokat az okokat, amelyek miatt a programban felhasználhatjuk őket, és elmélyültem azok előnyeiben és hátrányaiban. Még rövid példát is közöltem a C-n belüli összeszerelés során. Ez szemléltette a lényeget és leírta a hívás kezdeményezését, de semmi eredményeset nem tett. Nem éppen egy izgalmas fejlesztési gyakorlat, de szemléltette a lényeget.

Ebben a cikkben a tényleges rendszerhívásokat fogjuk használni, hogy valós munkát végezzünk a C programunkban. Először megvizsgáljuk, ha rendszerszintű hívást kell használnia, majd adjon egy példát a sendfile () hívással, amely drámai módon javíthatja a fájlmásolás teljesítményét. Végül áttekintünk néhány szempontot, amelyet emlékeznünk kell a Linux rendszerhívások használata közben.

Szüksége van egy rendszerhívásra?

Bár elkerülhetetlen, hogy a C fejlesztői karrierje során egy rendszerhívást használjon, hacsak nem a nagy teljesítményre vagy egy adott típusú funkcionalitásra irányul, a glibc könyvtár és más, a főbb Linux disztribúciókba tartozó egyéb alapkönyvtárak gondoskodnak a legtöbb a szükségleteid.

A glibc szabványkönyvtár platformokon átívelő, jól tesztelt keretrendszert biztosít olyan funkciók végrehajtásához, amelyek egyébként rendszerspecifikus rendszerhívásokat igényelnének. Például elolvashat egy fájlt az fscanf (), fread (), getc () stb., vagy használhatja a read () Linux rendszerhívást. A glibc függvények több funkciót nyújtanak (azaz.e. jobb hibakezelés, formázott IO stb.), és működni fog a rendszer bármely glibc-támogatásán.

Másrészt vannak esetek, amikor a kompromisszumok nélküli teljesítmény és a pontos végrehajtás kritikus fontosságú. A fread () által biztosított burkolat növelni fogja a rezsit, és bár kisebb, mégsem teljesen átlátszó. Ezenkívül előfordulhat, hogy nem szeretné, vagy szüksége lenne a burkoló által biztosított extra szolgáltatásokra. Ebben az esetben a rendszerhívás szolgál a legjobban.

Rendszerhívásokat is használhat olyan funkciók végrehajtására, amelyeket a glibc még nem támogat. Ha a glibc példánya naprakész, akkor ez aligha lesz probléma, de a régebbi disztribúciókkal történő fejlesztésnél újabb kernelekkel lehet szükség erre a technikára.

Most, hogy elolvasta a korlátozásokat, figyelmeztetéseket és lehetséges kitérőket, most ássunk be néhány gyakorlati példát.

Milyen CPU-n vagyunk?

Olyan kérdés, amelyet valószínűleg a legtöbb program nem gondol, de mégis érvényes. Ez egy példa egy olyan rendszerhívásra, amelyet nem lehet megismételni a glibc-vel, és amelyet nem takar egy glibc-csomagoló. Ebben a kódban a getcpu () hívást közvetlenül a syscall () függvényen keresztül hívjuk meg. A syscall funkció a következőképpen működik:

syscall (SYS_call, arg1, arg2,…);

Az első argumentum, a SYS_call, egy definíció, amely a rendszerhívás számát jelöli. Amikor felveszi a sys / syscall szót.h, ezeket tartalmazza. Az első rész SYS_, a második rész a rendszerhívás neve.

A hívás argumentumai a fenti arg1, arg2 pontokra mennek. Egyes hívásokhoz több érv szükséges, és a sorrendben folytatódnak a man oldalukon. Ne feledje, hogy a legtöbb argumentumhoz, különösen a visszatérésekhez, a tömb tömbökre vagy a malloc függvény által lefoglalt memóriára lesz szükség.

példa1.c

#include
#include
#include
#include
 
int main ()
 
aláíratlan cpu, csomópont;
 
// A jelenlegi CPU mag és a NUMA csomópont lekérése rendszerhívás útján
// Megjegyezzük, hogy ennek nincs glibc burkolója, ezért közvetlenül hívnunk kell
syscall (SYS_getcpu, & cpu, & node, NULL);
 
// Információk megjelenítése
printf ("Ez a program a% u CPU magon és a NUMA csomóponton fut% u.\ n \ n ", cpu, csomópont);
 
visszatér 0;
 

 
Összeállítás és futtatás:
 
gcc példa1.c -o példa1
./ példa1

Érdekesebb eredmények érdekében szálakat pörgethet a pthreads könyvtáron keresztül, majd felhívhatja ezt a funkciót, hogy megnézze, melyik processzoron fut a szál.

Sendfile: Kiváló teljesítmény

A Sendfile kiváló példát nyújt a teljesítmény növelésére a rendszerhívások révén. A sendfile () függvény átmásolja az adatokat az egyik fájlleíróból a másikba. A sendfile több fread () és fwrite () függvény használata helyett a kerneltérben hajtja végre az átvitelt, csökkentve a rezsit és ezáltal növelve a teljesítményt.

Ebben a példában 64 MB adatot másolunk egyik fájlból a másikba. Egy teszt során a szabványos olvasási / írási módszereket fogjuk használni a standard könyvtárban. A másikban a rendszerhívásokat és a sendfile () hívást fogjuk felhasználni az adatok egyik helyről a másikra történő robbantására.

teszt1.c (glibc)

#include
#include
#include
#include
 
#define BUFFER_SIZE 67108864
#define BUFFER_1 "buffer1"
#define BUFFER_2 "buffer2"
 
int main ()
 
FÁJL * fOut, * fIn;
 
printf ("\ nI / O teszt hagyományos glibc függvényekkel.\ n \ n ");
 
// Fogj egy BUFFER_SIZE puffert.
// A pufferben véletlenszerű adatok lesznek, de ez nem érdekel.
printf ("64 MB puffer kiosztása:");
char * puffer = (char *) malloc (BUFFER_SIZE);
printf ("KÉSZ \ n");
 
// Írja a puffert az fOut-ba
printf ("Adatok írása az első pufferbe:");
fOut = fopen (BUFFER_1, "wb");
fwrite (puffer, sizeof (char), BUFFER_SIZE, fOut);
fclose (fOut);
printf ("KÉSZ \ n");
 
printf ("Adatok másolása az első fájlból a másodikba:");
fIn = fopen (BUFFER_1, "rb");
fOut = fopen (BUFFER_2, "wb");
fread (puffer, sizeof (char), BUFFER_SIZE, fIn);
fwrite (puffer, sizeof (char), BUFFER_SIZE, fOut);
fclose (fIn);
fclose (fOut);
printf ("KÉSZ \ n");
 
printf ("Szabadító puffer:");
szabad (puffer);
printf ("KÉSZ \ n");
 
printf ("Fájlok törlése:");
eltávolítás (BUFFER_1);
eltávolítás (BUFFER_2);
printf ("KÉSZ \ n");
 
visszatér 0;
 

teszt2.c (rendszerhívások)

#include
#include
#include
#include
#include
#include
#include
#include
#include
 
#define BUFFER_SIZE 67108864
 
int main ()
 
int fOut, fIn;
 
printf ("\ nI / O teszt sendfile () és a kapcsolódó rendszerhívásokkal.\ n \ n ");
 
// Fogj egy BUFFER_SIZE puffert.
// A pufferben véletlenszerű adatok lesznek, de ez nem érdekel.
printf ("64 MB puffer kiosztása:");
char * puffer = (char *) malloc (BUFFER_SIZE);
printf ("KÉSZ \ n");
 
// Írja a puffert az fOut-ba
printf ("Adatok írása az első pufferbe:");
fOut = nyitott ("buffer1", O_RDONLY);
write (fOut, & buffer, BUFFER_SIZE);
bezár (fOut);
printf ("KÉSZ \ n");
 
printf ("Adatok másolása az első fájlból a másodikba:");
fIn = nyitott ("buffer1", O_RDONLY);
fOut = nyitott ("buffer2", O_RDONLY);
sendfile (fOut, fIn, 0, BUFFER_SIZE);
bezár (fIn);
bezár (fOut);
printf ("KÉSZ \ n");
 
printf ("Szabadító puffer:");
szabad (puffer);
printf ("KÉSZ \ n");
 
printf ("Fájlok törlése:");
leválasztás ("buffer1");
leválasztás ("buffer2");
printf ("KÉSZ \ n");
 
visszatér 0;
 

1. és 2. teszt összeállítása és futtatása

Ezeknek a példáknak az elkészítéséhez telepítenie kell a disztribúcióra fejlesztő eszközöket. A Debianon és az Ubuntuban ezt telepítheti:

apt install build-essentials

Ezután állítsa össze:

gcc teszt1.c -o teszt1 && gcc teszt2.c -o teszt2

Mindkettő futtatásához és a teljesítmény teszteléséhez futtassa:

idő ./ test1 && time ./ teszt2

Ilyen eredményeket kell elérnie:

I / O teszt hagyományos glibc funkciókkal.

64 MB puffer kiosztása: DONE
Adatok írása az első pufferbe: DONE
Adatok másolása az első fájlból a másodikba: DONE
Szabadító puffer: KÉSZ
Fájlok törlése: DONE
valós 0m0.397-es évek
felhasználó 0m0.000-es évek
sys 0m0.203-as évek
I / O teszt sendfile () és a kapcsolódó rendszerhívásokkal.
64 MB puffer kiosztása: DONE
Adatok írása az első pufferbe: DONE
Adatok másolása az első fájlból a másodikba: DONE
Szabadító puffer: KÉSZ
Fájlok törlése: DONE
valós 0m0.019-es évek
felhasználó 0m0.000-es évek
sys 0m0.016-os évek

Mint látható, a rendszerhívásokat használó kód sokkal gyorsabban fut, mint a glibc megfelelője.

Dolgok, amikre emlékezni kell

A rendszerhívások növelhetik a teljesítményt és további funkcionalitást nyújthatnak, de nem hátrányosak. Mérlegelnie kell a rendszerhívások előnyeit a platform hordozhatóságának hiányával és a könyvtári funkciókhoz képest néha csökkent funkcionalitással.

Egyes rendszerhívások használatakor ügyelnie kell arra, hogy a könyvtárhívások helyett a rendszerhívásokból visszakapott erőforrásokat használja. Például a FILE struktúra, amelyet a glibc fopen (), fread (), fwrite () és fclose () függvényei használnak, nem azonos a open () rendszerhívás fájlleíró számával (egész számként adják meg). Ezek keverése problémákhoz vezethet.

Általánosságban elmondható, hogy a Linux rendszerhívások kevesebb lökhárítóval rendelkeznek, mint a glibc funkciók. Bár igaz, hogy a rendszerhívások tartalmaznak hibakezelést és jelentéseket, a glibc funkcióval részletesebb funkciókat kap.

És végül egy szó a biztonságról. A rendszerhívások közvetlenül kapcsolódnak a kernellel. A Linux kernel kiterjedt védelemmel rendelkezik a felhasználói földterületről érkező shenanigans ellen, de felfedezetlen hibák vannak. Ne bízzon abban, hogy egy rendszerhívás érvényesíti a bemenetet, vagy elkülöníti a biztonsági problémáktól. Bölcs dolog annak biztosítása, hogy a rendszerhívásnak átadott adatokat megtisztítsák. Természetesen ez jó tanács bármilyen API-híváshoz, de nem lehet óvatos, amikor a kernellel dolgozik.

Remélem, élvezte ezt a mélyebb merülést a Linux rendszerhívások földjén. A Linux rendszerhívások teljes listáját lásd mesterlistánkban.

Az egér mozgásának vezérlése és kezelése több monitor között a Windows 10 rendszerben
Dual Display Mouse Manager lehetővé teszi az egér mozgásának vezérlését és konfigurálását több monitor között, lassítva annak mozgását a határ közeléb...
A WinMouse segítségével testre szabhatja és javíthatja az egérmutató mozgását a Windows PC-n
Ha szeretné javítani az egérmutató alapértelmezett funkcióit, használjon ingyenes programokat WinMouse. További funkciókat kínál, amelyek segítenek ab...
Az egér bal oldali gombja nem működik a Windows 10 rendszeren
Ha dedikált egeret használ laptopjával vagy asztali számítógépével, de a az egér bal gombja nem működik a Windows 10/8/7 rendszeren valamilyen oknál f...