C Programovanie

Prečítajte si Syscall Linux

Prečítajte si Syscall Linux
Musíte si teda prečítať binárne údaje? Možno budete chcieť čítať z FIFO alebo zásuvky? Uvidíte, že môžete použiť štandardnú funkciu knižnice C, ale pritom nebudete mať prospech zo špeciálnych funkcií poskytovaných Linux Kernel a POSIX. Možno budete chcieť napríklad použiť časové limity na čítanie v určitom čase bez toho, aby ste sa uchýlili k volebnému prieskumu. Možno budete musieť niečo prečítať bez toho, aby vás zaujímalo, či ide o špeciálny priečinok alebo zásuvku alebo čokoľvek iné. Vašou jedinou úlohou je prečítať si nejaký binárny obsah a dostať ho do svojej aplikácie. Tam svieti čítaný syscall.

Prečítajte si normálny súbor so systémom Linux

Najlepším spôsobom, ako s touto funkciou začať, je prečítať normálny súbor. Toto je najjednoduchší spôsob použitia tohto telefonického hovoru, a to z nejakého dôvodu: nemá toľko obmedzení ako iné typy streamu alebo potrubia. Ak o tom premýšľate logicky, pri čítaní výstupu inej aplikácie musíte mať pripravený nejaký výstup pred jej načítaním, takže budete musieť počkať, kým táto aplikácia tento výstup napíše.

Po prvé, kľúčový rozdiel oproti štandardnej knižnici: Neexistuje vôbec žiadne ukladanie do vyrovnávacej pamäte. Zakaždým, keď zavoláte funkciu čítania, zavoláte jadro Linuxu, a preto to bude chvíľu trvať - je to takmer okamžité, ak zavoláte raz, ale môže vás spomaliť, ak zavoláte tisíckrát za sekundu. Porovnaním bude štandardná knižnica ukladať údaje za vás. Takže kedykoľvek zavoláte na čítanie, mali by ste prečítať viac ako pár bajtov, ale skôr veľká vyrovnávacia pamäť ako pár kilobajtov - okrem toho, ak to, čo potrebujete, je naozaj niekoľko bajtov, napríklad ak skontrolujete, či súbor existuje a či nie je prázdny.

To má však výhodu: zakaždým, keď zavoláte na čítanie, ste si istí, že dostanete aktualizované údaje, ak ktorákoľvek iná aplikácia momentálne upraví súbor. Toto je obzvlášť užitočné pre špeciálne súbory, ako napríklad v / proc alebo / sys.

Je čas ukázať vám skutočný príklad. Tento program C kontroluje, či je súbor PNG alebo nie. Za týmto účelom načíta súbor zadaný v ceste, ktorú zadáte v argumente príkazového riadku, a skontroluje, či prvých 8 bajtov zodpovedá hlavičke PNG.

Tu je kód:

#include
#include
#include
#include
#include
#include
#include
 
typedef enum
IS_PNG,
PRÍLIŠ KRÁTKY,
INVALID_HEADER
pngStatus_t;
 
unsigned int isSyscallSuccessful (const ssize_t readStatus)
návrat readStatus> = 0;
 

 
/ *
* checkPngHeader kontroluje, či pole pngFileHeader zodpovedá PNG
* hlavička súboru.
*
* Momentálne kontroluje iba prvých 8 bajtov poľa. Ak je pole menšie
* ako 8 bajtov, vráti sa TOO_SHORT.
*
* pngFileHeaderLength musí udržiavať dĺžku tye poľa. Akákoľvek neplatná hodnota
* môže viesť k nedefinovanému správaniu, napríklad k zlyhaniu aplikácie.
*
* Vráti IS_PNG, ak zodpovedá hlavičke súboru PNG. Ak je aspoň
* 8 bajtov v poli, ale nejde o hlavičku PNG, vráti sa INVALID_HEADER.
*
* /
pngStatus_t checkPngHeader (konštantný nepodpísaný znak * konšt pngFileHeader,
size_t pngFileHeaderLength) konštantný nepodpísaný znak očakávanýPngHeader [8] =
0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A;
int i = 0;
 
