Linux sistēmas zvanu apmācība ar C

Linux System Call Tutorial With C



Mūsu pēdējā rakstā par Linux sistēmas zvani , Es definēju sistēmas zvanu, apspriedu iemeslus, kādēļ tos varētu izmantot programmā, un iedziļinājos to priekšrocībās un trūkumos. Es pat minēju īsu piemēru montāžā C daļā. Tas ilustrēja domu un aprakstīja, kā zvanīt, bet nedeva neko produktīvu. Nav īsti aizraujošs attīstības uzdevums, taču tas ilustrēja būtību.

Šajā rakstā mēs izmantosim faktiskos sistēmas zvanus, lai veiktu reālu darbu mūsu C programmā. Pirmkārt, mēs pārskatīsim, vai jums ir jāizmanto sistēmas zvans, un pēc tam sniedzam piemēru, izmantojot zvanu sendfile (), kas var ievērojami uzlabot failu kopēšanas veiktspēju. Visbeidzot, mēs apskatīsim dažus punktus, kas jāatceras, izmantojot Linux sistēmas zvanus.







Lai gan tas ir neizbēgami, jūs kādā C attīstības karjeras brīdī izmantosit sistēmas izsaukumu, ja vien jūsu mērķauditorija nav augsta veiktspēja vai noteikta veida funkcionalitāte, glibc bibliotēka un citas pamata bibliotēkas, kas iekļautas galvenajos Linux izplatījumos, rūpēsies par lielāko daļu jūsu vajadzībām.



Glibc standarta bibliotēka nodrošina daudzplatformu, labi pārbaudītu sistēmu, lai veiktu funkcijas, kurām citādi būtu nepieciešami sistēmas specifiski sistēmas izsaukumi. Piemēram, varat izlasīt failu ar fscanf (), fread (), getc () utt., Vai arī izmantot read () Linux sistēmas izsaukumu. Glibc funkcijas nodrošina vairāk funkciju (t.i., labāku kļūdu apstrādi, formatētu IO utt.), Un tās darbosies ar visiem sistēmas glibc balstiem.



No otras puses, ir reizes, kad bezkompromisa veiktspēja un precīza izpilde ir kritiska. Iesaiņojums, ko nodrošina fread (), pievienos virs galvas, un, lai arī neliels, tas nav pilnīgi caurspīdīgs. Turklāt, iespējams, nevēlaties vai jums nav nepieciešamas papildu funkcijas, ko nodrošina iesaiņojums. Tādā gadījumā jūs vislabāk apkalpo sistēmas zvans.





Varat arī izmantot sistēmas izsaukumus, lai veiktu funkcijas, kuras vēl neatbalsta glibc. Ja jūsu glibc kopija ir atjaunināta, tas diez vai būs problēma, taču, izstrādājot vecākus izplatījumus ar jaunākiem kodoliem, šī metode var būt nepieciešama.

Tagad, kad esat izlasījis atrunas, brīdinājumus un iespējamos apvedceļus, tagad izpētīsim dažus praktiskus piemērus.



Kāds CPU mums ir?

Jautājums, ko lielākā daļa programmu, iespējams, nedomā uzdot, bet tomēr pamatots. Šis ir sistēmas izsaukuma piemērs, kuru nevar dublēt ar glibc un kas nav pārklāts ar glibc iesaiņojumu. Šajā kodā mēs zvanīsim tieši getcpu () zvanam, izmantojot funkciju syscall (). Sistēmas zvanīšanas funkcija darbojas šādi:

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

Pirmais arguments SYS_call ir definīcija, kas apzīmē sistēmas izsaukuma numuru. Iekļaujot sys/syscall.h, tie ir iekļauti. Pirmā daļa ir SYS_, bet otrā daļa ir sistēmas izsaukuma nosaukums.

Zvana argumenti ir minēti augstāk arg1, arg2. Dažiem zvaniem ir nepieciešami vairāk argumentu, un tie tiks turpināti secībā no manas lapas. Atcerieties, ka lielākajai daļai argumentu, īpaši attiecībā uz atgriešanos, būs nepieciešami rādītāji, lai ierakstītu masīvus vai atmiņu, kas piešķirta, izmantojot malloc funkciju.

piemērs1.c

#iekļaut
#iekļaut
#iekļaut
#iekļaut

