Tutorial de trucades del sistema Linux amb C

Linux System Call Tutorial With C



En el nostre últim article sobre Trucades al sistema Linux , Vaig definir una trucada al sistema, vaig discutir els motius pels quals es podria utilitzar en un programa i vaig aprofundir en els seus avantatges i desavantatges. Fins i tot vaig donar un breu exemple en assemblea dins de C. Va il·lustrar el punt i va descriure com fer la trucada, però no va fer res productiu. No és precisament un exercici de desenvolupament emocionant, però il·lustra el punt.

En aquest article, farem servir trucades del sistema reals per fer feina real al nostre programa C. En primer lloc, revisarem si heu d’utilitzar una trucada del sistema i, a continuació, proporcionarem un exemple amb la trucada sendfile () que pot millorar dramàticament el rendiment de la còpia de fitxers. Finalment, revisarem alguns punts que cal recordar mentre s’utilitzen trucades al sistema Linux.







Tot i que és inevitable, feu servir una trucada al sistema en algun moment de la vostra carrera de desenvolupament de C, tret que estigueu orientat a un rendiment elevat o a una funcionalitat de tipus particular, la biblioteca glibc i altres biblioteques bàsiques incloses en les distribucions principals de Linux s’encarregaran de la majoria de les seves necessitats.



La biblioteca estàndard de glibc proporciona un marc multiplataforma ben provat per executar funcions que d’altra manera requeririen trucades de sistema específiques del sistema. Per exemple, podeu llegir un fitxer amb fscanf (), fread (), getc (), etc., o podeu utilitzar la trucada del sistema Linux read (). Les funcions de glibc proporcionen més funcions (és a dir, millor maneig d’errors, E / S formatat, etc.) i funcionaran en qualsevol sistema compatible amb glibc.



D'altra banda, hi ha moments en què el rendiment intransigent i l'execució exacta són fonamentals. L’embolcall que proporciona fread () afegirà despeses generals i, tot i que menor, no és del tot transparent. A més, és possible que no vulgueu ni necessiteu les funcions addicionals que proporciona l'embolcall. En aquest cas, és millor que us publiqueu amb una trucada al sistema.





També podeu utilitzar trucades del sistema per realitzar funcions que encara no són compatibles amb glibc. Si la vostra còpia de glibc està actualitzada, difícilment serà un problema, però és possible que necessiteu aquesta tècnica per desenvolupar distribucions anteriors amb nuclis més nous.

Ara que heu llegit les advertències i advertències i possibles desviaments, ara aprofundim en alguns exemples pràctics.



A quina CPU estem?

Una pregunta que probablement la majoria dels programes no pensen fer, però, no obstant això, és vàlida. Aquest és un exemple de trucada del sistema que no es pot duplicar amb glibc i que no es cobreix amb un embolcall de glibc. En aquest codi, trucarem directament a la trucada getcpu () mitjançant la funció syscall (). La funció syscall funciona de la següent manera:

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

El primer argument, SYS_call, és una definició que representa el número de la trucada del sistema. Quan s'inclou sys / syscall.h, s'inclouen. La primera part és SYS_ i la segona part és el nom de la trucada del sistema.

Els arguments per a la trucada entren a arg1, arg2 més amunt. Algunes trucades requereixen més arguments i continuaran en ordre des de la pàgina de manual. Recordeu que la majoria d'arguments, especialment per a retorns, requeriran indicadors per carregar matrius o memòria assignada mitjançant la funció malloc.

exemple1.c

#incloure
#incloure
#incloure
#incloure