if (pngFileHeaderLength < sizeof(expectedPngHeader))
návrat TOO_SHORT;
 

 
pre (i = 0; i < sizeof(expectedPngHeader); i++)
if (pngFileHeader [i] != expectPngHeader [i])
vrátiť INVALID_HEADER;
 


 
/ * Ak to dosiahne tu, všetkých prvých 8 bajtov zodpovedá hlavičke PNG. * /
návrat IS_PNG;

 
int main (int argumentLength, char * argumentList [])
char * pngFileName = NULL;
unsigned char pngFileHeader [8] = 0;
 
ssize_t readStatus = 0;
/ * Linux používa na identifikáciu otvoreného súboru číslo. * /
int pngFile = 0;
pngStatus_t pngCheckResult;
 
if (argumentDĺžka != 2)
fputs ("Tento program musíte zavolať pomocou isPng váš názov.\ n ", stderr);
návrat EXIT_FAILURE;
 

 
pngFileName = argumentList [1];
pngFile = open (pngFileName, O_RDONLY);
 
if (pngFile == -1)
perror ("Otvorenie poskytnutého súboru zlyhalo");
návrat EXIT_FAILURE;
 

 
/ * Prečítajte si niekoľko bajtov a zistite, či je súbor PNG. * /
readStatus = read (pngFile, pngFileHeader, sizeof (pngFileHeader));
 
if (isSyscallSuccessful (readStatus))
/ * Skontrolujte, či je súbor PNG, pretože obsahuje údaje. * /
pngCheckResult = checkPngHeader (pngFileHeader, readStatus);
 
if (pngCheckResult == TOO_SHORT)
printf ("Súbor% s nie je súbor PNG: je príliš krátky.\ n ", názov súboru png);
 
else if (pngCheckResult == IS_PNG)
printf ("Súbor% s je súbor PNG!\ n ", názov súboru png);
 
else
printf ("Súbor% s nie je vo formáte PNG.\ n ", názov súboru png);
 

 
else
perror ("Čítanie súboru zlyhalo");
návrat EXIT_FAILURE;
 

 
/ * Zavrieť súbor ... * /
if (close (pngFile) == -1)
perror ("Zavretie poskytnutého súboru zlyhalo");
návrat EXIT_FAILURE;
 

 
pngFile = 0;
 
návrat EXIT_SUCCESS;
 

Vidíte, je to plnohodnotný, funkčný a zostaviteľný príklad. Neváhajte a zostavte si ho sami a otestujte, naozaj to funguje. Program by ste mali volať z terminálu, ako je tento:

./ isPng váš názov

Teraz sa zamerajme na samotné čítanie:

pngFile = open (pngFileName, O_RDONLY);
if (pngFile == -1)
perror ("Otvorenie poskytnutého súboru zlyhalo");
návrat EXIT_FAILURE;

/ * Prečítajte si niekoľko bajtov a zistite, či je súbor PNG. * /
readStatus = read (pngFile, pngFileHeader, sizeof (pngFileHeader));

Čítaný podpis je nasledovný (extrahovaný z manuálových stránok systému Linux):

ssize_t read (int fd, void * buf, size_t count);

Po prvé, argument fd predstavuje deskriptor súboru. Tento pojem som trochu vysvetlil vo svojom článku venovanom vidličke.  Deskriptor súboru je int predstavujúci otvorený súbor, soket, potrubie, FIFO, zariadenie, no je to veľa vecí, kde sa dajú údaje čítať alebo zapisovať, zvyčajne prúdovým spôsobom. O tom sa podrobnejšie zmienim v budúcom článku.