intgalvenais() {

neparakstītsProcesors,mezgls;

// Iegūstiet pašreizējo CPU kodolu un NUMA mezglu, izmantojot sistēmas zvanu
// Ņemiet vērā, ka tam nav glibc iesaiņojuma, tāpēc mums tas ir jāsauc tieši
syscall(SYS_getcpu, &Procesors, &mezgls,NULL);

// Parādīt informāciju
printf ('Šī programma darbojas ar CPU kodolu %u un NUMA mezglu %u. n n',Procesors,mezgls);

atgriezties 0;

}

Lai apkopotu un palaistu:

gcc piemērs1.c -o piemērs 1
./piemērs1

Lai iegūtu interesantākus rezultātus, varat griezt pavedienus, izmantojot pthreads bibliotēku, un pēc tam izsaukt šo funkciju, lai redzētu, kurā procesorā darbojas jūsu pavediens.

Sūtīšanas fails: izcila veiktspēja

Sendfile ir lielisks piemērs veiktspējas uzlabošanai, izmantojot sistēmas zvanus. Funkcija sendfile () kopē datus no viena faila deskriptora uz citu. Tā vietā, lai izmantotu vairākas fread () un fwrite () funkcijas, sendfile veic pārsūtīšanu kodola telpā, samazinot pieskaitāmās izmaksas un tādējādi palielinot veiktspēju.

Šajā piemērā mēs kopēsim 64 MB datu no viena faila uz citu. Vienā testā mēs izmantosim standarta lasīšanas/rakstīšanas metodes standarta bibliotēkā. Otrā gadījumā mēs izmantosim sistēmas zvanus un sendfile () zvanu, lai pārsūtītu šos datus no vienas vietas uz citu.

test1.c (glibc)

#iekļaut
#iekļaut
#iekļaut
#iekļaut

#define BUFFER_SIZE 67108864
#define BUFFER_1 'buferis1'
#define BUFFER_2 'buferis2'

intgalvenais() {

FILE*nepareizi, *beigas;

printf (' nI/O tests ar tradicionālajām glibc funkcijām. n n');

// Paņemiet BUFFER_SIZE buferi.
// Buferī būs nejauši dati, bet mums tas nerūp.
printf ('64 MB bufera piešķiršana:');
char *buferšķīdums= (char *) malloc (BUFFER_SIZE);
printf ('Gatavs n');

// Ierakstiet buferi fOut
printf ('Datu ierakstīšana pirmajā buferī:');
nepareizi= fopen (BUFFER_1, 'wb');
rakstīt (buferšķīdums, izmērs(char),BUFFER_SIZE,nepareizi);
fclose (nepareizi);
printf ('Gatavs n');

printf ('Datu kopēšana no pirmā faila uz otro:');
beigas= fopen (BUFFER_1, 'rb');
nepareizi= fopen (BUFFER_2, 'wb');
fread (buferšķīdums, izmērs(char),BUFFER_SIZE,beigas);
rakstīt (buferšķīdums, izmērs(char),BUFFER_SIZE,nepareizi);
fclose (beigas);
fclose (nepareizi);
printf ('Gatavs n');

printf ('Bufera atbrīvošana:');
bezmaksas (buferšķīdums);
printf ('Gatavs n');

printf ('Failu dzēšana:');
noņemt (BUFFER_1);
noņemt (BUFFER_2);
printf ('Gatavs n');

atgriezties 0;

}

test2.c (sistēmas izsaukumi)

#iekļaut
#iekļaut
#iekļaut
#iekļaut
#iekļaut
#iekļaut
#iekļaut
#iekļaut
#iekļaut

#define BUFFER_SIZE 67108864

intgalvenais() {

intnepareizi,beigas;

printf (' nI/O tests ar sendfile () un saistītie sistēmas zvani. n n');

// Paņemiet BUFFER_SIZE buferi.
// Buferī būs nejauši dati, bet mums tas nerūp.
printf ('64 MB bufera piešķiršana:');
char *buferšķīdums= (char *) malloc (BUFFER_SIZE);
printf ('Gatavs n');


// Ierakstiet buferi fOut
printf ('Datu ierakstīšana pirmajā buferī:');
nepareizi=atvērts('buferis1',O_RDONLY);
rakstīt(nepareizi, &buferšķīdums,BUFFER_SIZE);
aizvērt(nepareizi);
printf ('Gatavs n');

printf ('Datu kopēšana no pirmā faila uz otro:');
beigas=atvērts('buferis1',O_RDONLY);
nepareizi=atvērts('buferis2',O_RDONLY);
sendfile(nepareizi,beigas, 0,BUFFER_SIZE);
aizvērt(beigas);
aizvērt(nepareizi);
printf ('Gatavs n');

printf ('Bufera atbrīvošana:');
bezmaksas (buferšķīdums);
printf ('Gatavs n');

printf ('Failu dzēšana:');
atsaistīt('buferis1');
atsaistīt('buferis2');
printf ('Gatavs n');

atgriezties 0;

}