intprincipal() {

sense signarCPU,node;

// Obteniu el nucli actual de la CPU i el node NUMA mitjançant una trucada al sistema
// Tingueu en compte que no té cap embolcall glibc, per tant, hem de trucar-lo directament
syscall(SYS_getcpu, &CPU, &node,NUL);

// Mostra informació
printf ('Aquest programa s'executa al nucli de la CPU% u i al node NUMA% u. n n',CPU,node);

tornar 0;

}

Per compilar i executar:

gcc exemple1.c -o exemple1
./exemple1

Per obtenir resultats més interessants, podeu fer girar fils a través de la biblioteca pthreads i després trucar a aquesta funció per veure en quin processador s’executa el vostre fil.

Sendfile: rendiment superior

Sendfile proporciona un exemple excel·lent de millora del rendiment mitjançant trucades al sistema. La funció sendfile () copia les dades d'un descriptor de fitxers a un altre. En lloc d’utilitzar múltiples funcions fread () i fwrite (), sendfile realitza la transferència a l’espai del nucli, reduint la sobrecàrrega i augmentant així el rendiment.

En aquest exemple, copiarem 64 MB de dades d’un fitxer a un altre. En una prova, utilitzarem els mètodes estàndard de lectura / escriptura a la biblioteca estàndard. A l’altra, utilitzarem les trucades del sistema i la trucada sendfile () per fer passar aquestes dades d’una ubicació a una altra.

test1.c (glibc)

#incloure
#incloure
#incloure
#incloure

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

intprincipal() {

DOSSIER*mal, *fi;

printf (' nProva d'E / S amb funcions glibc tradicionals. n n');

// Agafeu un buffer BUFFER_SIZE.
// El buffer tindrà dades aleatòries, però això no ens importa.
printf ('Assignació de memòria intermèdia de 64 MB:');
char *memòria intermèdia= (char *) malloc (BUFFER_SIZE);
printf (FET n');

// Escriviu el buffer a fOut
printf ('Escriptura de dades al primer buffer:');
mal= obrir (BUFFER_1, 'wb');
fwrite (memòria intermèdia, mida de(char),BUFFER_SIZE,mal);
tancar (mal);
printf (FET n');

printf ('Copiant dades del primer fitxer al segon:');
fi= obrir (BUFFER_1, 'rb');
mal= obrir (BUFFER_2, 'wb');
fread (memòria intermèdia, mida de(char),BUFFER_SIZE,fi);
fwrite (memòria intermèdia, mida de(char),BUFFER_SIZE,mal);
tancar (fi);
tancar (mal);
printf (FET n');

printf ('Alliberament de memòria intermèdia:');
gratuït (memòria intermèdia);
printf (FET n');

printf ('Suprimint fitxers:');
eliminar (BUFFER_1);
eliminar (BUFFER_2);
printf (FET n');

tornar 0;

}

test2.c (trucades al sistema)

#incloure
#incloure
#incloure
#incloure
#incloure
#incloure
#incloure
#incloure
#incloure

#define BUFFER_SIZE 67108864

intprincipal() {

intmal,fi;

printf (' nProva d'E / S amb sendfile () i trucades de sistema relacionades. n n');

// Agafeu un buffer BUFFER_SIZE.
// El buffer tindrà dades aleatòries, però això no ens importa.
printf ('Assignació de memòria intermèdia de 64 MB:');
char *memòria intermèdia= (char *) malloc (BUFFER_SIZE);
printf (FET n');


// Escriviu el buffer a fOut
printf ('Escriptura de dades al primer buffer:');
mal=obert('buffer1',O_RDONLY);
escriure(mal, &memòria intermèdia,BUFFER_SIZE);
Tanca(mal);
printf (FET n');

printf ('Copiant dades del primer fitxer al segon:');
fi=obert('buffer1',O_RDONLY);
mal=obert('buffer2',O_RDONLY);
enviar fitxer(mal,fi, 0,BUFFER_SIZE);
Tanca(fi);
Tanca(mal);
printf (FET n');

printf ('Alliberament de memòria intermèdia:');
gratuït (memòria intermèdia);
printf (FET n');

printf ('Suprimint fitxers:');
desenllaçar('buffer1');
desenllaçar('buffer2');
printf (FET n');

tornar 0;

}

Compilació i execució de proves 1 i 2

Per crear aquests exemples, necessitareu les eines de desenvolupament instal·lades a la vostra distribució. A Debian i Ubuntu, podeu instal·lar-ho amb:

apteinstal·laressencial de la construcció

A continuació, compileu amb:

gccprova1.c-otest1&& gcctest2.c-oprova2

Per executar tots dos i provar el rendiment, executeu:

temps./test1&& temps./prova2

Hauríeu d'obtenir resultats com aquest:

Prova d'E / S amb funcions glibc tradicionals.

Assignació de memòria intermèdia de 64 MB: FET
Escriptura de dades al primer buffer: FET
Copiant dades del primer fitxer al segon: FET
Alliberament de memòria intermèdia: FET
Supressió de fitxers: FET
real 0m0.397s
usuari 0m0.000s
sys 0m0.203s
Prova d'E / S amb sendfile () i trucades de sistema relacionades.
Assignació de memòria intermèdia de 64 MB: FET
Escriptura de dades al primer buffer: FET
Copiant dades del primer fitxer al segon: FET
Alliberament de memòria intermèdia: FET
Supressió de fitxers: FET
0m0.019s reals
usuari 0m0.000s
sys 0m0.016s

Com podeu veure, el codi que utilitza les trucades del sistema s’executa molt més ràpid que l’equivalent a glibc.

Coses que cal recordar

Les trucades del sistema poden augmentar el rendiment i proporcionar funcionalitats addicionals, però no estan exemptes de desavantatges. Haureu de ponderar els avantatges que proporcionen les trucades del sistema davant la manca de portabilitat de la plataforma i, de vegades, la funcionalitat reduïda en comparació amb les funcions de la biblioteca.

Quan utilitzeu algunes trucades del sistema, heu de tenir cura d’utilitzar els recursos retornats de les trucades del sistema en lloc de les funcions de biblioteca. Per exemple, l’estructura FILE que s’utilitza per a les funcions fopen (), fread (), fwrite () i fclose () de glibc no és el mateix que el número del descriptor de fitxers de la trucada del sistema open () (retornat com a enter). Barrejar-los pot provocar problemes.

En general, les trucades al sistema Linux tenen menys carrils bumper que les funcions glibc. Tot i que és cert que les trucades del sistema tenen un tractament i informes d’errors, obtindreu una funcionalitat més detallada amb una funció glibc.

I, finalment, una paraula sobre seguretat. Les trucades del sistema interactuen directament amb el nucli. El nucli de Linux té proteccions extenses contra els shenanigans de la terra de l'usuari, però existeixen errors no descoberts. No confieu que una trucada del sistema validi les vostres aportacions o us aïllarà de problemes de seguretat. És convenient assegurar-se que les dades que lliureu a una trucada del sistema estiguin higienitzades. Per descomptat, aquest és un bon consell per a qualsevol trucada a l'API, però no es pot tenir cura quan es treballa amb el nucli.

Espero que us hagi agradat aquesta immersió més profunda al país de les trucades al sistema Linux. Per obtenir una llista completa de trucades al sistema Linux, consulteu la nostra llista principal.