otvorená funkcia je jedným zo spôsobov, ako povedať Linuxu: Chcem robiť veci so súborom na tejto ceste, nájdite ho tam, kde je, a poskytnite mi k nemu prístup. Vráti vám tento int nazývaný deskriptor súborov a teraz, ak chcete s týmto súborom niečo urobiť, použite toto číslo. Po dokončení práce so súborom nezabudnite zavolať na číslo close, ako v príklade.

Na prečítanie musíte uviesť toto špeciálne číslo. Potom je tu argument buf. Mali by ste tu poskytnúť ukazovateľ na pole, do ktorého bude čítanie ukladať vaše údaje. Na záver sa počíta, koľko bajtov bude čítať najviac.

Návratová hodnota je typu ssize_t. Divný typ, však?? Znamená to „signed size_t“, v podstate je to dlhý int. Vráti počet bajtov, ktoré úspešne prečítal, alebo -1, ak je problém. Presnú príčinu problému nájdete v globálnej premennej errno vytvorenej systémom Linux, definovanej v . Ak však chcete vytlačiť chybové hlásenie, použitie perror je lepšie, pretože sa vo vašom mene tlačí chybne.

V bežných súboroch - a iba v tomto prípade - čítanie sa vráti menej ako počet, iba ak ste sa dostali na koniec súboru. Poskytujete bufetové pole musieť byť dostatočne veľké na to, aby sa zmestili aspoň do počtu bajtov, alebo môže dôjsť k zlyhaniu vášho programu alebo vytvoreniu bezpečnostnej chyby.

Čítanie teraz nie je užitočné iba pre bežné súbory a ak chcete cítiť jeho superveľmoci - Áno, viem, že to nie je v žiadnom komikse Marvelu, ale má skutočnú moc - budete ho chcieť použiť s inými prúdmi, ako sú napríklad rúry alebo zásuvky. Poďme sa na to pozrieť:

Špeciálne súbory pre Linux a čítanie systémových volaní

Skutočnosť, že čítanie funguje, pracuje s rôznymi súbormi, ako sú rúry, zásuvky, FIFO alebo špeciálne zariadenia, ako je disk alebo sériový port, vďaka čomu je skutočne výkonnejšia. Pomocou niektorých úprav môžete urobiť skutočne zaujímavé veci. Po prvé to znamená, že môžete doslova písať funkcie pracujúce na súbore a používať ich skôr s rúrok. Je zaujímavé prenášať údaje bez toho, aby ste niekedy narazili na disk, čím sa zabezpečí najlepší výkon.

Toto však tiež spúšťa špeciálne pravidlá. Zoberme si príklad čítania riadku z terminálu v porovnaní s normálnym súborom. Keď zavoláte čítanie v normálnom súbore, na získanie požadovaného množstva údajov stačí v systéme Linux iba niekoľko milisekúnd.

Pokiaľ však ide o terminál, je to iný príbeh: povedzme, že si vyžiadate používateľské meno. Používateľ zadáva do terminálu svoje používateľské meno a stlačte kláves Enter. Teraz sa budete riadiť mojou radou vyššie a zavoláte read s veľkým bufferom ako napríklad 256 bajtov.

Ak by čítanie fungovalo ako pri súboroch, čakalo by sa na zadanie 256 znakov pred návratom! Váš používateľ by čakal večne a potom vašu aplikáciu smutne zabil. Určite to nie je to, čo chcete, a mali by ste veľký problém.

Dobre, mohli ste čítať jeden bajt naraz, ale toto riešenie je strašne neefektívne, ako som vám povedal vyššie. Musí to fungovať lepšie.