1. un 2. testa apkopošana un izpilde

Lai izveidotu šos piemērus, jums būs nepieciešami jūsu izplatīšanā instalētie attīstības rīki. Debian un Ubuntu varat to instalēt, izmantojot:

trāpīgsuzstādītbūvēšanai nepieciešamās lietas

Pēc tam apkopojiet ar:

gcctests1.c-vaitests1&& gcctests2.c-vaitests2

Lai palaistu abus un pārbaudītu veiktspēju, palaidiet:

laiks./tests1&& laiks./tests2

Jums vajadzētu iegūt šādus rezultātus:

I/O tests ar tradicionālajām glibc funkcijām.

64 MB bufera piešķiršana: GATAVS
Datu ierakstīšana pirmajā buferī: GATAVS
Datu kopēšana no pirmā faila uz otro: GATAVS
Atbrīvošanas buferis: GATAVS
Failu dzēšana: GATAVS
īsts 0m0.397s
lietotājs 0m0.000s
sys 0m0,203s
I/O tests ar sendfile () un saistītie sistēmas zvani.
64 MB bufera piešķiršana: GATAVS
Datu ierakstīšana pirmajā buferī: GATAVS
Datu kopēšana no pirmā faila uz otro: GATAVS
Atbrīvošanas buferis: GATAVS
Failu dzēšana: GATAVS
īsts 0m0.019s
lietotājs 0m0.000s
sys 0m0.016s

Kā redzat, kods, kas izmanto sistēmas zvanus, darbojas daudz ātrāk nekā glibc ekvivalents.

Lietas, kas jāatceras

Sistēmas zvani var palielināt veiktspēju un nodrošināt papildu funkcionalitāti, taču tie nav bez trūkumiem. Jums būs jāizvērtē sistēmas zvanu sniegtās priekšrocības, salīdzinot ar platformas pārnesamības trūkumu un dažreiz samazinātu funkcionalitāti, salīdzinot ar bibliotēkas funkcijām.

Izmantojot dažus sistēmas izsaukumus, jums jārūpējas, lai tiktu izmantoti resursi, kas atgriezti no sistēmas zvaniem, nevis bibliotēkas funkcijas. Piemēram, FILE struktūra, ko izmanto funkcijām glibc fopen (), fread (), fwrite () un fclose (), nav tas pats, kas faila apraksta numurs no atvērtā () sistēmas izsaukuma (tiek atgriezts kā vesels skaitlis). To sajaukšana var radīt problēmas.

Kopumā Linux sistēmas zvaniem ir mazāk bufera joslu nekā glibc funkcijām. Lai gan ir taisnība, ka sistēmas zvaniem ir zināma kļūdu apstrāde un ziņošana, no glibc funkcijas jūs iegūsit detalizētāku funkcionalitāti.

Un visbeidzot, vārds par drošību. Sistēmas zvani tieši saskarē ar kodolu. Linux kodolam patiešām ir plaša aizsardzība pret ļaunprātīgu izmantošanu no lietotāja zemes, taču pastāv neatklātas kļūdas. Neuzticieties, ka sistēmas zvans apstiprinās jūsu ievadīto informāciju vai izolēs jūs no drošības problēmām. Ir prātīgi nodrošināt, lai sistēmas izsaukumam nodotie dati tiktu dezinficēti. Protams, tas ir labs padoms jebkuram API zvanam, taču jūs nevarat būt uzmanīgs, strādājot ar kodolu.

Es ceru, ka jums patika šī dziļākā ienirt Linux sistēmas zvanu zemē. Pilnu Linux sistēmas zvanu sarakstu skatiet mūsu galvenajā sarakstā.