Vývojári systému Linux si však mysleli, že ak sa chcete vyhnúť týmto problémom, prečítajte si niečo iné:

  • Keď čítate normálne súbory, pokúsi sa čo najviac prečítať počet bajtov a v prípade potreby aktívne získa bajty z disku.
  • Pre všetky ostatné typy súborov sa vráti tak skoro ako sú k dispozícii nejaké údaje a najviac počet bytov:
    1. Pre terminály to je všeobecne keď používateľ stlačí kláves Enter.
    2. V prípade zásuviek TCP je to hneď, ako počítač niečo prijme, nezáleží na tom, koľko bajtov dostane.
    3. Pre FIFO alebo rúry je to všeobecne rovnaké množstvo ako to, čo napísala iná aplikácia, ale linuxové jadro môže dodávať menej naraz, ak je to pohodlnejšie.

Môžete teda bezpečne volať pomocou vyrovnávacej pamäte 2 KiB bez toho, aby ste zostali navždy uzamknutí. Upozorňujeme, že môže dôjsť aj k prerušeniu, ak aplikácia dostane signál. Pretože čítanie zo všetkých týchto zdrojov môže trvať niekoľko sekúnd alebo dokonca hodín - až sa koniec koncov druhá strana rozhodne písať - prerušenie signálmi umožňuje prestať zostať blokovaný príliš dlho.

Má to však aj nevýhodu: ak chcete s týmito špeciálnymi súbormi presne prečítať 2 KiB, budete musieť skontrolovať návratovú hodnotu čítania a volať prečítať viackrát. čítanie zriedka vyplní celý váš buffer. Ak vaša aplikácia používa signály, musíte tiež pomocou errno skontrolovať, či sa čítanie nepodarilo s hodnotou -1, pretože ju prerušil signál.

Ukážem vám, ako môže byť zaujímavé použiť túto špeciálnu vlastnosť read:

#define _POSIX_C_SOURCE 1 / * sigaction nie je bez tohto #define k dispozícii. * /
#include
#include
#include
#include
#include
#include
/ *
* isSignal hovorí, či bol načítaný systémový hovor prerušený signálom.
*
* Vráti hodnotu TRUE, ak bol načítaný systémový hovor prerušený signálom.
*
* Globálne premenné: číta errno definované v errno.h
* /
unsigned int isSignal (const ssize_t readStatus)
návrat (readStatus == -1 && errno == EINTR);

unsigned int isSyscallSuccessful (const ssize_t readStatus)
návrat readStatus> = 0;

/ *
* shouldRestartRead povie, kedy bolo prerušené čítanie systému Syscall znakom
* signalizovať udalosť alebo nie a vzhľadom na túto „chybu“ je dôvod prechodný, môžeme
* bezpečne reštartujte čítaný hovor.
*
* Momentálne kontroluje iba to, či bolo čítanie prerušené signálom, ale je to tak
* je možné vylepšiť, aby sa zistilo, či bol načítaný cieľový počet bajtov a či je načítaný
* nie je to tak, vráťte sa na hodnotu TRUE a prečítajte si to znova.
*
* /
unsigned int shouldRestartRead (const ssize_t readStatus)
return isSignal (readStatus);

/ *
* Potrebujeme prázdny obslužný program, pretože načítaný systémový hovor bude prerušený, iba ak
* je spracovaný signál.
* /
void emptyHandler (int ignored)
návrat;

int main ()
/ * Je v sekundách. * /
const int alarmInterval = 5;
const struct sigaction emptySigaction = prázdnyHandler;
char lineBuf [256] = 0;
ssize_t readStatus = 0;
unsigned int waitTime = 0;
/ * Nemeňte sigaction okrem prípadov, keď presne viete, čo robíte. * /
sigaction (SIGALRM, & emptySigaction, NULL);
alarm (alarmInterval);
fputs ("Váš text: \ n", stderr);
robiť
/ * Nezabudnite na '\ 0' * /
readStatus = read (STDIN_FILENO, lineBuf, sizeof (lineBuf) - 1);
if (isSignal (readStatus))
waitTime + = alarmInterval;
alarm (alarmInterval);
fprintf (stderr, "% u sekúnd nečinnosti ... \ n", waitTime);

while (shouldRestartRead (readStatus));
if (isSyscallSuccessful (readStatus))
/ * Ukončite reťazec, aby ste sa vyhli chybe pri jeho poskytovaní fprintf. * /
lineBuf [readStatus] = '\ 0';
fprintf (stderr, "Zadali ste% lu znakov. Tu je váš reťazec: \ n% s \ n ", strlen (lineBuf),
lineBuf);
else
perror ("Čítanie zo štandardnej verzie zlyhalo");
návrat EXIT_FAILURE;

návrat EXIT_SUCCESS;

Opäť sa jedná o úplnú aplikáciu C, ktorú môžete skompilovať a skutočne spustiť.

Robí toto: číta riadok zo štandardného vstupu. Každých 5 sekúnd však vytlačí riadok, ktorý informuje používateľa, že zatiaľ nebol zadaný žiadny vstup.

Príklad, ak počkám 23 sekúnd pred napísaním „Penguin“:

$ alarm_read
Tvoj text:
5 sekúnd nečinnosti…
10 sekúnd nečinnosti…
15 sekúnd nečinnosti…
20 sekúnd nečinnosti…
Tučniak
Zadali ste 8 znakov. Tu je váš reťazec:
Tučniak

To je neuveriteľne užitočné. Môže sa použiť na častú aktualizáciu používateľského rozhrania na vytlačenie priebehu čítania alebo spracovania vašej aplikácie, ktorú práve robíte. Môže sa tiež použiť ako mechanizmus časového limitu. Tiež by vás mohol prerušiť akýkoľvek iný signál, ktorý by mohol byť užitočný pre vašu aplikáciu. To v každom prípade znamená, že vaša aplikácia teraz môže reagovať, namiesto toho, aby zostala uviaznutá navždy.

Výhody teda prevažujú nad vyššie popísanou nevýhodou. Ak vás zaujíma, či by ste mali podporovať špeciálne súbory v aplikácii, ktorá normálne pracuje s normálnymi súbormi - a tak volanie čítať v slučke - Povedal by som, že až na to, že ak sa ponáhľate, moja osobná skúsenosť často dokázala, že nahradenie súboru fajkou alebo FIFO môže pri malom úsilí doslova urobiť aplikáciu oveľa užitočnejšou. Na internete sú dokonca vopred pripravené funkcie C, ktoré túto slučku implementujú za vás: hovorí sa im funkcie čítania.

Záver

Ako vidíte, fread a read môžu vyzerať podobne, nie sú to tak. A len s niekoľkými zmenami v tom, ako čítanie funguje pre vývojárov jazyka C, je čítanie oveľa zaujímavejšie pre navrhovanie nových riešení problémov, s ktorými sa stretnete počas vývoja aplikácie.

Nabudúce vám poviem, ako funguje písanie syscall, pretože čítanie je v pohode, ale schopnosť oboje je oveľa lepšia. Medzitým experimentujte s čítaním, spoznajte to a želám vám šťastný nový rok!

Kurzor pri písaní v systéme Windows 10 skáče alebo sa pohybuje náhodne
Ak zistíte, že kurzor myši skáče alebo sa pohybuje sám, automaticky a náhodne pri písaní na notebooku alebo počítači so systémom Windows, niektoré z t...
Ako zmeniť smer posúvania myši a touchpadu v systéme Windows 10
Myš a TouchpadNielenže uľahčujú výpočty, ale sú aj efektívnejšie a menej časovo náročné. Nemôžeme si predstaviť život bez týchto zariadení, ale je pra...
Ako zmeniť veľkosť, farbu a schému ukazovateľa myši a kurzora v systéme Windows 10
Ukazovateľ myši a kurzor vo Windows 10 sú veľmi dôležité aspekty operačného systému. Dá sa to povedať aj pre iné operačné systémy, takže v skutočnosti...